@sansavision/aurora 0.1.0-alpha.20260212.4

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 (150) hide show
  1. package/README.md +4 -0
  2. package/package.json +17 -0
  3. package/src/ai-diagnostics.ts +156 -0
  4. package/src/ai.ts +574 -0
  5. package/src/analyze.ts +669 -0
  6. package/src/bin/aurora.ts +15 -0
  7. package/src/build.ts +431 -0
  8. package/src/bun-test-shims.d.ts +17 -0
  9. package/src/create-feature.ts +419 -0
  10. package/src/create-route.ts +581 -0
  11. package/src/create.ts +425 -0
  12. package/src/dev.ts +126 -0
  13. package/src/devtools.ts +1143 -0
  14. package/src/doctor.ts +611 -0
  15. package/src/explain.ts +855 -0
  16. package/src/help.ts +39 -0
  17. package/src/index.ts +34 -0
  18. package/src/init.ts +1011 -0
  19. package/src/inspect-cache.ts +464 -0
  20. package/src/lsp-inline-hints.ts +254 -0
  21. package/src/node-shims.d.ts +26 -0
  22. package/src/process.d.ts +11 -0
  23. package/src/query-profiler.ts +520 -0
  24. package/src/realtime-monitor.ts +389 -0
  25. package/src/registry.ts +303 -0
  26. package/src/run.ts +37 -0
  27. package/src/start.ts +56 -0
  28. package/src/test.ts +289 -0
  29. package/templates/basic/README.md +16 -0
  30. package/templates/basic/package.json +10 -0
  31. package/templates/basic/src/actions/createMessage.action.server.ts +22 -0
  32. package/templates/basic/src/lib/auth.server.ts +11 -0
  33. package/templates/basic/src/queries/listMessages.server.ts +17 -0
  34. package/templates/basic/src/routes/index.tsx +12 -0
  35. package/templates/blog/README.md +17 -0
  36. package/templates/blog/package.json +12 -0
  37. package/templates/blog/public/assets/og-default.svg +17 -0
  38. package/templates/blog/src/content/loadPosts.server.ts +22 -0
  39. package/templates/blog/src/content/posts/hello-world.md +11 -0
  40. package/templates/blog/src/content/posts/release-notes.md +9 -0
  41. package/templates/blog/src/routes/index.tsx +22 -0
  42. package/templates/blog/src/routes/posts/[slug].tsx +19 -0
  43. package/templates/blog/src/seo/meta.ts +19 -0
  44. package/templates/dashboard/README.md +18 -0
  45. package/templates/dashboard/package.json +10 -0
  46. package/templates/dashboard/src/actions/acknowledgeAlert.action.server.ts +6 -0
  47. package/templates/dashboard/src/queries/getDashboardMetrics.server.ts +30 -0
  48. package/templates/dashboard/src/realtime/useDashboardRealtime.client.ts +13 -0
  49. package/templates/dashboard/src/routes/index.tsx +19 -0
  50. package/templates/dashboard/src/widgets/DataGrid.client.ts +8 -0
  51. package/templates/dashboard/src/widgets/MetricChart.client.ts +8 -0
  52. package/templates/desktop/README.md +18 -0
  53. package/templates/desktop/package.json +11 -0
  54. package/templates/desktop/src/actions/saveDesktopPreference.action.server.ts +28 -0
  55. package/templates/desktop/src/desktop/secureStorage.client.ts +20 -0
  56. package/templates/desktop/src/desktop/tauriBridge.client.ts +14 -0
  57. package/templates/desktop/src/queries/getDesktopSyncStatus.server.ts +9 -0
  58. package/templates/desktop/src/routes/index.tsx +27 -0
  59. package/templates/desktop/src/sync/offlineSyncBoundary.server.ts +27 -0
  60. package/templates/feature-skeleton/README.md +13 -0
  61. package/templates/feature-skeleton/actions/createFeature.action.server.ts +19 -0
  62. package/templates/feature-skeleton/index.ts +8 -0
  63. package/templates/feature-skeleton/queries/listFeature.server.ts +15 -0
  64. package/templates/feature-skeleton/realtime/useFeatureRealtime.client.ts +16 -0
  65. package/templates/feature-skeleton/template.manifest.json +15 -0
  66. package/templates/feature-skeleton/ui/FeatureView.client.tsx +14 -0
  67. package/templates/mobile/README.md +17 -0
  68. package/templates/mobile/package.json +11 -0
  69. package/templates/mobile/src/mobile/auth/session-handoff.client.ts +69 -0
  70. package/templates/mobile/src/mobile/generated/mobile-api-sdk.ts +62 -0
  71. package/templates/mobile/src/mobile/transport/mobile-api-transport.client.ts +122 -0
  72. package/templates/mobile/src/routes/index.tsx +134 -0
  73. package/templates/monorepo/README.md +18 -0
  74. package/templates/monorepo/apps/web/package.json +9 -0
  75. package/templates/monorepo/apps/web/src/routes/index.tsx +1 -0
  76. package/templates/monorepo/package.json +13 -0
  77. package/templates/monorepo/packages/shared/README.md +3 -0
  78. package/templates/monorepo/packages/ui/README.md +3 -0
  79. package/templates/saas/README.md +17 -0
  80. package/templates/saas/package.json +10 -0
  81. package/templates/saas/src/admin/getDashboard.server.ts +18 -0
  82. package/templates/saas/src/auth/session.server.ts +13 -0
  83. package/templates/saas/src/billing/checkout.server.ts +11 -0
  84. package/templates/saas/src/email/sendWelcome.server.ts +8 -0
  85. package/templates/saas/src/realtime/notifications.server.ts +8 -0
  86. package/templates/saas/src/routes/index.tsx +20 -0
  87. package/test/ai.test.ts +94 -0
  88. package/test/analyze.test.ts +301 -0
  89. package/test/build.test.ts +135 -0
  90. package/test/create-feature.test.ts +145 -0
  91. package/test/create-route.test.ts +117 -0
  92. package/test/create.test.ts +222 -0
  93. package/test/dev.test.ts +52 -0
  94. package/test/devtools.test.ts +130 -0
  95. package/test/doctor.test.ts +129 -0
  96. package/test/explain.test.ts +232 -0
  97. package/test/feature-skeleton.test.ts +53 -0
  98. package/test/fixtures/analyze/cache-input.invalid.json +1 -0
  99. package/test/fixtures/analyze/cache-input.missing-keyhash.v1.json +10 -0
  100. package/test/fixtures/analyze/cache-input.unsupported-version.v2.json +10 -0
  101. package/test/fixtures/analyze/cache-input.v1.json +12 -0
  102. package/test/fixtures/analyze/compiler-manifest/manifest.json +11 -0
  103. package/test/fixtures/analyze/guardrails-input.unsupported-version.v2.json +4 -0
  104. package/test/fixtures/analyze/guardrails-input.v1.json +49 -0
  105. package/test/fixtures/analyze/query-input.invalid-cache-status.v1.json +11 -0
  106. package/test/fixtures/analyze/query-input.unsupported-version.v2.json +11 -0
  107. package/test/fixtures/analyze/query-input.v1.json +18 -0
  108. package/test/fixtures/analyze/realtime-input.missing-lag-p95.v1.json +10 -0
  109. package/test/fixtures/analyze/realtime-input.unsupported-version.v2.json +8 -0
  110. package/test/fixtures/analyze/realtime-input.v1.json +12 -0
  111. package/test/fixtures/cache-inspector/cache-input.v1.json +23 -0
  112. package/test/fixtures/cache-inspector/invalid.json +1 -0
  113. package/test/fixtures/cache-inspector/snapshot.v1.json +34 -0
  114. package/test/fixtures/cache-inspector/unsupported-version.v2.json +13 -0
  115. package/test/fixtures/devtools/healthy.v1.json +130 -0
  116. package/test/fixtures/devtools/invalid.json +1 -0
  117. package/test/fixtures/devtools/unsupported-version.v2.json +8 -0
  118. package/test/fixtures/devtools/warn.v1.json +114 -0
  119. package/test/fixtures/doctor/clean/src/page.tsx +3 -0
  120. package/test/fixtures/doctor/findings/src/accessibility.client.tsx +7 -0
  121. package/test/fixtures/doctor/findings/src/migration.config.ts +3 -0
  122. package/test/fixtures/doctor/findings/src/page.client.tsx +5 -0
  123. package/test/fixtures/doctor/findings/src/perf.server.ts +15 -0
  124. package/test/fixtures/doctor/findings/src/routes.js +3 -0
  125. package/test/fixtures/doctor/findings/src/security.server.ts +7 -0
  126. package/test/fixtures/doctor/findings/src/users.server.ts +3 -0
  127. package/test/fixtures/doctor/governance/src/features/analytics/OWNERS.ts +2 -0
  128. package/test/fixtures/doctor/governance/src/features/analytics/page.tsx +3 -0
  129. package/test/fixtures/doctor/governance/src/features/billing/page.tsx +3 -0
  130. package/test/fixtures/explain/invalid.json +1 -0
  131. package/test/fixtures/explain/module-report.unsupported-version.v2.json +6 -0
  132. package/test/fixtures/explain/module-report.v1.json +72 -0
  133. package/test/fixtures/query-profiler/healthy.v1.json +11 -0
  134. package/test/fixtures/query-profiler/invalid.json +1 -0
  135. package/test/fixtures/query-profiler/unsupported-version.v2.json +6 -0
  136. package/test/fixtures/query-profiler/warning.v1.json +10 -0
  137. package/test/fixtures/realtime-monitor/healthy.v1.json +8 -0
  138. package/test/fixtures/realtime-monitor/invalid.json +1 -0
  139. package/test/fixtures/realtime-monitor/unsupported-version.v2.json +8 -0
  140. package/test/fixtures/realtime-monitor/warning.v1.json +8 -0
  141. package/test/help-parity.test.ts +104 -0
  142. package/test/init.test.ts +164 -0
  143. package/test/inspect-cache.test.ts +112 -0
  144. package/test/lsp-inline-hints.test.ts +65 -0
  145. package/test/query-profiler.test.ts +123 -0
  146. package/test/realtime-monitor.test.ts +115 -0
  147. package/test/registry.test.ts +41 -0
  148. package/test/start.test.ts +23 -0
  149. package/test/test-command.test.ts +65 -0
  150. package/tsconfig.json +19 -0
@@ -0,0 +1,117 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import { describe, expect, it } from "bun:test";
5
+
6
+ import { runAuroraCli } from "../src";
7
+
8
+ let tempCounter = 1;
9
+
10
+ function uniqueTempPath(suffix: string): string {
11
+ tempCounter += 1;
12
+ return `/tmp/aurora-cli-create-route-${suffix}-${Date.now()}-${tempCounter}`;
13
+ }
14
+
15
+ describe("aurora create-route", () => {
16
+ it("scaffolds a default form route with novice-safe boilerplate", () => {
17
+ const cwd = uniqueTempPath("default");
18
+ mkdirSync(cwd, { recursive: true });
19
+
20
+ const result = runAuroraCli(["create-route", "guestbook"], { cwd });
21
+ const routeRoot = join(cwd, "src/routes/guestbook");
22
+
23
+ expect(result.exitCode).toBe(0);
24
+ expect(result.stdout).toContain("aurora create-route bootstrap");
25
+ expect(result.stdout).toContain("route_slug: guestbook");
26
+ expect(result.stdout).toContain("route_kind: form");
27
+ expect(result.stdout).toContain("auth: public");
28
+
29
+ expect(existsSync(join(routeRoot, "page.tsx"))).toBe(true);
30
+ expect(existsSync(join(routeRoot, "guestbook.client.tsx"))).toBe(true);
31
+ expect(existsSync(join(routeRoot, "guestbook.server.ts"))).toBe(true);
32
+
33
+ const serverSource = readFileSync(join(routeRoot, "guestbook.server.ts"), "utf8");
34
+ expect(serverSource).toContain('export const runtime = "node";');
35
+ expect(serverSource).toContain('export const auth = "public";');
36
+ expect(serverSource).toContain("export async function listGuestbook");
37
+ expect(serverSource).toContain("export async function submitGuestbook");
38
+ });
39
+
40
+ it("supports kind/data/auth/realtime options", () => {
41
+ const cwd = uniqueTempPath("custom");
42
+ mkdirSync(cwd, { recursive: true });
43
+
44
+ const result = runAuroraCli(
45
+ [
46
+ "create-route",
47
+ "alerts",
48
+ "--kind",
49
+ "dashboard",
50
+ "--data",
51
+ "api",
52
+ "--auth",
53
+ "user",
54
+ "--realtime",
55
+ ],
56
+ { cwd },
57
+ );
58
+
59
+ const routeRoot = join(cwd, "src/routes/alerts");
60
+ const serverSource = readFileSync(join(routeRoot, "alerts.server.ts"), "utf8");
61
+
62
+ expect(result.exitCode).toBe(0);
63
+ expect(result.stdout).toContain("route_kind: dashboard");
64
+ expect(result.stdout).toContain("data_source: api");
65
+ expect(result.stdout).toContain("auth: user");
66
+ expect(result.stdout).toContain("realtime: enabled");
67
+
68
+ expect(serverSource).toContain('export const auth = "user";');
69
+ expect(serverSource).toContain('export const dataSource = "api";');
70
+ expect(serverSource).toContain("loadAlertsMetrics");
71
+ expect(serverSource).toContain("AlertsRealtimeChannel");
72
+ });
73
+
74
+ it("returns a parse error when route name is missing", () => {
75
+ const cwd = uniqueTempPath("missing");
76
+ mkdirSync(cwd, { recursive: true });
77
+
78
+ const result = runAuroraCli(["create-route"], { cwd });
79
+
80
+ expect(result.exitCode).toBe(2);
81
+ expect(result.stderr).toContain("aurora create-route: route name is required");
82
+ });
83
+
84
+ it("rejects invalid kind/data/auth values and unknown options", () => {
85
+ const cwd = uniqueTempPath("invalid");
86
+ mkdirSync(cwd, { recursive: true });
87
+
88
+ const invalidKind = runAuroraCli(["create-route", "x", "--kind", "wizard"], { cwd });
89
+ const invalidData = runAuroraCli(["create-route", "x", "--data", "cache"], { cwd });
90
+ const invalidAuth = runAuroraCli(["create-route", "x", "--auth", "guest"], { cwd });
91
+ const unknown = runAuroraCli(["create-route", "x", "--dry-run"], { cwd });
92
+
93
+ expect(invalidKind.exitCode).toBe(2);
94
+ expect(invalidKind.stderr).toContain("--kind requires one of crud|read-only|form|dashboard|empty");
95
+
96
+ expect(invalidData.exitCode).toBe(2);
97
+ expect(invalidData.stderr).toContain("--data requires one of database|api|none");
98
+
99
+ expect(invalidAuth.exitCode).toBe(2);
100
+ expect(invalidAuth.stderr).toContain("--auth requires 'public', 'user', 'admin', or 'permission:<name>'");
101
+
102
+ expect(unknown.exitCode).toBe(2);
103
+ expect(unknown.stderr).toContain("unknown option '--dry-run'");
104
+ });
105
+
106
+ it("returns an error when route directory already exists and is not empty", () => {
107
+ const cwd = uniqueTempPath("exists");
108
+ const routeRoot = join(cwd, "src/routes/reports");
109
+ mkdirSync(routeRoot, { recursive: true });
110
+ writeFileSync(join(routeRoot, "seed.txt"), "seed\n", "utf8");
111
+
112
+ const result = runAuroraCli(["create-route", "reports"], { cwd });
113
+
114
+ expect(result.exitCode).toBe(2);
115
+ expect(result.stderr).toContain("target directory already exists and is not empty");
116
+ });
117
+ });
@@ -0,0 +1,222 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+
5
+ import { runAuroraCli } from "../src";
6
+
7
+ let tempCounter = 1;
8
+
9
+ function uniqueTempPath(suffix: string): string {
10
+ tempCounter += 1;
11
+ return `/tmp/aurora-cli-create-${suffix}-${Date.now()}-${tempCounter}`;
12
+ }
13
+
14
+ describe("aurora create", () => {
15
+ it("creates a project scaffold with default basic template", () => {
16
+ const cwd = uniqueTempPath("cwd-basic");
17
+ mkdirSync(cwd, { recursive: true });
18
+
19
+ const result = runAuroraCli(["create", "my-app"], { cwd });
20
+ const projectRoot = join(cwd, "my-app");
21
+
22
+ expect(result.exitCode).toBe(0);
23
+ expect(result.stdout).toContain("aurora create bootstrap");
24
+ expect(result.stdout).toContain("template: basic");
25
+ expect(result.stdout).toContain(`project_root: ${projectRoot}`);
26
+
27
+ expect(existsSync(join(projectRoot, "package.json"))).toBe(true);
28
+ expect(existsSync(join(projectRoot, "src/routes/index.tsx"))).toBe(true);
29
+ expect(existsSync(join(projectRoot, "src/actions/createMessage.action.server.ts"))).toBe(true);
30
+ expect(existsSync(join(projectRoot, "src/queries/listMessages.server.ts"))).toBe(true);
31
+ expect(existsSync(join(projectRoot, "src/lib/auth.server.ts"))).toBe(true);
32
+ expect(existsSync(join(projectRoot, "aurora.template.json"))).toBe(true);
33
+
34
+ const templateMeta = readFileSync(join(projectRoot, "aurora.template.json"), "utf8");
35
+ expect(templateMeta).toContain("\"template\": \"basic\"");
36
+ const readme = readFileSync(join(projectRoot, "README.md"), "utf8");
37
+ expect(readme).toContain("Aurora basic starter template.");
38
+ });
39
+
40
+ it("supports template selection and --git scaffold option", () => {
41
+ const cwd = uniqueTempPath("cwd-dashboard");
42
+ mkdirSync(cwd, { recursive: true });
43
+
44
+ const result = runAuroraCli(["create", "dash-app", "--template", "dashboard", "--git"], {
45
+ cwd,
46
+ });
47
+ const projectRoot = join(cwd, "dash-app");
48
+
49
+ expect(result.exitCode).toBe(0);
50
+ expect(result.stdout).toContain("template: dashboard");
51
+ expect(result.stdout).toContain("git: enabled");
52
+
53
+ expect(existsSync(join(projectRoot, ".gitignore"))).toBe(true);
54
+ const readme = readFileSync(join(projectRoot, "README.md"), "utf8");
55
+ expect(readme).toContain("Template: dashboard");
56
+ });
57
+
58
+ it("scaffolds dashboard template with chart/grid/realtime seams", () => {
59
+ const cwd = uniqueTempPath("cwd-dashboard-assets");
60
+ mkdirSync(cwd, { recursive: true });
61
+
62
+ const result = runAuroraCli(["create", "dashboard-app", "--template", "dashboard"], {
63
+ cwd,
64
+ });
65
+ const projectRoot = join(cwd, "dashboard-app");
66
+
67
+ expect(result.exitCode).toBe(0);
68
+ expect(result.stdout).toContain("template: dashboard");
69
+ expect(existsSync(join(projectRoot, "src/widgets/MetricChart.client.ts"))).toBe(true);
70
+ expect(existsSync(join(projectRoot, "src/widgets/DataGrid.client.ts"))).toBe(true);
71
+ expect(existsSync(join(projectRoot, "src/realtime/useDashboardRealtime.client.ts"))).toBe(
72
+ true,
73
+ );
74
+ expect(existsSync(join(projectRoot, "src/queries/getDashboardMetrics.server.ts"))).toBe(true);
75
+ });
76
+
77
+ it("scaffolds the saas template with auth/billing/realtime/email seams", () => {
78
+ const cwd = uniqueTempPath("cwd-saas");
79
+ mkdirSync(cwd, { recursive: true });
80
+
81
+ const result = runAuroraCli(["create", "saas-app", "--template", "saas"], {
82
+ cwd,
83
+ });
84
+ const projectRoot = join(cwd, "saas-app");
85
+
86
+ expect(result.exitCode).toBe(0);
87
+ expect(result.stdout).toContain("template: saas");
88
+ expect(existsSync(join(projectRoot, "src/auth/session.server.ts"))).toBe(true);
89
+ expect(existsSync(join(projectRoot, "src/billing/checkout.server.ts"))).toBe(true);
90
+ expect(existsSync(join(projectRoot, "src/realtime/notifications.server.ts"))).toBe(true);
91
+ expect(existsSync(join(projectRoot, "src/email/sendWelcome.server.ts"))).toBe(true);
92
+ });
93
+
94
+ it("scaffolds the blog template with markdown, seo, and static assets", () => {
95
+ const cwd = uniqueTempPath("cwd-blog");
96
+ mkdirSync(cwd, { recursive: true });
97
+
98
+ const result = runAuroraCli(["create", "blog-app", "--template", "blog"], {
99
+ cwd,
100
+ });
101
+ const projectRoot = join(cwd, "blog-app");
102
+
103
+ expect(result.exitCode).toBe(0);
104
+ expect(result.stdout).toContain("template: blog");
105
+ expect(existsSync(join(projectRoot, "src/content/posts/hello-world.md"))).toBe(true);
106
+ expect(existsSync(join(projectRoot, "src/content/posts/release-notes.md"))).toBe(true);
107
+ expect(existsSync(join(projectRoot, "src/seo/meta.ts"))).toBe(true);
108
+ expect(existsSync(join(projectRoot, "src/routes/posts/[slug].tsx"))).toBe(true);
109
+ expect(existsSync(join(projectRoot, "public/assets/og-default.svg"))).toBe(true);
110
+
111
+ const readme = readFileSync(join(projectRoot, "README.md"), "utf8");
112
+ expect(readme).toContain("Aurora blog starter template.");
113
+ });
114
+
115
+ it("scaffolds the desktop template with tauri and secure-storage seams", () => {
116
+ const cwd = uniqueTempPath("cwd-desktop");
117
+ mkdirSync(cwd, { recursive: true });
118
+
119
+ const result = runAuroraCli(["create", "desktop-app", "--template", "desktop"], {
120
+ cwd,
121
+ });
122
+ const projectRoot = join(cwd, "desktop-app");
123
+
124
+ expect(result.exitCode).toBe(0);
125
+ expect(result.stdout).toContain("template: desktop");
126
+ expect(existsSync(join(projectRoot, "src/desktop/tauriBridge.client.ts"))).toBe(true);
127
+ expect(existsSync(join(projectRoot, "src/desktop/secureStorage.client.ts"))).toBe(true);
128
+ expect(existsSync(join(projectRoot, "src/sync/offlineSyncBoundary.server.ts"))).toBe(true);
129
+ expect(existsSync(join(projectRoot, "src/queries/getDesktopSyncStatus.server.ts"))).toBe(
130
+ true,
131
+ );
132
+ expect(
133
+ existsSync(join(projectRoot, "src/actions/saveDesktopPreference.action.server.ts")),
134
+ ).toBe(true);
135
+
136
+ const readme = readFileSync(join(projectRoot, "README.md"), "utf8");
137
+ expect(readme).toContain("Aurora desktop starter template.");
138
+ });
139
+
140
+ it("scaffolds the mobile template with typed SDK and retry transport seams", () => {
141
+ const cwd = uniqueTempPath("cwd-mobile");
142
+ mkdirSync(cwd, { recursive: true });
143
+
144
+ const result = runAuroraCli(["create", "mobile-app", "--template", "mobile"], {
145
+ cwd,
146
+ });
147
+ const projectRoot = join(cwd, "mobile-app");
148
+
149
+ expect(result.exitCode).toBe(0);
150
+ expect(result.stdout).toContain("template: mobile");
151
+ expect(existsSync(join(projectRoot, "src/mobile/generated/mobile-api-sdk.ts"))).toBe(true);
152
+ expect(
153
+ existsSync(join(projectRoot, "src/mobile/auth/session-handoff.client.ts")),
154
+ ).toBe(true);
155
+ expect(
156
+ existsSync(join(projectRoot, "src/mobile/transport/mobile-api-transport.client.ts")),
157
+ ).toBe(true);
158
+ expect(existsSync(join(projectRoot, "src/routes/index.tsx"))).toBe(true);
159
+
160
+ const readme = readFileSync(join(projectRoot, "README.md"), "utf8");
161
+ expect(readme).toContain("Aurora mobile companion starter template.");
162
+ });
163
+
164
+ it("supports monorepo template layout", () => {
165
+ const cwd = uniqueTempPath("cwd-monorepo");
166
+ mkdirSync(cwd, { recursive: true });
167
+
168
+ const result = runAuroraCli(["create", "mono-app", "--template", "monorepo"], { cwd });
169
+ const projectRoot = join(cwd, "mono-app");
170
+
171
+ expect(result.exitCode).toBe(0);
172
+ expect(result.stdout).toContain("template: monorepo");
173
+ expect(existsSync(join(projectRoot, "apps/web/src/routes/index.tsx"))).toBe(true);
174
+ expect(existsSync(join(projectRoot, "packages/ui/README.md"))).toBe(true);
175
+ expect(existsSync(join(projectRoot, "packages/shared/README.md"))).toBe(true);
176
+
177
+ const rootReadme = readFileSync(join(projectRoot, "README.md"), "utf8");
178
+ expect(rootReadme).toContain("Template: monorepo");
179
+ });
180
+
181
+ it("returns an error when project name is missing", () => {
182
+ const cwd = uniqueTempPath("cwd-missing-name");
183
+ mkdirSync(cwd, { recursive: true });
184
+
185
+ const result = runAuroraCli(["create"], { cwd });
186
+
187
+ expect(result.exitCode).toBe(2);
188
+ expect(result.stderr).toContain("aurora create: project name is required");
189
+ });
190
+
191
+ it("returns an error for invalid template values", () => {
192
+ const cwd = uniqueTempPath("cwd-invalid-template");
193
+ mkdirSync(cwd, { recursive: true });
194
+
195
+ const result = runAuroraCli(["create", "my-app", "--template", "unknown"], { cwd });
196
+
197
+ expect(result.exitCode).toBe(2);
198
+ expect(result.stderr).toContain("aurora create: invalid template 'unknown'");
199
+ });
200
+
201
+ it("returns an error when target directory already exists and is not empty", () => {
202
+ const cwd = uniqueTempPath("cwd-existing");
203
+ const projectRoot = join(cwd, "existing-app");
204
+ mkdirSync(projectRoot, { recursive: true });
205
+ writeFileSync(join(projectRoot, "seed.txt"), "seed\n", "utf8");
206
+
207
+ const result = runAuroraCli(["create", "existing-app"], { cwd });
208
+
209
+ expect(result.exitCode).toBe(2);
210
+ expect(result.stderr).toContain("target directory already exists and is not empty");
211
+ });
212
+
213
+ it("returns an error for unknown create options", () => {
214
+ const cwd = uniqueTempPath("cwd-unknown-option");
215
+ mkdirSync(cwd, { recursive: true });
216
+
217
+ const result = runAuroraCli(["create", "my-app", "--open"], { cwd });
218
+
219
+ expect(result.exitCode).toBe(2);
220
+ expect(result.stderr).toContain("aurora create: unknown option '--open'");
221
+ });
222
+ });
@@ -0,0 +1,52 @@
1
+ import { describe, expect, it } from "bun:test";
2
+
3
+ import { runAuroraCli } from "../src";
4
+
5
+ describe("aurora dev", () => {
6
+ const cwd = "/tmp/aurora-dev";
7
+
8
+ it("prints bootstrap output with default HTTP and HMR bridge settings", () => {
9
+ const result = runAuroraCli(["dev"], { cwd });
10
+
11
+ expect(result.exitCode).toBe(0);
12
+ expect(result.stdout).toContain("aurora dev bootstrap");
13
+ expect(result.stdout).toContain("url: http://localhost:3000");
14
+ expect(result.stdout).toContain("hmr_bridge: ws://localhost:3000/__aurora_hmr");
15
+ expect(result.stdout).toContain("open_browser: disabled");
16
+ expect(result.stdout).toContain("https: disabled");
17
+ });
18
+
19
+ it("supports --port, --open, and --https options", () => {
20
+ const result = runAuroraCli(["dev", "--port", "4310", "--open", "--https"], { cwd });
21
+
22
+ expect(result.exitCode).toBe(0);
23
+ expect(result.stdout).toContain("url: https://localhost:4310");
24
+ expect(result.stdout).toContain("hmr_bridge: wss://localhost:4310/__aurora_hmr");
25
+ expect(result.stdout).toContain("open_browser: enabled");
26
+ expect(result.stdout).toContain("https: enabled");
27
+ });
28
+
29
+ it("returns an error when --port is missing a value", () => {
30
+ const result = runAuroraCli(["dev", "--port"], { cwd });
31
+
32
+ expect(result.exitCode).toBe(2);
33
+ expect(result.stderr).toContain("aurora dev: --port requires a numeric value");
34
+ });
35
+
36
+ it("returns an error for out-of-range or invalid --port values", () => {
37
+ const nonNumeric = runAuroraCli(["dev", "--port", "abc"], { cwd });
38
+ const outOfRange = runAuroraCli(["dev", "--port", "70000"], { cwd });
39
+
40
+ expect(nonNumeric.exitCode).toBe(2);
41
+ expect(nonNumeric.stderr).toContain("aurora dev: --port must be an integer between 1 and 65535");
42
+ expect(outOfRange.exitCode).toBe(2);
43
+ expect(outOfRange.stderr).toContain("aurora dev: --port must be an integer between 1 and 65535");
44
+ });
45
+
46
+ it("returns an error for unknown options", () => {
47
+ const result = runAuroraCli(["dev", "--unknown"], { cwd });
48
+
49
+ expect(result.exitCode).toBe(2);
50
+ expect(result.stderr).toContain("aurora dev: unknown option '--unknown'");
51
+ });
52
+ });
@@ -0,0 +1,130 @@
1
+ import { join } from "node:path";
2
+
3
+ import { describe, expect, it } from "bun:test";
4
+
5
+ import { runAuroraCli } from "../src";
6
+
7
+ const projectRoot = join(import.meta.dir, "..");
8
+
9
+ function fixturePath(name: string): string {
10
+ return join("test/fixtures/devtools", name);
11
+ }
12
+
13
+ describe("aurora devtools", () => {
14
+ it("renders all v2 panels in text output for healthy input", () => {
15
+ const result = runAuroraCli(
16
+ ["devtools", "--input", fixturePath("healthy.v1.json")],
17
+ { cwd: projectRoot },
18
+ );
19
+
20
+ expect(result.exitCode).toBe(0);
21
+ expect(result.stdout).toContain("aurora devtools report");
22
+ expect(result.stdout).toContain("status: ok");
23
+ expect(result.stdout).toContain("[explain]");
24
+ expect(result.stdout).toContain("[waterfall]");
25
+ expect(result.stdout).toContain("[performance]");
26
+ expect(result.stdout).toContain("[realtime]");
27
+ expect(result.stdout).toContain("[auth]");
28
+ expect(result.stdout).toContain("query_auth: admin,user");
29
+ expect(result.stdout).toContain("auth_scoped_subscriptions: admin:1,user:1");
30
+ });
31
+
32
+ it("returns warn status and non-zero exit when panel issues are present", () => {
33
+ const result = runAuroraCli(
34
+ ["devtools", "--input", fixturePath("warn.v1.json")],
35
+ { cwd: projectRoot },
36
+ );
37
+
38
+ expect(result.exitCode).toBe(1);
39
+ expect(result.stdout).toContain("status: warn");
40
+ expect(result.stdout).toContain("[EXPLAIN_WARNINGS_PRESENT]");
41
+ expect(result.stdout).toContain("[BLOCKING_OPERATION_SLOW]");
42
+ expect(result.stdout).toContain("[ROUTE_SCORE_BELOW_MIN]");
43
+ expect(result.stdout).toContain("[CONNECTION_UNSTABLE]");
44
+ expect(result.stdout).toContain("[ROUTE_AUTH_SESSION_MISMATCH]");
45
+ });
46
+
47
+ it("supports json output, panel filtering, and custom thresholds", () => {
48
+ const result = runAuroraCli(
49
+ [
50
+ "devtools",
51
+ "--input",
52
+ fixturePath("healthy.v1.json"),
53
+ "--format",
54
+ "json",
55
+ "--panel",
56
+ "performance,realtime",
57
+ "--panel",
58
+ "auth",
59
+ "--min-route-score",
60
+ "95",
61
+ "--max-lag-p95-ms",
62
+ "100",
63
+ ],
64
+ { cwd: projectRoot },
65
+ );
66
+
67
+ expect(result.exitCode).toBe(1);
68
+ const parsed = JSON.parse(result.stdout ?? "{}") as {
69
+ mode?: string;
70
+ status?: string;
71
+ selectedPanels?: string[];
72
+ panels?: Array<{ name?: string; status?: string }>;
73
+ };
74
+
75
+ expect(parsed.mode).toBe("devtools");
76
+ expect(parsed.status).toBe("warn");
77
+ expect(parsed.selectedPanels).toEqual(["performance", "realtime", "auth"]);
78
+ expect(parsed.panels?.length).toBe(3);
79
+ expect(parsed.panels?.some((panel) => panel.name === "performance")).toBe(true);
80
+ expect(parsed.panels?.some((panel) => panel.name === "realtime" && panel.status === "warn")).toBe(
81
+ true,
82
+ );
83
+ expect(parsed.panels?.some((panel) => panel.name === "auth")).toBe(true);
84
+ expect(parsed.panels?.some((panel) => panel.name === "explain")).toBe(false);
85
+ });
86
+
87
+ it("rejects missing required options and unknown flags", () => {
88
+ const missingInput = runAuroraCli(["devtools"], { cwd: projectRoot });
89
+ const unknown = runAuroraCli(
90
+ ["devtools", "--input", fixturePath("healthy.v1.json"), "--unknown"],
91
+ { cwd: projectRoot },
92
+ );
93
+ const badPanel = runAuroraCli(
94
+ ["devtools", "--input", fixturePath("healthy.v1.json"), "--panel", "cache"],
95
+ { cwd: projectRoot },
96
+ );
97
+
98
+ expect(missingInput.exitCode).toBe(2);
99
+ expect(missingInput.stderr).toContain("aurora devtools: --input is required");
100
+ expect(unknown.exitCode).toBe(2);
101
+ expect(unknown.stderr).toContain("aurora devtools: unknown option '--unknown'");
102
+ expect(badPanel.exitCode).toBe(2);
103
+ expect(badPanel.stderr).toContain(
104
+ "aurora devtools: invalid panel 'cache'. Expected one of explain, waterfall, performance, realtime, auth",
105
+ );
106
+ });
107
+
108
+ it("fails for invalid json and unsupported schema versions", () => {
109
+ const invalidJsonPath = fixturePath("invalid.json");
110
+ const unsupportedVersionPath = fixturePath("unsupported-version.v2.json");
111
+
112
+ const invalidJson = runAuroraCli(
113
+ ["devtools", "--input", invalidJsonPath],
114
+ { cwd: projectRoot },
115
+ );
116
+ const unsupportedVersion = runAuroraCli(
117
+ ["devtools", "--input", unsupportedVersionPath],
118
+ { cwd: projectRoot },
119
+ );
120
+
121
+ expect(invalidJson.exitCode).toBe(1);
122
+ expect(invalidJson.stderr).toContain(
123
+ `aurora devtools: invalid json input at ${invalidJsonPath}`,
124
+ );
125
+ expect(unsupportedVersion.exitCode).toBe(1);
126
+ expect(unsupportedVersion.stderr).toContain(
127
+ "aurora devtools: unsupported schemaVersion 2; expected 1",
128
+ );
129
+ });
130
+ });
@@ -0,0 +1,129 @@
1
+ import { join } from "node:path";
2
+
3
+ import { describe, expect, it } from "bun:test";
4
+
5
+ import { runAuroraCli } from "../src";
6
+
7
+ describe("aurora doctor", () => {
8
+ const fixturesRoot = join(import.meta.dir, "fixtures/doctor");
9
+
10
+ it("returns a clean report when no issues are found", () => {
11
+ const result = runAuroraCli(["doctor", "--format", "json"], {
12
+ cwd: join(fixturesRoot, "clean"),
13
+ });
14
+
15
+ expect(result.exitCode).toBe(0);
16
+ const parsed = JSON.parse(result.stdout ?? "{}") as {
17
+ issues?: unknown[];
18
+ errors?: number;
19
+ warnings?: number;
20
+ byCategory?: Record<string, number>;
21
+ };
22
+
23
+ expect(parsed.errors).toBe(0);
24
+ expect(parsed.warnings).toBe(0);
25
+ expect(Array.isArray(parsed.issues)).toBe(true);
26
+ expect(parsed.issues?.length).toBe(0);
27
+ expect(parsed.byCategory?.boundary).toBe(0);
28
+ expect(parsed.byCategory?.security).toBe(0);
29
+ expect(parsed.byCategory?.performance).toBe(0);
30
+ expect(parsed.byCategory?.a11y).toBe(0);
31
+ expect(parsed.byCategory?.migration).toBe(0);
32
+ expect(parsed.byCategory?.governance).toBe(0);
33
+ });
34
+
35
+ it("reports boundary/security/perf/a11y/migration findings with actionable remediation", () => {
36
+ const result = runAuroraCli(["doctor", "--format", "json"], {
37
+ cwd: join(fixturesRoot, "findings"),
38
+ });
39
+
40
+ expect(result.exitCode).toBe(1);
41
+ const parsed = JSON.parse(result.stdout ?? "{}") as {
42
+ errors?: number;
43
+ warnings?: number;
44
+ byCategory?: Record<string, number>;
45
+ issues?: Array<{
46
+ code?: string;
47
+ remediation?: string;
48
+ ai?: { why?: string; howToFix?: string; docs?: string };
49
+ }>;
50
+ };
51
+
52
+ expect(parsed.errors).toBe(2);
53
+ expect(parsed.warnings).toBe(7);
54
+ expect(parsed.byCategory?.boundary).toBe(1);
55
+ expect(parsed.byCategory?.migration).toBe(2);
56
+ expect(parsed.byCategory?.security).toBe(2);
57
+ expect(parsed.byCategory?.performance).toBe(2);
58
+ expect(parsed.byCategory?.a11y).toBe(2);
59
+ expect(parsed.byCategory?.governance).toBe(0);
60
+
61
+ expect(parsed.issues?.some((issue) => issue.code === "BOUNDARY_CLIENT_IMPORTS_SERVER")).toBe(
62
+ true,
63
+ );
64
+ expect(parsed.issues?.some((issue) => issue.code === "DEPRECATED_ROUTE_API")).toBe(true);
65
+ expect(parsed.issues?.some((issue) => issue.code === "MIGRATION_MANUAL_ROUTES_ARRAY")).toBe(
66
+ true,
67
+ );
68
+ expect(parsed.issues?.some((issue) => issue.code === "SECURITY_DYNAMIC_CODE_EXECUTION")).toBe(
69
+ true,
70
+ );
71
+ expect(parsed.issues?.some((issue) => issue.code === "SECURITY_RAW_SQL_STRING")).toBe(true);
72
+ expect(parsed.issues?.some((issue) => issue.code === "PERF_UNBOUNDED_QUERY")).toBe(true);
73
+ expect(parsed.issues?.some((issue) => issue.code === "PERF_N_PLUS_ONE_LOOP_AWAIT")).toBe(
74
+ true,
75
+ );
76
+ expect(parsed.issues?.some((issue) => issue.code === "A11Y_IMAGE_MISSING_ALT")).toBe(true);
77
+ expect(parsed.issues?.some((issue) => issue.code === "A11Y_CLICKABLE_NON_INTERACTIVE")).toBe(
78
+ true,
79
+ );
80
+ expect(parsed.issues?.every((issue) => Boolean(issue.remediation))).toBe(true);
81
+ expect(parsed.issues?.every((issue) => Boolean(issue.ai?.why))).toBe(true);
82
+ expect(parsed.issues?.every((issue) => Boolean(issue.ai?.howToFix))).toBe(true);
83
+ expect(parsed.issues?.every((issue) => Boolean(issue.ai?.docs))).toBe(true);
84
+ });
85
+
86
+ it("renders text output and rejects unknown options", () => {
87
+ const textResult = runAuroraCli(["doctor"], {
88
+ cwd: join(fixturesRoot, "findings"),
89
+ });
90
+ const invalidOptionResult = runAuroraCli(["doctor", "--unknown"], {
91
+ cwd: join(fixturesRoot, "clean"),
92
+ });
93
+
94
+ expect(textResult.exitCode).toBe(1);
95
+ expect(textResult.stdout).toContain("aurora doctor report");
96
+ expect(textResult.stdout).toContain("categories:");
97
+ expect(textResult.stdout).toContain("BOUNDARY_CLIENT_IMPORTS_SERVER");
98
+ expect(textResult.stdout).toContain("DEPRECATED_ROUTE_API");
99
+ expect(textResult.stdout).toContain("SECURITY_DYNAMIC_CODE_EXECUTION");
100
+ expect(textResult.stdout).toContain("PERF_UNBOUNDED_QUERY");
101
+ expect(textResult.stdout).toContain("A11Y_IMAGE_MISSING_ALT");
102
+ expect(textResult.stdout).toContain("fix:");
103
+
104
+ expect(invalidOptionResult.exitCode).toBe(2);
105
+ expect(invalidOptionResult.stderr).toContain("aurora doctor: unknown option '--unknown'");
106
+ });
107
+
108
+ it("reports governance findings for missing/invalid OWNERS metadata", () => {
109
+ const result = runAuroraCli(["doctor", "--format", "json"], {
110
+ cwd: join(fixturesRoot, "governance"),
111
+ });
112
+
113
+ expect(result.exitCode).toBe(0);
114
+ const parsed = JSON.parse(result.stdout ?? "{}") as {
115
+ warnings?: number;
116
+ byCategory?: Record<string, number>;
117
+ issues?: Array<{ code?: string; file?: string }>;
118
+ };
119
+
120
+ expect(parsed.warnings).toBe(2);
121
+ expect(parsed.byCategory?.governance).toBe(2);
122
+ expect(parsed.issues?.some((issue) => issue.code === "GOVERNANCE_MISSING_MODULE_OWNER")).toBe(
123
+ true,
124
+ );
125
+ expect(parsed.issues?.some((issue) => issue.code === "GOVERNANCE_INVALID_OWNERS_EXPORT")).toBe(
126
+ true,
127
+ );
128
+ });
129
+ });