@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
package/src/create.ts ADDED
@@ -0,0 +1,425 @@
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+
4
+ import { type CommandContext, type CommandResult } from "./registry";
5
+
6
+ type TemplateId =
7
+ | "basic"
8
+ | "saas"
9
+ | "blog"
10
+ | "dashboard"
11
+ | "monorepo"
12
+ | "desktop"
13
+ | "mobile";
14
+
15
+ interface CreateOptions {
16
+ projectName: string;
17
+ template: TemplateId;
18
+ initializeGit: boolean;
19
+ }
20
+
21
+ interface TemplateScaffold {
22
+ id: TemplateId;
23
+ summary: string;
24
+ targets: ReadonlyArray<"node" | "edge" | "static">;
25
+ files: ReadonlyArray<{ path: string; content: string }>;
26
+ }
27
+
28
+ interface CreateBootstrapReport {
29
+ mode: "create";
30
+ cwd: string;
31
+ projectName: string;
32
+ projectRoot: string;
33
+ template: TemplateId;
34
+ templateSummary: string;
35
+ targets: ReadonlyArray<"node" | "edge" | "static">;
36
+ initializeGit: boolean;
37
+ createdFileCount: number;
38
+ }
39
+
40
+ const TEMPLATE_CATALOG: Record<TemplateId, Omit<TemplateScaffold, "files">> = {
41
+ basic: {
42
+ id: "basic",
43
+ summary: "Basic web app starter with query/action examples.",
44
+ targets: ["node", "edge"],
45
+ },
46
+ saas: {
47
+ id: "saas",
48
+ summary: "SaaS starter with auth, billing seam, and team management shape.",
49
+ targets: ["node"],
50
+ },
51
+ blog: {
52
+ id: "blog",
53
+ summary: "Content/blog starter optimized for static generation and SEO.",
54
+ targets: ["static", "node"],
55
+ },
56
+ dashboard: {
57
+ id: "dashboard",
58
+ summary: "Dashboard starter with data-grid/realtime oriented structure.",
59
+ targets: ["node"],
60
+ },
61
+ monorepo: {
62
+ id: "monorepo",
63
+ summary: "Workspace starter with app + shared packages layout.",
64
+ targets: ["node"],
65
+ },
66
+ desktop: {
67
+ id: "desktop",
68
+ summary: "Desktop starter with Tauri integration seams.",
69
+ targets: ["node"],
70
+ },
71
+ mobile: {
72
+ id: "mobile",
73
+ summary: "Mobile companion starter with typed client SDK usage.",
74
+ targets: ["node"],
75
+ },
76
+ };
77
+
78
+ const TEMPLATE_IDS = Object.keys(TEMPLATE_CATALOG) as ReadonlyArray<TemplateId>;
79
+ const APP_NAME_PLACEHOLDER = "__AURORA_APP_NAME__";
80
+
81
+ function parseTemplate(rawValue: string | undefined): TemplateId | CommandResult {
82
+ if (!rawValue) {
83
+ return {
84
+ exitCode: 2,
85
+ stderr: `aurora create: --template requires one of ${TEMPLATE_IDS.map((item) => `'${item}'`).join(", ")}`,
86
+ };
87
+ }
88
+
89
+ if (TEMPLATE_IDS.includes(rawValue as TemplateId)) {
90
+ return rawValue as TemplateId;
91
+ }
92
+
93
+ return {
94
+ exitCode: 2,
95
+ stderr: `aurora create: invalid template '${rawValue}'. Expected ${TEMPLATE_IDS.join(", ")}`,
96
+ };
97
+ }
98
+
99
+ function parseCreateOptions(args: ReadonlyArray<string>): CreateOptions | CommandResult {
100
+ if (args.length === 0) {
101
+ return {
102
+ exitCode: 2,
103
+ stderr: "aurora create: project name is required",
104
+ };
105
+ }
106
+
107
+ const firstArg = args[0];
108
+ if (firstArg.startsWith("-")) {
109
+ return {
110
+ exitCode: 2,
111
+ stderr: "aurora create: project name must be provided before options",
112
+ };
113
+ }
114
+
115
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9-_]*$/.test(firstArg)) {
116
+ return {
117
+ exitCode: 2,
118
+ stderr:
119
+ "aurora create: project name must match ^[a-zA-Z0-9][a-zA-Z0-9-_]*$",
120
+ };
121
+ }
122
+
123
+ const options: CreateOptions = {
124
+ projectName: firstArg,
125
+ template: "basic",
126
+ initializeGit: false,
127
+ };
128
+
129
+ for (let i = 1; i < args.length; i += 1) {
130
+ const arg = args[i];
131
+
132
+ if (arg === "--template") {
133
+ const parsedTemplate = parseTemplate(args[i + 1]);
134
+ if (typeof parsedTemplate !== "string") {
135
+ return parsedTemplate;
136
+ }
137
+
138
+ options.template = parsedTemplate;
139
+ i += 1;
140
+ continue;
141
+ }
142
+
143
+ if (arg === "--git") {
144
+ options.initializeGit = true;
145
+ continue;
146
+ }
147
+
148
+ return {
149
+ exitCode: 2,
150
+ stderr: `aurora create: unknown option '${arg}'`,
151
+ };
152
+ }
153
+
154
+ return options;
155
+ }
156
+
157
+ function isNonEmptyDirectory(path: string): boolean {
158
+ if (!existsSync(path)) {
159
+ return false;
160
+ }
161
+
162
+ try {
163
+ return readdirSync(path).length > 0;
164
+ } catch {
165
+ return false;
166
+ }
167
+ }
168
+
169
+ function loadTemplateFilesFromDisk(
170
+ template: TemplateId,
171
+ projectName: string,
172
+ ): ReadonlyArray<{ path: string; content: string }> {
173
+ const templateRoot = resolve(import.meta.dir, "../templates", template);
174
+ if (!existsSync(templateRoot)) {
175
+ return [];
176
+ }
177
+
178
+ return collectTemplateFiles(templateRoot, templateRoot, projectName);
179
+ }
180
+
181
+ function collectTemplateFiles(
182
+ templateRoot: string,
183
+ currentDirectory: string,
184
+ projectName: string,
185
+ ): ReadonlyArray<{ path: string; content: string }> {
186
+ const collected: Array<{ path: string; content: string }> = [];
187
+ const entries = readdirSync(currentDirectory, { withFileTypes: true }).sort((left, right) =>
188
+ left.name.localeCompare(right.name),
189
+ );
190
+
191
+ for (const entry of entries) {
192
+ const absolutePath = join(currentDirectory, entry.name);
193
+ if (entry.isDirectory()) {
194
+ collected.push(...collectTemplateFiles(templateRoot, absolutePath, projectName));
195
+ continue;
196
+ }
197
+
198
+ if (!entry.isFile()) {
199
+ continue;
200
+ }
201
+
202
+ const relativePath = absolutePath
203
+ .slice(templateRoot.length + 1)
204
+ .replaceAll("\\", "/");
205
+ const source = readFileSync(absolutePath, "utf8");
206
+ collected.push({
207
+ path: relativePath,
208
+ content: source.split(APP_NAME_PLACEHOLDER).join(projectName),
209
+ });
210
+ }
211
+
212
+ return collected;
213
+ }
214
+
215
+ function buildScaffoldFiles(projectName: string, template: TemplateId): TemplateScaffold {
216
+ const templateMeta = TEMPLATE_CATALOG[template];
217
+ const files: Array<{ path: string; content: string }> = [];
218
+ const diskTemplateFiles = loadTemplateFilesFromDisk(template, projectName);
219
+
220
+ if (diskTemplateFiles.length > 0) {
221
+ files.push(...diskTemplateFiles);
222
+ }
223
+
224
+ if (template === "monorepo" && diskTemplateFiles.length === 0) {
225
+ files.push({
226
+ path: "package.json",
227
+ content: JSON.stringify(
228
+ {
229
+ name: projectName,
230
+ private: true,
231
+ workspaces: ["apps/*", "packages/*"],
232
+ scripts: {
233
+ dev: "bun run --cwd apps/web aurora dev",
234
+ build: "bun run --cwd apps/web aurora build",
235
+ start: "bun run --cwd apps/web aurora start",
236
+ },
237
+ },
238
+ null,
239
+ 2,
240
+ ),
241
+ });
242
+ files.push({
243
+ path: "apps/web/package.json",
244
+ content: JSON.stringify(
245
+ {
246
+ name: `${projectName}-web`,
247
+ private: true,
248
+ scripts: {
249
+ dev: "aurora dev",
250
+ build: "aurora build",
251
+ start: "aurora start",
252
+ },
253
+ },
254
+ null,
255
+ 2,
256
+ ),
257
+ });
258
+ files.push({
259
+ path: "apps/web/src/routes/index.tsx",
260
+ content:
261
+ "export const HomePage = () => <main>Welcome to Aurora monorepo starter.</main>;\n",
262
+ });
263
+ files.push({
264
+ path: "packages/ui/README.md",
265
+ content: "# UI Package\n\nShared UI components for Aurora apps.\n",
266
+ });
267
+ files.push({
268
+ path: "packages/shared/README.md",
269
+ content: "# Shared Package\n\nShared types and utilities.\n",
270
+ });
271
+ } else if (diskTemplateFiles.length === 0) {
272
+ files.push({
273
+ path: "package.json",
274
+ content: JSON.stringify(
275
+ {
276
+ name: projectName,
277
+ private: true,
278
+ scripts: {
279
+ dev: "aurora dev",
280
+ build: "aurora build",
281
+ start: "aurora start",
282
+ test: "aurora test",
283
+ },
284
+ },
285
+ null,
286
+ 2,
287
+ ),
288
+ });
289
+ files.push({
290
+ path: "src/routes/index.tsx",
291
+ content: `export const HomePage = () => <main>${templateMeta.summary}</main>;\n`,
292
+ });
293
+ files.push({
294
+ path: "src/actions/health.action.server.ts",
295
+ content:
296
+ "export const health = async () => ({ ok: true, source: \"aurora-template\" });\n",
297
+ });
298
+ }
299
+
300
+ files.push({
301
+ path: "aurora.template.json",
302
+ content: JSON.stringify(
303
+ {
304
+ template: templateMeta.id,
305
+ summary: templateMeta.summary,
306
+ targets: templateMeta.targets,
307
+ },
308
+ null,
309
+ 2,
310
+ ),
311
+ });
312
+
313
+ if (!files.some((file) => file.path === "README.md")) {
314
+ files.push({
315
+ path: "README.md",
316
+ content: [
317
+ `# ${projectName}`,
318
+ "",
319
+ `Template: ${templateMeta.id}`,
320
+ "",
321
+ templateMeta.summary,
322
+ "",
323
+ "## Commands",
324
+ "",
325
+ "- `aurora dev`",
326
+ "- `aurora build`",
327
+ "- `aurora start`",
328
+ ].join("\n"),
329
+ });
330
+ }
331
+
332
+ return {
333
+ ...templateMeta,
334
+ files,
335
+ };
336
+ }
337
+
338
+ function ensureParentDirectory(filePath: string): void {
339
+ mkdirSync(dirname(filePath), { recursive: true });
340
+ }
341
+
342
+ function scaffoldProject(
343
+ options: CreateOptions,
344
+ context: CommandContext,
345
+ ): CreateBootstrapReport | CommandResult {
346
+ const projectRoot = resolve(context.cwd, options.projectName);
347
+
348
+ if (isNonEmptyDirectory(projectRoot)) {
349
+ return {
350
+ exitCode: 2,
351
+ stderr: `aurora create: target directory already exists and is not empty: ${projectRoot}`,
352
+ };
353
+ }
354
+
355
+ try {
356
+ mkdirSync(projectRoot, { recursive: true });
357
+
358
+ const scaffold = buildScaffoldFiles(options.projectName, options.template);
359
+ for (const file of scaffold.files) {
360
+ const absolutePath = join(projectRoot, file.path);
361
+ ensureParentDirectory(absolutePath);
362
+ writeFileSync(absolutePath, `${file.content}\n`, "utf8");
363
+ }
364
+
365
+ if (options.initializeGit) {
366
+ writeFileSync(join(projectRoot, ".gitignore"), "node_modules\n.aurora\n", "utf8");
367
+ }
368
+
369
+ return {
370
+ mode: "create",
371
+ cwd: context.cwd,
372
+ projectName: options.projectName,
373
+ projectRoot,
374
+ template: scaffold.id,
375
+ templateSummary: scaffold.summary,
376
+ targets: scaffold.targets,
377
+ initializeGit: options.initializeGit,
378
+ createdFileCount: scaffold.files.length + (options.initializeGit ? 1 : 0),
379
+ };
380
+ } catch (error) {
381
+ const message = error instanceof Error ? error.message : String(error);
382
+ return {
383
+ exitCode: 1,
384
+ stderr: `aurora create: failed to scaffold project at ${projectRoot}: ${message}`,
385
+ };
386
+ }
387
+ }
388
+
389
+ function renderCreateBootstrapReport(report: CreateBootstrapReport): string {
390
+ return [
391
+ "aurora create bootstrap",
392
+ `cwd: ${report.cwd}`,
393
+ `project: ${report.projectName}`,
394
+ `project_root: ${report.projectRoot}`,
395
+ `template: ${report.template}`,
396
+ `template_summary: ${report.templateSummary}`,
397
+ `targets: ${report.targets.join(", ")}`,
398
+ `git: ${report.initializeGit ? "enabled" : "disabled"}`,
399
+ `created_files: ${report.createdFileCount}`,
400
+ "next_steps:",
401
+ ` cd ${report.projectName}`,
402
+ " bun install",
403
+ " aurora dev",
404
+ ].join("\n");
405
+ }
406
+
407
+ export function runCreateCommand(
408
+ args: ReadonlyArray<string>,
409
+ context: CommandContext,
410
+ ): CommandResult {
411
+ const options = parseCreateOptions(args);
412
+ if ("exitCode" in options) {
413
+ return options;
414
+ }
415
+
416
+ const report = scaffoldProject(options, context);
417
+ if ("exitCode" in report) {
418
+ return report;
419
+ }
420
+
421
+ return {
422
+ exitCode: 0,
423
+ stdout: renderCreateBootstrapReport(report),
424
+ };
425
+ }
package/src/dev.ts ADDED
@@ -0,0 +1,126 @@
1
+ import { resolve } from "node:path";
2
+
3
+ import { type CommandContext, type CommandResult } from "./registry";
4
+
5
+ interface DevOptions {
6
+ port: number;
7
+ openBrowser: boolean;
8
+ https: boolean;
9
+ }
10
+
11
+ interface DevBootstrapReport {
12
+ mode: "dev";
13
+ cwd: string;
14
+ url: string;
15
+ hmrBridgeUrl: string;
16
+ compilerManifestPath: string;
17
+ openBrowser: boolean;
18
+ https: boolean;
19
+ }
20
+
21
+ function parsePort(rawValue: string | undefined): number | CommandResult {
22
+ if (!rawValue) {
23
+ return {
24
+ exitCode: 2,
25
+ stderr: "aurora dev: --port requires a numeric value",
26
+ };
27
+ }
28
+
29
+ const value = Number(rawValue);
30
+ if (!Number.isInteger(value) || value < 1 || value > 65535) {
31
+ return {
32
+ exitCode: 2,
33
+ stderr: "aurora dev: --port must be an integer between 1 and 65535",
34
+ };
35
+ }
36
+
37
+ return value;
38
+ }
39
+
40
+ function parseDevOptions(args: ReadonlyArray<string>): DevOptions | CommandResult {
41
+ const options: DevOptions = {
42
+ port: 3000,
43
+ openBrowser: false,
44
+ https: false,
45
+ };
46
+
47
+ for (let i = 0; i < args.length; i += 1) {
48
+ const arg = args[i];
49
+
50
+ if (arg === "--port") {
51
+ const parsedPort = parsePort(args[i + 1]);
52
+ if (typeof parsedPort !== "number") {
53
+ return parsedPort;
54
+ }
55
+
56
+ options.port = parsedPort;
57
+ i += 1;
58
+ continue;
59
+ }
60
+
61
+ if (arg === "--open") {
62
+ options.openBrowser = true;
63
+ continue;
64
+ }
65
+
66
+ if (arg === "--https") {
67
+ options.https = true;
68
+ continue;
69
+ }
70
+
71
+ return {
72
+ exitCode: 2,
73
+ stderr: `aurora dev: unknown option '${arg}'`,
74
+ };
75
+ }
76
+
77
+ return options;
78
+ }
79
+
80
+ function buildDevBootstrapReport(
81
+ options: DevOptions,
82
+ context: CommandContext,
83
+ ): DevBootstrapReport {
84
+ const protocol = options.https ? "https" : "http";
85
+ const wsProtocol = options.https ? "wss" : "ws";
86
+ const host = "localhost";
87
+ const baseUrl = `${protocol}://${host}:${options.port}`;
88
+
89
+ return {
90
+ mode: "dev",
91
+ cwd: context.cwd,
92
+ url: baseUrl,
93
+ hmrBridgeUrl: `${wsProtocol}://${host}:${options.port}/__aurora_hmr`,
94
+ compilerManifestPath: resolve(context.cwd, ".aurora/compiler/manifest.json"),
95
+ openBrowser: options.openBrowser,
96
+ https: options.https,
97
+ };
98
+ }
99
+
100
+ function renderDevBootstrapReport(report: DevBootstrapReport): string {
101
+ return [
102
+ "aurora dev bootstrap",
103
+ `cwd: ${report.cwd}`,
104
+ `url: ${report.url}`,
105
+ `hmr_bridge: ${report.hmrBridgeUrl}`,
106
+ `compiler_manifest: ${report.compilerManifestPath}`,
107
+ `open_browser: ${report.openBrowser ? "enabled" : "disabled"}`,
108
+ `https: ${report.https ? "enabled" : "disabled"}`,
109
+ ].join("\n");
110
+ }
111
+
112
+ export function runDevCommand(
113
+ args: ReadonlyArray<string>,
114
+ context: CommandContext,
115
+ ): CommandResult {
116
+ const options = parseDevOptions(args);
117
+ if ("exitCode" in options) {
118
+ return options;
119
+ }
120
+
121
+ const report = buildDevBootstrapReport(options, context);
122
+ return {
123
+ exitCode: 0,
124
+ stdout: renderDevBootstrapReport(report),
125
+ };
126
+ }