@codaijs/keel 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.
Files changed (116) hide show
  1. package/dist/__tests__/cli.test.d.ts +2 -0
  2. package/dist/__tests__/cli.test.d.ts.map +1 -0
  3. package/dist/__tests__/cli.test.js +173 -0
  4. package/dist/__tests__/cli.test.js.map +1 -0
  5. package/dist/__tests__/registry.test.d.ts +2 -0
  6. package/dist/__tests__/registry.test.d.ts.map +1 -0
  7. package/dist/__tests__/registry.test.js +86 -0
  8. package/dist/__tests__/registry.test.js.map +1 -0
  9. package/dist/__tests__/sail-installer.test.d.ts +2 -0
  10. package/dist/__tests__/sail-installer.test.d.ts.map +1 -0
  11. package/dist/__tests__/sail-installer.test.js +158 -0
  12. package/dist/__tests__/sail-installer.test.js.map +1 -0
  13. package/dist/create-runner.d.ts +11 -0
  14. package/dist/create-runner.d.ts.map +1 -0
  15. package/dist/create-runner.js +63 -0
  16. package/dist/create-runner.js.map +1 -0
  17. package/dist/create.d.ts +10 -0
  18. package/dist/create.d.ts.map +1 -0
  19. package/dist/create.js +15 -0
  20. package/dist/create.js.map +1 -0
  21. package/dist/manage.d.ts +24 -0
  22. package/dist/manage.d.ts.map +1 -0
  23. package/dist/manage.js +1461 -0
  24. package/dist/manage.js.map +1 -0
  25. package/dist/prompts.d.ts +36 -0
  26. package/dist/prompts.d.ts.map +1 -0
  27. package/dist/prompts.js +208 -0
  28. package/dist/prompts.js.map +1 -0
  29. package/dist/sail-installer.d.ts +37 -0
  30. package/dist/sail-installer.d.ts.map +1 -0
  31. package/dist/sail-installer.js +935 -0
  32. package/dist/sail-installer.js.map +1 -0
  33. package/dist/scaffold.d.ts +10 -0
  34. package/dist/scaffold.d.ts.map +1 -0
  35. package/dist/scaffold.js +297 -0
  36. package/dist/scaffold.js.map +1 -0
  37. package/package.json +57 -0
  38. package/sails/_template/addon.json +20 -0
  39. package/sails/_template/install.ts +402 -0
  40. package/sails/admin-dashboard/README.md +117 -0
  41. package/sails/admin-dashboard/addon.json +28 -0
  42. package/sails/admin-dashboard/files/backend/middleware/admin.ts +34 -0
  43. package/sails/admin-dashboard/files/backend/routes/admin.ts +243 -0
  44. package/sails/admin-dashboard/files/frontend/components/admin/StatsCard.tsx +40 -0
  45. package/sails/admin-dashboard/files/frontend/components/admin/UsersTable.tsx +240 -0
  46. package/sails/admin-dashboard/files/frontend/hooks/useAdmin.ts +149 -0
  47. package/sails/admin-dashboard/files/frontend/pages/admin/Dashboard.tsx +173 -0
  48. package/sails/admin-dashboard/files/frontend/pages/admin/UserDetail.tsx +203 -0
  49. package/sails/admin-dashboard/install.ts +305 -0
  50. package/sails/analytics/README.md +178 -0
  51. package/sails/analytics/addon.json +27 -0
  52. package/sails/analytics/files/frontend/components/AnalyticsProvider.tsx +58 -0
  53. package/sails/analytics/files/frontend/hooks/useAnalytics.ts +64 -0
  54. package/sails/analytics/files/frontend/lib/analytics.ts +103 -0
  55. package/sails/analytics/install.ts +297 -0
  56. package/sails/file-uploads/README.md +191 -0
  57. package/sails/file-uploads/addon.json +30 -0
  58. package/sails/file-uploads/files/backend/routes/files.ts +198 -0
  59. package/sails/file-uploads/files/backend/schema/files.ts +36 -0
  60. package/sails/file-uploads/files/backend/services/file-storage.ts +128 -0
  61. package/sails/file-uploads/files/frontend/components/FileList.tsx +248 -0
  62. package/sails/file-uploads/files/frontend/components/FileUploadButton.tsx +147 -0
  63. package/sails/file-uploads/files/frontend/hooks/useFileUpload.ts +106 -0
  64. package/sails/file-uploads/files/frontend/hooks/useFiles.ts +118 -0
  65. package/sails/file-uploads/files/frontend/pages/Files.tsx +37 -0
  66. package/sails/file-uploads/install.ts +466 -0
  67. package/sails/gdpr/README.md +174 -0
  68. package/sails/gdpr/addon.json +27 -0
  69. package/sails/gdpr/files/backend/routes/gdpr.ts +140 -0
  70. package/sails/gdpr/files/backend/services/gdpr.ts +293 -0
  71. package/sails/gdpr/files/frontend/components/auth/ConsentCheckboxes.tsx +97 -0
  72. package/sails/gdpr/files/frontend/components/gdpr/AccountDeletionRequest.tsx +192 -0
  73. package/sails/gdpr/files/frontend/components/gdpr/DataExportButton.tsx +75 -0
  74. package/sails/gdpr/files/frontend/pages/PrivacyPolicy.tsx +186 -0
  75. package/sails/gdpr/install.ts +756 -0
  76. package/sails/google-oauth/README.md +121 -0
  77. package/sails/google-oauth/addon.json +22 -0
  78. package/sails/google-oauth/files/GoogleButton.tsx +50 -0
  79. package/sails/google-oauth/install.ts +252 -0
  80. package/sails/i18n/README.md +193 -0
  81. package/sails/i18n/addon.json +30 -0
  82. package/sails/i18n/files/frontend/components/LanguageSwitcher.tsx +108 -0
  83. package/sails/i18n/files/frontend/hooks/useLanguage.ts +31 -0
  84. package/sails/i18n/files/frontend/lib/i18n.ts +32 -0
  85. package/sails/i18n/files/frontend/locales/de/common.json +44 -0
  86. package/sails/i18n/files/frontend/locales/en/common.json +44 -0
  87. package/sails/i18n/install.ts +407 -0
  88. package/sails/push-notifications/README.md +163 -0
  89. package/sails/push-notifications/addon.json +31 -0
  90. package/sails/push-notifications/files/backend/routes/notifications.ts +153 -0
  91. package/sails/push-notifications/files/backend/schema/notifications.ts +31 -0
  92. package/sails/push-notifications/files/backend/services/notifications.ts +117 -0
  93. package/sails/push-notifications/files/frontend/components/PushNotificationInit.tsx +12 -0
  94. package/sails/push-notifications/files/frontend/hooks/usePushNotifications.ts +154 -0
  95. package/sails/push-notifications/install.ts +384 -0
  96. package/sails/r2-storage/README.md +101 -0
  97. package/sails/r2-storage/addon.json +29 -0
  98. package/sails/r2-storage/files/backend/services/storage.ts +71 -0
  99. package/sails/r2-storage/files/frontend/components/ProfilePictureUpload.tsx +167 -0
  100. package/sails/r2-storage/install.ts +412 -0
  101. package/sails/rate-limiting/README.md +145 -0
  102. package/sails/rate-limiting/addon.json +20 -0
  103. package/sails/rate-limiting/files/backend/middleware/rate-limit-store.ts +104 -0
  104. package/sails/rate-limiting/files/backend/middleware/rate-limit.ts +137 -0
  105. package/sails/rate-limiting/install.ts +300 -0
  106. package/sails/registry.json +107 -0
  107. package/sails/stripe/README.md +214 -0
  108. package/sails/stripe/addon.json +24 -0
  109. package/sails/stripe/files/backend/routes/stripe.ts +154 -0
  110. package/sails/stripe/files/backend/schema/stripe.ts +74 -0
  111. package/sails/stripe/files/backend/services/stripe.ts +224 -0
  112. package/sails/stripe/files/frontend/components/SubscriptionStatus.tsx +135 -0
  113. package/sails/stripe/files/frontend/hooks/useSubscription.ts +86 -0
  114. package/sails/stripe/files/frontend/pages/Checkout.tsx +116 -0
  115. package/sails/stripe/files/frontend/pages/Pricing.tsx +226 -0
  116. package/sails/stripe/install.ts +378 -0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cli.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,173 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ // Mock the interactive prompts since we're testing flag parsing
3
+ vi.mock("@inquirer/prompts", () => ({
4
+ input: vi.fn(),
5
+ select: vi.fn(),
6
+ checkbox: vi.fn(),
7
+ confirm: vi.fn(),
8
+ }));
9
+ const { parseFlags, runPrompts } = await import("../prompts.js");
10
+ // ---------------------------------------------------------------------------
11
+ // parseFlags
12
+ // ---------------------------------------------------------------------------
13
+ describe("parseFlags", () => {
14
+ it("parses --yes flag", () => {
15
+ const { flags } = parseFlags(["--yes"]);
16
+ expect(flags.yes).toBe(true);
17
+ });
18
+ it("parses -y shorthand", () => {
19
+ const { flags } = parseFlags(["-y"]);
20
+ expect(flags.yes).toBe(true);
21
+ });
22
+ it("extracts project name from positional arg", () => {
23
+ const { projectName, flags } = parseFlags(["my-app"]);
24
+ expect(projectName).toBe("my-app");
25
+ expect(flags.yes).toBe(false);
26
+ });
27
+ it("parses --db=docker", () => {
28
+ const { flags } = parseFlags(["--db=docker"]);
29
+ expect(flags.db).toBe("docker");
30
+ });
31
+ it("parses --db=url", () => {
32
+ const { flags } = parseFlags(["--db=url"]);
33
+ expect(flags.db).toBe("url");
34
+ });
35
+ it("parses --db=skip", () => {
36
+ const { flags } = parseFlags(["--db=skip"]);
37
+ expect(flags.db).toBe("skip");
38
+ });
39
+ it("parses --db-url and sets db=url", () => {
40
+ const { flags } = parseFlags(["--db-url=postgresql://localhost/mydb"]);
41
+ expect(flags.db).toBe("url");
42
+ expect(flags.dbUrl).toBe("postgresql://localhost/mydb");
43
+ });
44
+ it("parses --resend-key", () => {
45
+ const { flags } = parseFlags(["--resend-key=re_test_abc"]);
46
+ expect(flags.resendKey).toBe("re_test_abc");
47
+ });
48
+ it("parses --email-from", () => {
49
+ const { flags } = parseFlags(["--email-from=noreply@example.com"]);
50
+ expect(flags.emailFrom).toBe("noreply@example.com");
51
+ });
52
+ it("parses --sails with comma-separated values", () => {
53
+ const { flags } = parseFlags(["--sails=stripe,google-oauth"]);
54
+ expect(flags.sails).toEqual(["stripe", "google-oauth"]);
55
+ });
56
+ it("parses --sails with single value", () => {
57
+ const { flags } = parseFlags(["--sails=stripe"]);
58
+ expect(flags.sails).toEqual(["stripe"]);
59
+ });
60
+ it("parses combined flags", () => {
61
+ const { projectName, flags } = parseFlags([
62
+ "my-app",
63
+ "--yes",
64
+ "--db=docker",
65
+ "--sails=stripe,google-oauth",
66
+ "--resend-key=re_test",
67
+ "--email-from=noreply@x.com",
68
+ ]);
69
+ expect(projectName).toBe("my-app");
70
+ expect(flags.yes).toBe(true);
71
+ expect(flags.db).toBe("docker");
72
+ expect(flags.sails).toEqual(["stripe", "google-oauth"]);
73
+ expect(flags.resendKey).toBe("re_test");
74
+ expect(flags.emailFrom).toBe("noreply@x.com");
75
+ });
76
+ it("ignores unknown flags gracefully", () => {
77
+ const { flags } = parseFlags(["--unknown-flag=value", "--yes"]);
78
+ expect(flags.yes).toBe(true);
79
+ });
80
+ it("returns empty flags for empty args", () => {
81
+ const { projectName, flags } = parseFlags([]);
82
+ expect(projectName).toBeUndefined();
83
+ expect(flags.yes).toBe(false);
84
+ expect(flags.db).toBeUndefined();
85
+ expect(flags.sails).toBeUndefined();
86
+ });
87
+ it("filters empty strings from sails", () => {
88
+ const { flags } = parseFlags(["--sails=,"]);
89
+ expect(flags.sails).toEqual([]);
90
+ });
91
+ });
92
+ // ---------------------------------------------------------------------------
93
+ // runPrompts (non-interactive / --yes mode)
94
+ // ---------------------------------------------------------------------------
95
+ describe("runPrompts with --yes flag", () => {
96
+ it("returns default config with --yes and no project name", async () => {
97
+ const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
98
+ const config = await runPrompts(undefined, { yes: true });
99
+ expect(config.projectName).toBe("my-app");
100
+ expect(config.displayName).toBe("My App");
101
+ expect(config.description).toBe("My App — built with keel");
102
+ expect(config.databaseSetup).toBe("docker");
103
+ expect(config.databaseUrl).toContain("postgresql://");
104
+ expect(config.databaseUrl).toContain("my_app");
105
+ expect(config.resendApiKey).toBe("");
106
+ expect(config.emailFrom).toBe("");
107
+ expect(config.betterAuthSecret).toBeTruthy();
108
+ expect(config.betterAuthSecret.length).toBeGreaterThan(0);
109
+ expect(config.sails).toEqual([]);
110
+ consoleSpy.mockRestore();
111
+ });
112
+ it("uses provided project name with --yes", async () => {
113
+ const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
114
+ const config = await runPrompts("cool-project", { yes: true });
115
+ expect(config.projectName).toBe("cool-project");
116
+ expect(config.displayName).toBe("Cool Project");
117
+ expect(config.databaseUrl).toContain("cool_project");
118
+ consoleSpy.mockRestore();
119
+ });
120
+ it("sanitizes project name", async () => {
121
+ const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
122
+ const config = await runPrompts("My Cool App!", { yes: true });
123
+ // sanitize: lowercase -> replace non-alnum with - -> strip leading/trailing -
124
+ expect(config.projectName).toBe("my-cool-app");
125
+ consoleSpy.mockRestore();
126
+ });
127
+ it("uses provided flags", async () => {
128
+ const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
129
+ const config = await runPrompts("test-proj", {
130
+ yes: true,
131
+ db: "url",
132
+ dbUrl: "postgresql://custom:pass@host/db",
133
+ resendKey: "re_test",
134
+ emailFrom: "hello@example.com",
135
+ sails: ["stripe", "google-oauth"],
136
+ });
137
+ expect(config.databaseSetup).toBe("url");
138
+ expect(config.databaseUrl).toBe("postgresql://custom:pass@host/db");
139
+ expect(config.resendApiKey).toBe("re_test");
140
+ expect(config.emailFrom).toBe("hello@example.com");
141
+ expect(config.sails).toEqual(["stripe", "google-oauth"]);
142
+ consoleSpy.mockRestore();
143
+ });
144
+ it("generates a unique betterAuthSecret each time", async () => {
145
+ const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
146
+ const config1 = await runPrompts("app1", { yes: true });
147
+ const config2 = await runPrompts("app2", { yes: true });
148
+ expect(config1.betterAuthSecret).not.toBe(config2.betterAuthSecret);
149
+ // Should be a hex string (64 chars for 32 bytes)
150
+ expect(config1.betterAuthSecret).toMatch(/^[a-f0-9]{64}$/);
151
+ consoleSpy.mockRestore();
152
+ });
153
+ it("constructs docker database URL from project name", async () => {
154
+ const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
155
+ const config = await runPrompts("my-cool-app", {
156
+ yes: true,
157
+ db: "docker",
158
+ });
159
+ expect(config.databaseUrl).toBe("postgresql://postgres:postgres@localhost:5432/my_cool_app");
160
+ consoleSpy.mockRestore();
161
+ });
162
+ it("handles skip database setup", async () => {
163
+ const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
164
+ const config = await runPrompts("test", {
165
+ yes: true,
166
+ db: "skip",
167
+ });
168
+ expect(config.databaseSetup).toBe("skip");
169
+ expect(config.databaseUrl).toBe("");
170
+ consoleSpy.mockRestore();
171
+ });
172
+ });
173
+ //# sourceMappingURL=cli.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.test.js","sourceRoot":"","sources":["../../src/__tests__/cli.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=registry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/registry.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,86 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { readFileSync, existsSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const SAILS_DIR = join(__dirname, "..", "..", "sails");
7
+ const REGISTRY_PATH = join(SAILS_DIR, "registry.json");
8
+ describe("sail registry", () => {
9
+ it("registry.json exists and is valid JSON", () => {
10
+ expect(existsSync(REGISTRY_PATH)).toBe(true);
11
+ const content = readFileSync(REGISTRY_PATH, "utf-8");
12
+ const registry = JSON.parse(content);
13
+ expect(registry).toBeDefined();
14
+ expect(registry.version).toBeDefined();
15
+ expect(Array.isArray(registry.sails)).toBe(true);
16
+ });
17
+ it("each sail has required fields", () => {
18
+ const registry = JSON.parse(readFileSync(REGISTRY_PATH, "utf-8"));
19
+ for (const sail of registry.sails) {
20
+ expect(sail.name).toBeTruthy();
21
+ expect(sail.displayName).toBeTruthy();
22
+ expect(sail.description).toBeTruthy();
23
+ expect(sail.category).toBeTruthy();
24
+ expect(sail.version).toBeTruthy();
25
+ }
26
+ });
27
+ it("sail names are unique", () => {
28
+ const registry = JSON.parse(readFileSync(REGISTRY_PATH, "utf-8"));
29
+ const names = registry.sails.map((s) => s.name);
30
+ const uniqueNames = new Set(names);
31
+ expect(uniqueNames.size).toBe(names.length);
32
+ });
33
+ it("non-planned sails have an addon.json manifest", () => {
34
+ const registry = JSON.parse(readFileSync(REGISTRY_PATH, "utf-8"));
35
+ const availableSails = registry.sails.filter((s) => s.status !== "planned");
36
+ for (const sail of availableSails) {
37
+ const manifestPath = join(SAILS_DIR, sail.name, "addon.json");
38
+ expect(existsSync(manifestPath), `Missing addon.json for sail: ${sail.name}`).toBe(true);
39
+ }
40
+ });
41
+ it("non-planned sails have an install.ts file", () => {
42
+ const registry = JSON.parse(readFileSync(REGISTRY_PATH, "utf-8"));
43
+ const availableSails = registry.sails.filter((s) => s.status !== "planned");
44
+ for (const sail of availableSails) {
45
+ const installPath = join(SAILS_DIR, sail.name, "install.ts");
46
+ expect(existsSync(installPath), `Missing install.ts for sail: ${sail.name}`).toBe(true);
47
+ }
48
+ });
49
+ it("addon.json manifests have valid structure", () => {
50
+ const registry = JSON.parse(readFileSync(REGISTRY_PATH, "utf-8"));
51
+ const availableSails = registry.sails.filter((s) => s.status !== "planned");
52
+ for (const sail of availableSails) {
53
+ const manifestPath = join(SAILS_DIR, sail.name, "addon.json");
54
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
55
+ expect(manifest.name).toBe(sail.name);
56
+ expect(manifest.displayName).toBeTruthy();
57
+ expect(manifest.version).toBeTruthy();
58
+ expect(manifest.dependencies).toBeDefined();
59
+ expect(manifest.dependencies.backend).toBeDefined();
60
+ expect(manifest.dependencies.frontend).toBeDefined();
61
+ expect(manifest.modifies).toBeDefined();
62
+ expect(manifest.adds).toBeDefined();
63
+ }
64
+ });
65
+ it("contains the expected core sails", () => {
66
+ const registry = JSON.parse(readFileSync(REGISTRY_PATH, "utf-8"));
67
+ const sailNames = registry.sails.map((s) => s.name);
68
+ expect(sailNames).toContain("google-oauth");
69
+ expect(sailNames).toContain("stripe");
70
+ expect(sailNames).toContain("gdpr");
71
+ expect(sailNames).toContain("r2-storage");
72
+ expect(sailNames).toContain("push-notifications");
73
+ expect(sailNames).toContain("analytics");
74
+ expect(sailNames).toContain("admin-dashboard");
75
+ expect(sailNames).toContain("i18n");
76
+ });
77
+ it("planned sails are correctly marked", () => {
78
+ const registry = JSON.parse(readFileSync(REGISTRY_PATH, "utf-8"));
79
+ const plannedSails = registry.sails.filter((s) => s.status === "planned");
80
+ // Based on the current registry, rate-limiting and file-uploads are planned
81
+ const plannedNames = plannedSails.map((s) => s.name);
82
+ expect(plannedNames).toContain("rate-limiting");
83
+ expect(plannedNames).toContain("file-uploads");
84
+ });
85
+ });
86
+ //# sourceMappingURL=registry.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.test.js","sourceRoot":"","sources":["../../src/__tests__/registry.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sail-installer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sail-installer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/sail-installer.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,158 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { readFileSync, writeFileSync, mkdirSync, rmSync, } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ // ---------------------------------------------------------------------------
6
+ // Test insertAtMarker directly (exported function)
7
+ // ---------------------------------------------------------------------------
8
+ // We need to mock chalk and ora to avoid terminal output during tests
9
+ vi.mock("chalk", () => ({
10
+ default: {
11
+ yellow: (s) => s,
12
+ cyan: (s) => s,
13
+ bold: (s) => s,
14
+ white: (s) => s,
15
+ green: (s) => s,
16
+ red: (s) => s,
17
+ gray: (s) => s,
18
+ },
19
+ }));
20
+ vi.mock("ora", () => ({
21
+ default: () => ({
22
+ start: vi.fn().mockReturnThis(),
23
+ succeed: vi.fn().mockReturnThis(),
24
+ fail: vi.fn().mockReturnThis(),
25
+ warn: vi.fn().mockReturnThis(),
26
+ }),
27
+ }));
28
+ const { insertAtMarker, getManualSteps } = await import("../sail-installer.js");
29
+ describe("insertAtMarker", () => {
30
+ let tempDir;
31
+ beforeEach(() => {
32
+ tempDir = join(tmpdir(), `keel-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
33
+ mkdirSync(tempDir, { recursive: true });
34
+ // Clear manual steps
35
+ getManualSteps();
36
+ });
37
+ afterEach(() => {
38
+ rmSync(tempDir, { recursive: true, force: true });
39
+ });
40
+ it("inserts code after marker comment", () => {
41
+ const filePath = join(tempDir, "test.ts");
42
+ writeFileSync(filePath, `import express from "express";
43
+ // [SAIL_IMPORTS]
44
+
45
+ const app = express();
46
+ // [SAIL_ROUTES]
47
+
48
+ app.listen(3000);
49
+ `);
50
+ const result = insertAtMarker(filePath, "// [SAIL_IMPORTS]", 'import { stripeRouter } from "./routes/stripe.js";');
51
+ expect(result).toBe(true);
52
+ const content = readFileSync(filePath, "utf-8");
53
+ expect(content).toContain('import { stripeRouter } from "./routes/stripe.js";');
54
+ // The import should appear after the marker
55
+ const markerIndex = content.indexOf("// [SAIL_IMPORTS]");
56
+ const importIndex = content.indexOf('import { stripeRouter }');
57
+ expect(importIndex).toBeGreaterThan(markerIndex);
58
+ });
59
+ it("inserts code after JSX marker comment", () => {
60
+ const filePath = join(tempDir, "router.tsx");
61
+ writeFileSync(filePath, `<Route path="/" element={<Home />} />
62
+ {/* [SAIL_ROUTES] */}
63
+ </Route>
64
+ `);
65
+ const result = insertAtMarker(filePath, "{/* [SAIL_ROUTES] */}", ' <Route path="/pricing" element={<Pricing />} />');
66
+ expect(result).toBe(true);
67
+ const content = readFileSync(filePath, "utf-8");
68
+ expect(content).toContain('<Route path="/pricing" element={<Pricing />} />');
69
+ });
70
+ it("is idempotent — does not insert the same code twice", () => {
71
+ const filePath = join(tempDir, "test.ts");
72
+ const originalContent = `// [SAIL_IMPORTS]
73
+ const x = 1;
74
+ `;
75
+ writeFileSync(filePath, originalContent);
76
+ const code = 'import { foo } from "./foo.js";';
77
+ // First insertion
78
+ insertAtMarker(filePath, "// [SAIL_IMPORTS]", code);
79
+ const afterFirst = readFileSync(filePath, "utf-8");
80
+ // Second insertion — should be a no-op
81
+ insertAtMarker(filePath, "// [SAIL_IMPORTS]", code);
82
+ const afterSecond = readFileSync(filePath, "utf-8");
83
+ expect(afterFirst).toBe(afterSecond);
84
+ // Count occurrences of the code
85
+ const matches = afterSecond.match(/import \{ foo \}/g);
86
+ expect(matches?.length).toBe(1);
87
+ });
88
+ it("records manual step when marker is missing", () => {
89
+ const filePath = join(tempDir, "test.ts");
90
+ writeFileSync(filePath, `import express from "express";
91
+ const app = express();
92
+ `);
93
+ const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
94
+ const result = insertAtMarker(filePath, "// [SAIL_IMPORTS]", 'import { foo } from "./foo.js";');
95
+ expect(result).toBe(false);
96
+ const steps = getManualSteps();
97
+ expect(steps.length).toBeGreaterThan(0);
98
+ expect(steps[0]).toContain('import { foo } from "./foo.js";');
99
+ consoleSpy.mockRestore();
100
+ });
101
+ it("records manual step when file does not exist", () => {
102
+ const filePath = join(tempDir, "nonexistent.ts");
103
+ const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
104
+ const result = insertAtMarker(filePath, "// [SAIL_IMPORTS]", 'import { foo } from "./foo.js";');
105
+ expect(result).toBe(false);
106
+ const steps = getManualSteps();
107
+ expect(steps.length).toBeGreaterThan(0);
108
+ consoleSpy.mockRestore();
109
+ });
110
+ it("preserves existing content around marker", () => {
111
+ const filePath = join(tempDir, "test.ts");
112
+ writeFileSync(filePath, `line1
113
+ // [SAIL_IMPORTS]
114
+ line3
115
+ line4
116
+ `);
117
+ insertAtMarker(filePath, "// [SAIL_IMPORTS]", "inserted_line");
118
+ const content = readFileSync(filePath, "utf-8");
119
+ expect(content).toContain("line1");
120
+ expect(content).toContain("// [SAIL_IMPORTS]");
121
+ expect(content).toContain("inserted_line");
122
+ expect(content).toContain("line3");
123
+ expect(content).toContain("line4");
124
+ });
125
+ it("handles multiple markers in same file", () => {
126
+ const filePath = join(tempDir, "test.ts");
127
+ writeFileSync(filePath, `// [SAIL_IMPORTS]
128
+
129
+ const app = express();
130
+
131
+ // [SAIL_ROUTES]
132
+
133
+ app.listen(3000);
134
+ `);
135
+ insertAtMarker(filePath, "// [SAIL_IMPORTS]", 'import { a } from "./a.js";');
136
+ insertAtMarker(filePath, "// [SAIL_ROUTES]", 'app.use("/api/a", a);');
137
+ const content = readFileSync(filePath, "utf-8");
138
+ expect(content).toContain('import { a } from "./a.js";');
139
+ expect(content).toContain('app.use("/api/a", a);');
140
+ });
141
+ });
142
+ describe("getManualSteps", () => {
143
+ it("returns accumulated steps and clears the list", () => {
144
+ // Clear any existing steps
145
+ getManualSteps();
146
+ const filePath = join(tmpdir(), "nonexistent-file.ts");
147
+ const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
148
+ insertAtMarker(filePath, "// [MARKER]", "code1");
149
+ insertAtMarker(filePath, "// [MARKER]", "code2");
150
+ const steps = getManualSteps();
151
+ expect(steps.length).toBe(2);
152
+ // After retrieval, should be empty
153
+ const stepsAgain = getManualSteps();
154
+ expect(stepsAgain.length).toBe(0);
155
+ consoleSpy.mockRestore();
156
+ });
157
+ });
158
+ //# sourceMappingURL=sail-installer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sail-installer.test.js","sourceRoot":"","sources":["../../src/__tests__/sail-installer.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Create project logic — shared between create-keel binary and `keel create` command.
3
+ *
4
+ * Supports non-interactive mode:
5
+ * keel create my-app --yes # all defaults
6
+ * keel create my-app --yes --db=docker # specify database
7
+ * keel create my-app --yes --sails=stripe # with sails
8
+ * keel create my-app --yes --resend-key=re_xx --email-from=noreply@x.com
9
+ */
10
+ export declare function main(args?: string[]): Promise<void>;
11
+ //# sourceMappingURL=create-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-runner.d.ts","sourceRoot":"","sources":["../src/create-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,wBAAsB,IAAI,CAAC,IAAI,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAwD7D"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Create project logic — shared between create-keel binary and `keel create` command.
3
+ *
4
+ * Supports non-interactive mode:
5
+ * keel create my-app --yes # all defaults
6
+ * keel create my-app --yes --db=docker # specify database
7
+ * keel create my-app --yes --sails=stripe # with sails
8
+ * keel create my-app --yes --resend-key=re_xx --email-from=noreply@x.com
9
+ */
10
+ import chalk from "chalk";
11
+ import { runPrompts, parseFlags } from "./prompts.js";
12
+ import { scaffold } from "./scaffold.js";
13
+ import { installSails } from "./sail-installer.js";
14
+ export async function main(args = []) {
15
+ const { projectName, flags } = parseFlags(args);
16
+ // Only show banner in interactive mode
17
+ if (!flags.yes) {
18
+ console.log();
19
+ console.log(chalk.blue(" ██╗ ██╗███████╗███████╗██╗ "));
20
+ console.log(chalk.blue(" ██║ ██╔╝██╔════╝██╔════╝██║ "));
21
+ console.log(chalk.blue(" █████╔╝ █████╗ █████╗ ██║ "));
22
+ console.log(chalk.blue(" ██╔═██╗ ██╔══╝ ██╔══╝ ██║ "));
23
+ console.log(chalk.blue(" ██║ ██╗███████╗███████╗███████╗"));
24
+ console.log(chalk.blue(" ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝"));
25
+ console.log();
26
+ console.log(chalk.gray(" a codai project"));
27
+ console.log();
28
+ }
29
+ let config;
30
+ try {
31
+ config = await runPrompts(projectName, flags);
32
+ }
33
+ catch {
34
+ console.log(chalk.yellow("\n Cancelled.\n"));
35
+ process.exit(0);
36
+ }
37
+ console.log();
38
+ const success = await scaffold(config);
39
+ if (!success) {
40
+ console.log(chalk.red("\n Scaffolding failed. See errors above.\n"));
41
+ process.exit(1);
42
+ }
43
+ // Install selected sails
44
+ if (config.sails.length > 0) {
45
+ console.log();
46
+ console.log(chalk.bold(" Installing sails..."));
47
+ await installSails(config);
48
+ }
49
+ // Done
50
+ console.log();
51
+ console.log(chalk.green.bold(" ✔ Project created!"));
52
+ console.log();
53
+ console.log(chalk.bold(" Quick start:"));
54
+ console.log(chalk.cyan(` cd ${config.projectName}`));
55
+ console.log(chalk.cyan(" keel dev"));
56
+ console.log();
57
+ if (!config.resendApiKey) {
58
+ console.log(chalk.gray(" Tip: Set RESEND_API_KEY in .env for email sending."));
59
+ console.log();
60
+ }
61
+ process.exit(0);
62
+ }
63
+ //# sourceMappingURL=create-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-runner.js","sourceRoot":"","sources":["../src/create-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * create-keel CLI entry point (a codai project)
4
+ *
5
+ * Usage:
6
+ * npx create-keel my-app
7
+ * npx create-keel # prompts for project name
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":";AAEA;;;;;;GAMG"}
package/dist/create.js ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * create-keel CLI entry point (a codai project)
4
+ *
5
+ * Usage:
6
+ * npx create-keel my-app
7
+ * npx create-keel # prompts for project name
8
+ */
9
+ import chalk from "chalk";
10
+ import { main } from "./create-runner.js";
11
+ main(process.argv.slice(2)).catch((err) => {
12
+ console.error(chalk.red("Unexpected error:"), err);
13
+ process.exit(1);
14
+ });
15
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":";AAEA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE1C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACxC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,mBAAmB,CAAC,EAAE,GAAG,CAAC,CAAC;IACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * keel CLI — Project management & sail tool (a codai project)
4
+ *
5
+ * Used from inside a keel project to manage sails, generate code,
6
+ * run database operations, and perform health checks.
7
+ *
8
+ * Usage:
9
+ * npx @codaijs/keel sail add <name> — install a sail into the current project
10
+ * npx @codaijs/keel sail remove <name> — remove a sail from the current project
11
+ * npx @codaijs/keel list — list available sails with status
12
+ * npx @codaijs/keel info <name> — show sail details
13
+ * npx @codaijs/keel doctor — run project health checks
14
+ * npx @codaijs/keel generate route <name> — scaffold a new API route
15
+ * npx @codaijs/keel generate page <name> — scaffold a new React page
16
+ * npx @codaijs/keel generate email <name> — scaffold a new email template
17
+ * npx @codaijs/keel db:reset — drop and recreate database schema
18
+ * npx @codaijs/keel db:studio — open Drizzle Studio
19
+ * npx @codaijs/keel db:seed — run database seed file
20
+ * npx @codaijs/keel env — check environment variables
21
+ * npx @codaijs/keel upgrade — upgrade keel CLI to latest version
22
+ */
23
+ export {};
24
+ //# sourceMappingURL=manage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manage.d.ts","sourceRoot":"","sources":["../src/manage.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;GAoBG"}