@usequark/quark-create-app 1.23.11

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 (237) hide show
  1. package/README.md +210 -0
  2. package/package.json +56 -0
  3. package/src/dependabot-config.test.js +128 -0
  4. package/src/deploy/adapters/iac.js +203 -0
  5. package/src/deploy/adapters/index.js +27 -0
  6. package/src/deploy/adapters/railway.js +508 -0
  7. package/src/deploy/adapters/railway.test.js +318 -0
  8. package/src/deploy/contract.js +59 -0
  9. package/src/deploy/deploy.integration.test.js +183 -0
  10. package/src/deploy/deploy.js +430 -0
  11. package/src/deploy/deploy.test.js +105 -0
  12. package/src/deploy/discovery.js +115 -0
  13. package/src/deploy/discovery.test.js +141 -0
  14. package/src/deploy/index.js +38 -0
  15. package/src/deploy/inspect.js +130 -0
  16. package/src/index.js +2920 -0
  17. package/src/mobile-template.test.js +232 -0
  18. package/src/prepare-hooks.test.js +535 -0
  19. package/src/scaffold-guards.test.js +140 -0
  20. package/src/scaffold-output.test.js +228 -0
  21. package/src/template-scripts.test.js +160 -0
  22. package/src/utils.js +77 -0
  23. package/src/utils.test.js +95 -0
  24. package/src/worker-dockerfile.test.js +85 -0
  25. package/templates/base-project/.cursor/rules/quark.mdc +172 -0
  26. package/templates/base-project/.dockerignore +14 -0
  27. package/templates/base-project/.env.railway.example +87 -0
  28. package/templates/base-project/.github/copilot-instructions.md +65 -0
  29. package/templates/base-project/.github/dependabot.yml +31 -0
  30. package/templates/base-project/.github/skills/project-context/SKILL.md +109 -0
  31. package/templates/base-project/.github/workflows/ci.yml +116 -0
  32. package/templates/base-project/.github/workflows/dependabot-auto-merge.yml +22 -0
  33. package/templates/base-project/.github/workflows/install-windows.yml +27 -0
  34. package/templates/base-project/.github/workflows/release.yml +67 -0
  35. package/templates/base-project/.railway/railway.ts +40 -0
  36. package/templates/base-project/CLAUDE.md +163 -0
  37. package/templates/base-project/MAIN.md +21 -0
  38. package/templates/base-project/README.md +164 -0
  39. package/templates/base-project/apps/web/.env.example +99 -0
  40. package/templates/base-project/apps/web/Dockerfile +38 -0
  41. package/templates/base-project/apps/web/biome.json +8 -0
  42. package/templates/base-project/apps/web/jsconfig.json +9 -0
  43. package/templates/base-project/apps/web/next.config.js +154 -0
  44. package/templates/base-project/apps/web/package.json +42 -0
  45. package/templates/base-project/apps/web/postcss.config.mjs +7 -0
  46. package/templates/base-project/apps/web/public/.well-known/security.txt +3 -0
  47. package/templates/base-project/apps/web/public/quark.svg +47 -0
  48. package/templates/base-project/apps/web/scripts/prepare-standalone.mjs +71 -0
  49. package/templates/base-project/apps/web/src/app/_components/AsciiPanel.js +103 -0
  50. package/templates/base-project/apps/web/src/app/_components/HealthIndicator.js +85 -0
  51. package/templates/base-project/apps/web/src/app/_components/HomeThemeToggle.js +63 -0
  52. package/templates/base-project/apps/web/src/app/_components/QuarkAnimation.js +168 -0
  53. package/templates/base-project/apps/web/src/app/_components/SyncedPanels.js +212 -0
  54. package/templates/base-project/apps/web/src/app/_components/UmamiReplayRecorder.js +198 -0
  55. package/templates/base-project/apps/web/src/app/_components/UmamiWebVitals.js +39 -0
  56. package/templates/base-project/apps/web/src/app/api/auth/[...nextauth]/route.js +7 -0
  57. package/templates/base-project/apps/web/src/app/api/auth/apple/route.js +82 -0
  58. package/templates/base-project/apps/web/src/app/api/auth/google/route.js +59 -0
  59. package/templates/base-project/apps/web/src/app/api/auth/refresh/route.js +48 -0
  60. package/templates/base-project/apps/web/src/app/api/auth/register/route.js +63 -0
  61. package/templates/base-project/apps/web/src/app/api/auth/token/route.js +44 -0
  62. package/templates/base-project/apps/web/src/app/api/csrf/route.js +40 -0
  63. package/templates/base-project/apps/web/src/app/api/device/register/route.js +57 -0
  64. package/templates/base-project/apps/web/src/app/api/error-handler.js +23 -0
  65. package/templates/base-project/apps/web/src/app/api/files/[id]/route.js +89 -0
  66. package/templates/base-project/apps/web/src/app/api/files/route.js +120 -0
  67. package/templates/base-project/apps/web/src/app/api/health/route.js +160 -0
  68. package/templates/base-project/apps/web/src/app/api/users/[id]/route.js +54 -0
  69. package/templates/base-project/apps/web/src/app/api/users/me/route.js +67 -0
  70. package/templates/base-project/apps/web/src/app/api/users/route.js +45 -0
  71. package/templates/base-project/apps/web/src/app/auth/error/page.js +83 -0
  72. package/templates/base-project/apps/web/src/app/auth/forgot-password/page.js +58 -0
  73. package/templates/base-project/apps/web/src/app/auth/provider-loading.js +25 -0
  74. package/templates/base-project/apps/web/src/app/auth/register/RegisterPageClient.js +340 -0
  75. package/templates/base-project/apps/web/src/app/auth/register/page.js +17 -0
  76. package/templates/base-project/apps/web/src/app/auth/signin/SignInPageClient.js +230 -0
  77. package/templates/base-project/apps/web/src/app/auth/signin/page.js +10 -0
  78. package/templates/base-project/apps/web/src/app/favicon.ico +0 -0
  79. package/templates/base-project/apps/web/src/app/global-error.js +58 -0
  80. package/templates/base-project/apps/web/src/app/globals.css +1166 -0
  81. package/templates/base-project/apps/web/src/app/icon.svg +47 -0
  82. package/templates/base-project/apps/web/src/app/layout.js +57 -0
  83. package/templates/base-project/apps/web/src/app/manifest.js +12 -0
  84. package/templates/base-project/apps/web/src/app/not-found.js +39 -0
  85. package/templates/base-project/apps/web/src/app/page.js +262 -0
  86. package/templates/base-project/apps/web/src/app/robots.js +23 -0
  87. package/templates/base-project/apps/web/src/app/sitemap.js +14 -0
  88. package/templates/base-project/apps/web/src/lib/analytics/umami-before-send.js +29 -0
  89. package/templates/base-project/apps/web/src/lib/analytics/umami-config.js +95 -0
  90. package/templates/base-project/apps/web/src/lib/analytics/umami-csp.js +24 -0
  91. package/templates/base-project/apps/web/src/lib/analytics/umami-marketing.js +131 -0
  92. package/templates/base-project/apps/web/src/lib/analytics/umami-replay.js +204 -0
  93. package/templates/base-project/apps/web/src/lib/analytics/umami-web-vitals.js +237 -0
  94. package/templates/base-project/apps/web/src/lib/analytics/umami.js +57 -0
  95. package/templates/base-project/apps/web/src/lib/auth-middleware.js +82 -0
  96. package/templates/base-project/apps/web/src/lib/auth-signup.js +5 -0
  97. package/templates/base-project/apps/web/src/lib/auth.js +191 -0
  98. package/templates/base-project/apps/web/src/lib/csrf-cookie.js +11 -0
  99. package/templates/base-project/apps/web/src/lib/jwt.js +84 -0
  100. package/templates/base-project/apps/web/src/lib/load-crm-config.js +35 -0
  101. package/templates/base-project/apps/web/src/lib/normalize-auth-request.js +33 -0
  102. package/templates/base-project/apps/web/src/lib/proxy-auth.js +80 -0
  103. package/templates/base-project/apps/web/src/lib/seo/indexing.js +25 -0
  104. package/templates/base-project/apps/web/src/lib/seo/site-metadata.js +74 -0
  105. package/templates/base-project/apps/web/src/lib/sitemap-entries.js +22 -0
  106. package/templates/base-project/apps/web/src/preflight.js +55 -0
  107. package/templates/base-project/apps/web/src/proxy.js +328 -0
  108. package/templates/base-project/biome.json +52 -0
  109. package/templates/base-project/docker-compose.override.yml +17 -0
  110. package/templates/base-project/docker-compose.yml +37 -0
  111. package/templates/base-project/docs/FIRST_FEATURE.md +74 -0
  112. package/templates/base-project/docs/START_HERE.md +70 -0
  113. package/templates/base-project/docs/adr/README.md +42 -0
  114. package/templates/base-project/package.json +58 -0
  115. package/templates/base-project/packages/db/package.json +38 -0
  116. package/templates/base-project/packages/db/prisma/migrations/20260202061128_initial/migration.sql +202 -0
  117. package/templates/base-project/packages/db/prisma/migrations/migration_lock.toml +3 -0
  118. package/templates/base-project/packages/db/prisma/schema.prisma +161 -0
  119. package/templates/base-project/packages/db/prisma/seed.js +152 -0
  120. package/templates/base-project/packages/db/prisma.config.js +27 -0
  121. package/templates/base-project/packages/db/src/client.js +75 -0
  122. package/templates/base-project/packages/db/src/connection.js +44 -0
  123. package/templates/base-project/packages/db/src/context.js +275 -0
  124. package/templates/base-project/packages/db/src/index.js +5 -0
  125. package/templates/base-project/packages/db/src/ping.js +66 -0
  126. package/templates/base-project/packages/db/src/queries.js +292 -0
  127. package/templates/base-project/packages/db/src/schemas.js +30 -0
  128. package/templates/base-project/packages/db/src/umami.js +299 -0
  129. package/templates/base-project/pnpm-workspace.yaml +20 -0
  130. package/templates/base-project/scripts/check-loading.mjs +49 -0
  131. package/templates/base-project/scripts/check-standards.mjs +186 -0
  132. package/templates/base-project/scripts/clean-workspace.mjs +479 -0
  133. package/templates/base-project/scripts/dev-preflight.mjs +48 -0
  134. package/templates/base-project/scripts/doctor.js +408 -0
  135. package/templates/base-project/scripts/prepare.js +490 -0
  136. package/templates/base-project/scripts/run-tests.mjs +71 -0
  137. package/templates/base-project/skills/add-dashboard/SKILL.md +28 -0
  138. package/templates/base-project/skills/add-endpoint/SKILL.md +29 -0
  139. package/templates/base-project/skills/add-model/SKILL.md +28 -0
  140. package/templates/base-project/skills/ecommerce/SKILL.md +224 -0
  141. package/templates/base-project/skills/ecommerce-cart/SKILL.md +193 -0
  142. package/templates/base-project/skills/ecommerce-catalog/SKILL.md +179 -0
  143. package/templates/base-project/skills/ecommerce-checkout/SKILL.md +195 -0
  144. package/templates/base-project/skills/i18n/SKILL.md +211 -0
  145. package/templates/base-project/skills/payment/SKILL.md +280 -0
  146. package/templates/base-project/skills/quark-skills/SKILL.md +37 -0
  147. package/templates/base-project/turbo.json +143 -0
  148. package/templates/config/package.json +20 -0
  149. package/templates/config/src/app-url.js +134 -0
  150. package/templates/config/src/environment.js +284 -0
  151. package/templates/config/src/index.js +16 -0
  152. package/templates/config/src/load-config.js +157 -0
  153. package/templates/config/src/rate-limit.js +136 -0
  154. package/templates/config/src/redis.js +43 -0
  155. package/templates/config/src/validate-env.js +532 -0
  156. package/templates/jobs/README.md +52 -0
  157. package/templates/jobs/package.json +9 -0
  158. package/templates/jobs/src/definitions.js +13 -0
  159. package/templates/jobs/src/index.js +1 -0
  160. package/templates/mobile/.env.example +16 -0
  161. package/templates/mobile/CHECKLIST.md +40 -0
  162. package/templates/mobile/README.md +130 -0
  163. package/templates/mobile/app/(app)/_layout.tsx +83 -0
  164. package/templates/mobile/app/(app)/index.tsx +147 -0
  165. package/templates/mobile/app/(app)/profile.tsx +204 -0
  166. package/templates/mobile/app/(app)/settings.tsx +80 -0
  167. package/templates/mobile/app/(auth)/_layout.tsx +10 -0
  168. package/templates/mobile/app/(auth)/sign-in.tsx +140 -0
  169. package/templates/mobile/app/(auth)/sign-up.tsx +178 -0
  170. package/templates/mobile/app/+not-found.tsx +32 -0
  171. package/templates/mobile/app/_layout.tsx +18 -0
  172. package/templates/mobile/app.json +47 -0
  173. package/templates/mobile/assets/adaptive-icon.png +0 -0
  174. package/templates/mobile/assets/icon.png +0 -0
  175. package/templates/mobile/assets/notification-icon.png +0 -0
  176. package/templates/mobile/assets/splash.png +0 -0
  177. package/templates/mobile/biome.json +27 -0
  178. package/templates/mobile/components/AuthGate.tsx +19 -0
  179. package/templates/mobile/components/ThemeProvider.tsx +23 -0
  180. package/templates/mobile/eas.json +29 -0
  181. package/templates/mobile/hooks/use-auth.ts +127 -0
  182. package/templates/mobile/lib/api-client.ts +138 -0
  183. package/templates/mobile/lib/api.ts +83 -0
  184. package/templates/mobile/lib/auth-providers-google.ts +74 -0
  185. package/templates/mobile/lib/auth-providers.ts +74 -0
  186. package/templates/mobile/lib/auth.ts +100 -0
  187. package/templates/mobile/lib/config.ts +14 -0
  188. package/templates/mobile/lib/errors.ts +46 -0
  189. package/templates/mobile/lib/notifications.ts +77 -0
  190. package/templates/mobile/lib/storage.ts +25 -0
  191. package/templates/mobile/lib/theme-tokens.ts +16 -0
  192. package/templates/mobile/package.json +37 -0
  193. package/templates/mobile/tsconfig.json +10 -0
  194. package/templates/pwa/app/_components/PWARegister.js +15 -0
  195. package/templates/pwa/app/manifest.js +32 -0
  196. package/templates/pwa/public/sw.js +72 -0
  197. package/templates/ui/README.md +44 -0
  198. package/templates/ui/package.json +21 -0
  199. package/templates/ui/src/badge.js +23 -0
  200. package/templates/ui/src/button.js +111 -0
  201. package/templates/ui/src/card.js +128 -0
  202. package/templates/ui/src/checkbox.js +20 -0
  203. package/templates/ui/src/container.js +13 -0
  204. package/templates/ui/src/dialog.js +137 -0
  205. package/templates/ui/src/error-banner.js +15 -0
  206. package/templates/ui/src/footer.js +239 -0
  207. package/templates/ui/src/form-field.integration-test.js +142 -0
  208. package/templates/ui/src/form-field.js +50 -0
  209. package/templates/ui/src/index.js +24 -0
  210. package/templates/ui/src/input.js +11 -0
  211. package/templates/ui/src/label.js +14 -0
  212. package/templates/ui/src/lightbox.integration-test.js +167 -0
  213. package/templates/ui/src/lightbox.js +243 -0
  214. package/templates/ui/src/logo.js +52 -0
  215. package/templates/ui/src/navbar.js +602 -0
  216. package/templates/ui/src/password-input.js +31 -0
  217. package/templates/ui/src/rich-text.js +514 -0
  218. package/templates/ui/src/select.js +317 -0
  219. package/templates/ui/src/skeleton.js +10 -0
  220. package/templates/ui/src/spinner.js +31 -0
  221. package/templates/ui/src/table.js +169 -0
  222. package/templates/ui/src/textarea.js +11 -0
  223. package/templates/ui/src/theme-constants.js +24 -0
  224. package/templates/ui/src/theme.js +196 -0
  225. package/templates/ui/src/toast.js +206 -0
  226. package/templates/worker/Dockerfile +64 -0
  227. package/templates/worker/README.md +56 -0
  228. package/templates/worker/package.json +36 -0
  229. package/templates/worker/src/config/pricing.js +46 -0
  230. package/templates/worker/src/handlers/email-job-validation.js +36 -0
  231. package/templates/worker/src/handlers/email.js +98 -0
  232. package/templates/worker/src/handlers/files.js +59 -0
  233. package/templates/worker/src/handlers/index.js +20 -0
  234. package/templates/worker/src/handlers/push-notification.js +234 -0
  235. package/templates/worker/src/index.js +505 -0
  236. package/templates/worker/src/lib/railway.js +438 -0
  237. package/templates/worker/src/lib/tokens.js +81 -0
@@ -0,0 +1,141 @@
1
+ import assert from "node:assert/strict";
2
+ import fs from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { after, test } from "node:test";
6
+ import {
7
+ discoverQuarkDeployProject,
8
+ QUARK_DEPLOY_DIAGNOSTIC_CODES,
9
+ resolveQuarkDeployProject,
10
+ } from "./discovery.js";
11
+
12
+ const temporaryDirectories = [];
13
+
14
+ after(async () => {
15
+ await Promise.all(
16
+ temporaryDirectories.map((directory) =>
17
+ fs.rm(directory, { recursive: true, force: true }),
18
+ ),
19
+ );
20
+ });
21
+
22
+ async function writeJson(filePath, value) {
23
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
24
+ await fs.writeFile(filePath, JSON.stringify(value, null, "\t"));
25
+ }
26
+
27
+ async function createFixture({ hasWeb = true, hasWorker = false } = {}) {
28
+ const projectDir = await fs.mkdtemp(path.join(os.tmpdir(), "quark-deploy-"));
29
+ temporaryDirectories.push(projectDir);
30
+
31
+ await writeJson(path.join(projectDir, "package.json"), {
32
+ name: "fixture-quark-project",
33
+ private: true,
34
+ });
35
+
36
+ if (hasWeb) {
37
+ await writeJson(path.join(projectDir, "apps", "web", "package.json"), {
38
+ name: "@fixture/quark-web",
39
+ private: true,
40
+ type: "module",
41
+ });
42
+ }
43
+
44
+ if (hasWorker) {
45
+ await writeJson(path.join(projectDir, "apps", "worker", "package.json"), {
46
+ name: "@fixture/quark-worker",
47
+ private: true,
48
+ type: "module",
49
+ });
50
+ }
51
+
52
+ return projectDir;
53
+ }
54
+
55
+ test("resolveQuarkDeployProject discovers web and worker services in a Quark monorepo", async () => {
56
+ const projectDir = await createFixture({ hasWeb: true, hasWorker: true });
57
+ const discovery = await resolveQuarkDeployProject(projectDir);
58
+
59
+ assert.equal(discovery.kind, "quark");
60
+ assert.deepEqual(
61
+ discovery.services.map((service) => service.kind),
62
+ ["web", "worker"],
63
+ );
64
+
65
+ const [webService, workerService] = discovery.services;
66
+
67
+ assert.equal(webService.required, true);
68
+ assert.equal(webService.packageName, "@fixture/quark-web");
69
+ assert.equal(webService.relativeRootDir, "apps/web");
70
+ assert.equal(
71
+ webService.runtime.relativeEntrypoint,
72
+ "apps/web/.next/standalone/apps/web/server.js",
73
+ );
74
+ assert.equal(webService.runtime.healthcheckPath, "/api/health");
75
+ assert.equal(
76
+ webService.runtime.entrypointPath,
77
+ path.join(
78
+ projectDir,
79
+ "apps",
80
+ "web",
81
+ ".next",
82
+ "standalone",
83
+ "apps",
84
+ "web",
85
+ "server.js",
86
+ ),
87
+ );
88
+
89
+ assert.equal(workerService.required, false);
90
+ assert.equal(workerService.packageName, "@fixture/quark-worker");
91
+ assert.equal(workerService.relativeRootDir, "apps/worker");
92
+ assert.equal(
93
+ workerService.runtime.relativeEntrypoint,
94
+ "apps/worker/src/index.js",
95
+ );
96
+ });
97
+
98
+ test("resolveQuarkDeployProject accepts a Quark monorepo without a worker service", async () => {
99
+ const projectDir = await createFixture({ hasWeb: true, hasWorker: false });
100
+ const discovery = await resolveQuarkDeployProject(projectDir);
101
+
102
+ assert.equal(discovery.diagnostics.length, 0);
103
+ assert.deepEqual(
104
+ discovery.services.map((service) => service.kind),
105
+ ["web"],
106
+ );
107
+ assert.equal(discovery.services[0].required, true);
108
+ });
109
+
110
+ test("discoverQuarkDeployProject reports diagnostics and resolveQuarkDeployProject rejects when web is missing", async () => {
111
+ const projectDir = await createFixture({ hasWeb: false, hasWorker: true });
112
+ const discovery = await discoverQuarkDeployProject(projectDir);
113
+
114
+ assert.deepEqual(
115
+ discovery.services.map((service) => service.kind),
116
+ ["worker"],
117
+ );
118
+ assert.deepEqual(discovery.diagnostics, [
119
+ {
120
+ code: QUARK_DEPLOY_DIAGNOSTIC_CODES.MISSING_REQUIRED_SERVICE,
121
+ serviceKind: "web",
122
+ required: true,
123
+ expectedPath: "apps/web/package.json",
124
+ message: "Missing required Quark web service at apps/web/package.json.",
125
+ },
126
+ ]);
127
+
128
+ await assert.rejects(
129
+ () => resolveQuarkDeployProject(projectDir),
130
+ (error) => {
131
+ assert.equal(error.code, "QUARK_DEPLOY_DISCOVERY_FAILED");
132
+ assert.equal(error.diagnostics.length, 1);
133
+ assert.match(error.message, /apps\/web\/package\.json/);
134
+ assert.deepEqual(
135
+ error.discovery.services.map((service) => service.kind),
136
+ ["worker"],
137
+ );
138
+ return true;
139
+ },
140
+ );
141
+ });
@@ -0,0 +1,38 @@
1
+ export {
2
+ applyIacConfig,
3
+ checkRailwayCLI,
4
+ checkRailwayLogin,
5
+ deleteService,
6
+ deployService,
7
+ ensurePlugin,
8
+ ensureRailwayProject,
9
+ ensureService,
10
+ escapeTsString,
11
+ generateIacFile,
12
+ getDeploymentStatus,
13
+ getExistingVariable,
14
+ getServiceUrl,
15
+ installRailwaySdk,
16
+ isProjectLinked,
17
+ listProjects,
18
+ RAILWAY,
19
+ RAILWAY_DIAGNOSTIC_CODES,
20
+ RailwayError,
21
+ removeServiceDomain,
22
+ setProjectVariables,
23
+ tryLinkProject,
24
+ } from "./adapters/index.js";
25
+ export {
26
+ QUARK_DEPLOY_PROJECT_KIND,
27
+ QUARK_SERVICE_CONTRACTS,
28
+ QUARK_SERVICE_KINDS,
29
+ SUPPORTED_QUARK_SERVICE_KINDS,
30
+ } from "./contract.js";
31
+ export { deployToRailway } from "./deploy.js";
32
+ export {
33
+ discoverQuarkDeployProject,
34
+ formatQuarkDeployDiagnostics,
35
+ QUARK_DEPLOY_DIAGNOSTIC_CODES,
36
+ resolveQuarkDeployProject,
37
+ } from "./discovery.js";
38
+ export { inspectProject } from "./inspect.js";
@@ -0,0 +1,130 @@
1
+ import path from "node:path";
2
+ import chalk from "chalk";
3
+ import { execa } from "execa";
4
+ import fs from "fs-extra";
5
+ import {
6
+ checkRailwayCLI,
7
+ checkRailwayLogin,
8
+ getProjectDomains,
9
+ isProjectLinked,
10
+ RAILWAY,
11
+ } from "./adapters/index.js";
12
+ import { discoverQuarkDeployProject } from "./discovery.js";
13
+
14
+ function formatBool(value) {
15
+ return value ? chalk.green("āœ” yes") : chalk.red("āœ– no");
16
+ }
17
+
18
+ export async function inspectProject(options = {}) {
19
+ const { cwd = process.cwd(), output = console.log } = options;
20
+
21
+ output(chalk.blue.bold("\nšŸ” Quark Deploy Inspection\n"));
22
+
23
+ // --- Service discovery ---
24
+ output(chalk.cyan(" Project Services\n"));
25
+
26
+ const discovery = await discoverQuarkDeployProject(cwd);
27
+
28
+ if (discovery.services.length === 0) {
29
+ output(chalk.yellow(" No Quark services discovered."));
30
+ } else {
31
+ for (const service of discovery.services) {
32
+ output(` ${chalk.bold(service.name)}`);
33
+ output(` Package: ${service.packageName ?? chalk.dim("unknown")}`);
34
+ output(` Root: ${service.relativeRootDir}`);
35
+ output(` Entrypoint: ${service.runtime.relativeEntrypoint}`);
36
+ if (service.runtime.healthcheckPath) {
37
+ output(` Health: ${service.runtime.healthcheckPath}`);
38
+ }
39
+ output(
40
+ ` Required: ${service.required ? chalk.green("yes") : chalk.dim("no")}`,
41
+ );
42
+ output();
43
+ }
44
+ }
45
+
46
+ // --- Diagnostics ---
47
+ if (discovery.diagnostics.length > 0) {
48
+ output(chalk.yellow(" Diagnostics\n"));
49
+ for (const diagnostic of discovery.diagnostics) {
50
+ output(` ${chalk.yellow("⚠")} ${diagnostic.message}`);
51
+ }
52
+ output();
53
+ }
54
+
55
+ // --- Railway readiness ---
56
+ output(chalk.cyan(" Railway Readiness\n"));
57
+
58
+ const railCLIVersion = await checkRailwayCLI();
59
+ output(` CLI installed: ${formatBool(!!railCLIVersion)}`);
60
+ if (railCLIVersion) {
61
+ output(` Version: ${railCLIVersion}`);
62
+ }
63
+
64
+ const railUser = await checkRailwayLogin();
65
+ output(` Logged in: ${formatBool(!!railUser)}`);
66
+ if (railUser) {
67
+ output(` User: ${railUser}`);
68
+ }
69
+
70
+ const linked = await isProjectLinked(cwd);
71
+ output(` Project linked: ${formatBool(linked)}`);
72
+
73
+ if (linked) {
74
+ const domains = await getProjectDomains({ cwd });
75
+ if (domains.length > 0) {
76
+ output(chalk.green("\n Deployed Services\n"));
77
+ for (const domain of domains) {
78
+ output(
79
+ ` ${domain.service ?? chalk.dim("unknown")}: ${domain.domain ?? chalk.dim("no domain")}`,
80
+ );
81
+ }
82
+ }
83
+ }
84
+
85
+ // --- Service Status ---
86
+ if (linked) {
87
+ output(chalk.cyan(" Service Status\n"));
88
+
89
+ try {
90
+ const configPath = path.join(cwd, ".railway", "config.json");
91
+ const config = fs.readJsonSync(configPath);
92
+ const projectId = config.project;
93
+
94
+ const { stdout } = await execa(RAILWAY, ["service", "list", "--json"], {
95
+ cwd,
96
+ timeout: 15_000,
97
+ });
98
+ const services = JSON.parse(stdout);
99
+
100
+ for (const service of services) {
101
+ const shortId = service.id.slice(0, 8);
102
+ const status = service.status ?? "unknown";
103
+ let coloredStatus;
104
+ if (status === "deployed") {
105
+ coloredStatus = chalk.green(status);
106
+ } else if (status === "deploying") {
107
+ coloredStatus = chalk.yellow(status);
108
+ } else if (
109
+ status === "crashing" ||
110
+ status === "crashed" ||
111
+ status === "failed"
112
+ ) {
113
+ coloredStatus = chalk.red(status);
114
+ } else {
115
+ coloredStatus = chalk.dim(status);
116
+ }
117
+ const url = `https://railway.com/project/${projectId}/service/${service.id}`;
118
+ output(
119
+ ` ${chalk.bold(service.name)} : ${coloredStatus} (${shortId}) → ${chalk.dim(url)}`,
120
+ );
121
+ }
122
+ } catch (err) {
123
+ output(
124
+ ` ${chalk.red("āœ–")} ${chalk.dim(`Failed to fetch service status: ${err.message}`)}`,
125
+ );
126
+ }
127
+ }
128
+
129
+ output();
130
+ }