@checkstack/backend 0.8.2 → 0.9.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 (36) hide show
  1. package/CHANGELOG.md +203 -0
  2. package/drizzle/0001_slim_mordo.sql +34 -0
  3. package/drizzle/meta/0001_snapshot.json +444 -0
  4. package/drizzle/meta/_journal.json +7 -0
  5. package/package.json +12 -7
  6. package/src/index.ts +276 -17
  7. package/src/plugin-deregistration.test.ts +137 -0
  8. package/src/plugin-manager/api-router.ts +35 -11
  9. package/src/plugin-manager/plugin-loader.ts +73 -0
  10. package/src/plugin-manager.ts +295 -105
  11. package/src/schema.ts +79 -1
  12. package/src/services/compatibility-checker.test.ts +146 -0
  13. package/src/services/compatibility-checker.ts +137 -0
  14. package/src/services/dev-auth.test.ts +87 -0
  15. package/src/services/dev-auth.ts +56 -0
  16. package/src/services/plugin-artifact-store.ts +131 -0
  17. package/src/services/plugin-bundle-resolver.ts +76 -0
  18. package/src/services/plugin-event-recorder.ts +87 -0
  19. package/src/services/plugin-installers/catalog-installer.ts +33 -0
  20. package/src/services/plugin-installers/github-installer.ts +207 -0
  21. package/src/services/plugin-installers/install-from-tarball.ts +69 -0
  22. package/src/services/plugin-installers/installer-registry.ts +51 -0
  23. package/src/services/plugin-installers/npm-installer.ts +156 -0
  24. package/src/services/plugin-installers/plugin-install-error.ts +37 -0
  25. package/src/services/plugin-installers/tarball-installer.ts +80 -0
  26. package/src/services/plugin-installers/tarball-utils.test.ts +200 -0
  27. package/src/services/plugin-installers/tarball-utils.ts +172 -0
  28. package/src/services/plugin-manager-orchestrator.ts +522 -0
  29. package/src/services/plugin-manager-router.ts +219 -0
  30. package/src/utils/plugin-discovery.test.ts +6 -0
  31. package/src/utils/plugin-discovery.ts +6 -1
  32. package/tsconfig.json +3 -0
  33. package/src/plugin-lifecycle.test.ts +0 -276
  34. package/src/plugin-manager/plugin-admin-router.ts +0 -89
  35. package/src/services/plugin-installer.test.ts +0 -90
  36. package/src/services/plugin-installer.ts +0 -70
@@ -1,90 +0,0 @@
1
- import { describe, it, expect, mock, beforeEach } from "bun:test";
2
-
3
- // 1. Mock child_process and fs BEFORE importing the target module
4
- const mockExec = mock((_cmd: string, cb: any) => {
5
- cb(null, { stdout: "mocked" }, { stderr: "" });
6
- });
7
-
8
- const mockExistsSync = mock(() => true);
9
- const mockMkdirSync = mock();
10
- const mockReadFileSync = mock(() => JSON.stringify({ name: "mock-plugin" }));
11
-
12
- mock.module("node:util", () => ({
13
- promisify: (fn: any) => {
14
- return async (...args: any[]) => {
15
- // Return a promise that resolves with what our mock would return
16
- // We can just call mockExec and return its "result"
17
- return new Promise((resolve) => {
18
- fn(...args, (err: any, stdout: any, stderr: any) =>
19
- resolve({ stdout, stderr })
20
- );
21
- });
22
- };
23
- },
24
- }));
25
-
26
- mock.module("node:child_process", () => ({
27
- exec: mockExec,
28
- }));
29
-
30
- mock.module("node:fs", () => {
31
- const exports = {
32
- existsSync: mockExistsSync,
33
- mkdirSync: mockMkdirSync,
34
- readFileSync: mockReadFileSync,
35
- };
36
- return {
37
- ...exports,
38
- default: exports,
39
- };
40
- });
41
-
42
- // 2. Now import the module under test
43
- import { PluginLocalInstaller } from "./plugin-installer";
44
- import fs from "node:fs";
45
- import path from "node:path";
46
-
47
- describe("PluginLocalInstaller", () => {
48
- const runtimeDir = "/tmp/runtime_plugins";
49
- let installer: PluginLocalInstaller;
50
- let customExec: any;
51
-
52
- beforeEach(() => {
53
- customExec = mock(() => Promise.resolve({ stdout: "mocked", stderr: "" }));
54
- installer = new PluginLocalInstaller(runtimeDir, customExec);
55
- mockExistsSync.mockClear();
56
- mockExistsSync.mockReturnValue(true);
57
- });
58
-
59
- it("should install a package using bun", async () => {
60
- const result = await installer.install("my-plugin");
61
-
62
- expect(customExec).toHaveBeenCalled();
63
- const command = customExec.mock.calls[0][0];
64
- expect(command).toContain("bun install my-plugin");
65
- expect(command).toContain(`--cwd ${path.resolve(runtimeDir)}`);
66
- expect(command).toContain("--no-save");
67
-
68
- expect(result.name).toBe("mock-plugin");
69
- });
70
-
71
- it("should handle scoped packages correctly", async () => {
72
- const result = await installer.install("@scope/plugin");
73
-
74
- expect(customExec).toHaveBeenCalled();
75
- const command = customExec.mock.calls[0][0];
76
- expect(command).toContain("bun install @scope/plugin");
77
-
78
- expect(result.name).toBe("mock-plugin");
79
- });
80
-
81
- it("should throw error if package.json is missing after install", async () => {
82
- // The constructor was already called in beforeEach.
83
- // The next call to existsSync will be inside the install method for the pkgJsonPath.
84
- mockExistsSync.mockReturnValueOnce(false);
85
-
86
- await expect(installer.install("failing-plugin")).rejects.toThrow(
87
- "not found"
88
- );
89
- });
90
- });
@@ -1,70 +0,0 @@
1
- import { PluginInstaller } from "@checkstack/backend-api";
2
- import { exec } from "node:child_process";
3
- import { promisify } from "node:util";
4
- import path from "node:path";
5
- import fs from "node:fs";
6
-
7
- export class PluginLocalInstaller implements PluginInstaller {
8
- private runtimeDir: string;
9
- private execAsync: (
10
- command: string
11
- ) => Promise<{ stdout: string; stderr: string }>;
12
-
13
- constructor(
14
- runtimeDir: string,
15
- customExec?: (
16
- command: string
17
- ) => Promise<{ stdout: string; stderr: string }>
18
- ) {
19
- this.runtimeDir = path.resolve(runtimeDir);
20
- this.execAsync = customExec || promisify(exec);
21
- if (!fs.existsSync(this.runtimeDir)) {
22
- fs.mkdirSync(this.runtimeDir, { recursive: true });
23
- }
24
- }
25
-
26
- async install(packageName: string): Promise<{ name: string; path: string }> {
27
- try {
28
- console.log(
29
- `🔌 Installing plugin: ${packageName} into ${this.runtimeDir}`
30
- );
31
-
32
- // Use bun install with --no-save to avoid creating/modifying lockfiles
33
- // in the runtime directory. This keeps plugins isolated from the main app.
34
- await this.execAsync(
35
- `bun install ${packageName} --cwd ${this.runtimeDir} --no-save`
36
- );
37
-
38
- // Extract the actual package name (packageName could be a URL or @org/name@version)
39
- // For now, we assume it's a simple package name for the path lookup,
40
- // or we can parse it better.
41
- // A safer way is to check the recently changed folders in node_modules?
42
- // Or just assume the input packageName (stripped of @version) matches the folder.
43
- let folderName = packageName;
44
- if (packageName.includes("@") && !packageName.startsWith("@")) {
45
- folderName = packageName.split("@")[0];
46
- } else if (packageName.startsWith("@") && packageName.includes("@", 1)) {
47
- folderName = "@" + packageName.split("@")[1];
48
- }
49
-
50
- const pkgPath = path.join(this.runtimeDir, "node_modules", folderName);
51
- const pkgJsonPath = path.join(pkgPath, "package.json");
52
-
53
- if (!fs.existsSync(pkgJsonPath)) {
54
- throw new Error(
55
- `Package folder ${folderName} not found at ${pkgPath} after installation`
56
- );
57
- }
58
-
59
- const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
60
-
61
- return {
62
- name: pkgJson.name,
63
- path: pkgPath,
64
- };
65
- } catch (error) {
66
- console.error(`❌ Failed to install plugin ${packageName}:`, error);
67
- throw error;
68
- }
69
- }
70
- }