@topogram/cli 0.3.34

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 (257) hide show
  1. package/ARCHITECTURE.md +67 -0
  2. package/CHANGELOG.md +240 -0
  3. package/README.md +223 -0
  4. package/package.json +51 -0
  5. package/src/adoption/index.js +3 -0
  6. package/src/adoption/plan.js +702 -0
  7. package/src/adoption/reporting.js +464 -0
  8. package/src/adoption/review-groups.js +313 -0
  9. package/src/agent-ops/query-builders.js +5012 -0
  10. package/src/archive/archive.js +141 -0
  11. package/src/archive/compact.js +26 -0
  12. package/src/archive/jsonl.js +70 -0
  13. package/src/archive/resolver-bridge.js +82 -0
  14. package/src/archive/schema.js +87 -0
  15. package/src/archive/unarchive.js +108 -0
  16. package/src/catalog.js +752 -0
  17. package/src/cli/catalog-alias.js +166 -0
  18. package/src/cli.js +9738 -0
  19. package/src/component-behavior.js +173 -0
  20. package/src/example-implementation.js +91 -0
  21. package/src/format.js +19 -0
  22. package/src/generator/adapters.d.ts +4 -0
  23. package/src/generator/adapters.js +325 -0
  24. package/src/generator/api.d.ts +1 -0
  25. package/src/generator/api.js +1196 -0
  26. package/src/generator/check.js +355 -0
  27. package/src/generator/component-conformance.js +767 -0
  28. package/src/generator/components.js +39 -0
  29. package/src/generator/context/bundle.js +291 -0
  30. package/src/generator/context/diff.js +256 -0
  31. package/src/generator/context/digest.js +182 -0
  32. package/src/generator/context/domain-coverage.js +94 -0
  33. package/src/generator/context/domain-page.js +137 -0
  34. package/src/generator/context/index.js +42 -0
  35. package/src/generator/context/report.js +121 -0
  36. package/src/generator/context/shared.js +1397 -0
  37. package/src/generator/context/slice.js +703 -0
  38. package/src/generator/context/task-mode.js +466 -0
  39. package/src/generator/docs.js +327 -0
  40. package/src/generator/index.js +161 -0
  41. package/src/generator/native/parity-bundle.js +311 -0
  42. package/src/generator/output.js +300 -0
  43. package/src/generator/registry.js +482 -0
  44. package/src/generator/runtime/app-bundle.js +456 -0
  45. package/src/generator/runtime/bundle-shared.js +166 -0
  46. package/src/generator/runtime/compile-check.js +163 -0
  47. package/src/generator/runtime/deployment.js +287 -0
  48. package/src/generator/runtime/environment.js +635 -0
  49. package/src/generator/runtime/index.js +32 -0
  50. package/src/generator/runtime/runtime-check.js +554 -0
  51. package/src/generator/runtime/shared.js +515 -0
  52. package/src/generator/runtime/smoke.js +219 -0
  53. package/src/generator/schema.js +204 -0
  54. package/src/generator/sdlc/board.js +66 -0
  55. package/src/generator/sdlc/doc-page.js +53 -0
  56. package/src/generator/sdlc/index.js +23 -0
  57. package/src/generator/sdlc/release-notes.js +62 -0
  58. package/src/generator/sdlc/traceability-matrix.js +65 -0
  59. package/src/generator/shared.js +29 -0
  60. package/src/generator/surfaces/contracts.js +146 -0
  61. package/src/generator/surfaces/databases/contract.js +40 -0
  62. package/src/generator/surfaces/databases/index.js +84 -0
  63. package/src/generator/surfaces/databases/lifecycle-shared.d.ts +1 -0
  64. package/src/generator/surfaces/databases/lifecycle-shared.js +612 -0
  65. package/src/generator/surfaces/databases/migration-plan.js +281 -0
  66. package/src/generator/surfaces/databases/postgres/capabilities.js +14 -0
  67. package/src/generator/surfaces/databases/postgres/drizzle.js +99 -0
  68. package/src/generator/surfaces/databases/postgres/index.js +9 -0
  69. package/src/generator/surfaces/databases/postgres/lifecycle.js +16 -0
  70. package/src/generator/surfaces/databases/postgres/prisma.js +159 -0
  71. package/src/generator/surfaces/databases/postgres/sql-migration.js +102 -0
  72. package/src/generator/surfaces/databases/postgres/sql-schema.js +34 -0
  73. package/src/generator/surfaces/databases/shared.d.ts +1 -0
  74. package/src/generator/surfaces/databases/shared.js +350 -0
  75. package/src/generator/surfaces/databases/snapshot.js +96 -0
  76. package/src/generator/surfaces/databases/sqlite/capabilities.js +14 -0
  77. package/src/generator/surfaces/databases/sqlite/index.js +8 -0
  78. package/src/generator/surfaces/databases/sqlite/lifecycle.js +16 -0
  79. package/src/generator/surfaces/databases/sqlite/prisma.js +143 -0
  80. package/src/generator/surfaces/databases/sqlite/sql-migration.js +65 -0
  81. package/src/generator/surfaces/databases/sqlite/sql-schema.js +27 -0
  82. package/src/generator/surfaces/index.js +25 -0
  83. package/src/generator/surfaces/native/swiftui-app.js +38 -0
  84. package/src/generator/surfaces/native/swiftui-templates/Package.swift.txt +20 -0
  85. package/src/generator/surfaces/native/swiftui-templates/README.generated.md +26 -0
  86. package/src/generator/surfaces/native/swiftui-templates/runtime/DynamicScreens.swift +682 -0
  87. package/src/generator/surfaces/native/swiftui-templates/runtime/TodoAPIClient.swift +156 -0
  88. package/src/generator/surfaces/native/swiftui-templates/runtime/TodoSwiftUIApp.swift +44 -0
  89. package/src/generator/surfaces/native/swiftui-templates/runtime/Visibility.swift +183 -0
  90. package/src/generator/surfaces/services/express.d.ts +1 -0
  91. package/src/generator/surfaces/services/express.js +766 -0
  92. package/src/generator/surfaces/services/hono.d.ts +1 -0
  93. package/src/generator/surfaces/services/hono.js +204 -0
  94. package/src/generator/surfaces/services/index.js +42 -0
  95. package/src/generator/surfaces/services/persistence-wiring.js +240 -0
  96. package/src/generator/surfaces/services/runtime-helpers.js +631 -0
  97. package/src/generator/surfaces/services/server-contract.js +80 -0
  98. package/src/generator/surfaces/services/stateless.d.ts +1 -0
  99. package/src/generator/surfaces/services/stateless.js +97 -0
  100. package/src/generator/surfaces/shared.js +64 -0
  101. package/src/generator/surfaces/web/api-client.js +1 -0
  102. package/src/generator/surfaces/web/forms.js +1 -0
  103. package/src/generator/surfaces/web/index.d.ts +2 -0
  104. package/src/generator/surfaces/web/index.js +53 -0
  105. package/src/generator/surfaces/web/react-components.js +248 -0
  106. package/src/generator/surfaces/web/react.js +538 -0
  107. package/src/generator/surfaces/web/routes.js +1 -0
  108. package/src/generator/surfaces/web/screens.js +1 -0
  109. package/src/generator/surfaces/web/shared.js +369 -0
  110. package/src/generator/surfaces/web/sveltekit-actions.js +28 -0
  111. package/src/generator/surfaces/web/sveltekit-components.js +234 -0
  112. package/src/generator/surfaces/web/sveltekit.js +426 -0
  113. package/src/generator/surfaces/web/ui-web-contract.js +65 -0
  114. package/src/generator/surfaces/web/vanilla.js +239 -0
  115. package/src/generator/verification.js +84 -0
  116. package/src/generator.js +1 -0
  117. package/src/import/core/context.js +52 -0
  118. package/src/import/core/contracts.js +23 -0
  119. package/src/import/core/registry.js +81 -0
  120. package/src/import/core/runner.js +646 -0
  121. package/src/import/core/shared.js +910 -0
  122. package/src/import/enrichers/auth-session.js +18 -0
  123. package/src/import/enrichers/django-rest.js +226 -0
  124. package/src/import/enrichers/doc-linking.js +20 -0
  125. package/src/import/enrichers/rails-controllers.js +246 -0
  126. package/src/import/enrichers/rails-models.js +130 -0
  127. package/src/import/enrichers/workflow-target-state.js +10 -0
  128. package/src/import/extractors/api/aspnet-core.js +304 -0
  129. package/src/import/extractors/api/django-routes.js +318 -0
  130. package/src/import/extractors/api/express.js +154 -0
  131. package/src/import/extractors/api/fastify.js +371 -0
  132. package/src/import/extractors/api/flutter-dio.js +135 -0
  133. package/src/import/extractors/api/generic-route-fallback.js +90 -0
  134. package/src/import/extractors/api/graphql-code-first.js +565 -0
  135. package/src/import/extractors/api/graphql-sdl.js +309 -0
  136. package/src/import/extractors/api/jaxrs.js +303 -0
  137. package/src/import/extractors/api/micronaut.js +213 -0
  138. package/src/import/extractors/api/next-route.js +50 -0
  139. package/src/import/extractors/api/next-server-action.js +51 -0
  140. package/src/import/extractors/api/nextauth.js +52 -0
  141. package/src/import/extractors/api/openapi-code.js +242 -0
  142. package/src/import/extractors/api/openapi.js +232 -0
  143. package/src/import/extractors/api/rails-routes.js +230 -0
  144. package/src/import/extractors/api/react-native-repository.js +128 -0
  145. package/src/import/extractors/api/retrofit.js +103 -0
  146. package/src/import/extractors/api/spring-web.js +372 -0
  147. package/src/import/extractors/api/swift-webapi.js +116 -0
  148. package/src/import/extractors/api/trpc.js +212 -0
  149. package/src/import/extractors/db/django-models.js +232 -0
  150. package/src/import/extractors/db/dotnet-models.js +93 -0
  151. package/src/import/extractors/db/drizzle.js +242 -0
  152. package/src/import/extractors/db/ef-core.js +221 -0
  153. package/src/import/extractors/db/flutter-entities.js +120 -0
  154. package/src/import/extractors/db/jpa.js +120 -0
  155. package/src/import/extractors/db/liquibase.js +180 -0
  156. package/src/import/extractors/db/mybatis-xml.js +145 -0
  157. package/src/import/extractors/db/prisma.js +185 -0
  158. package/src/import/extractors/db/rails-schema.js +175 -0
  159. package/src/import/extractors/db/react-native-entities.js +95 -0
  160. package/src/import/extractors/db/room.js +193 -0
  161. package/src/import/extractors/db/snapshot.js +112 -0
  162. package/src/import/extractors/db/sql.js +180 -0
  163. package/src/import/extractors/db/swiftdata.js +137 -0
  164. package/src/import/extractors/ui/android-compose.js +230 -0
  165. package/src/import/extractors/ui/backend-only.js +70 -0
  166. package/src/import/extractors/ui/blazor.js +227 -0
  167. package/src/import/extractors/ui/flutter-screens.js +152 -0
  168. package/src/import/extractors/ui/maui-xaml.js +135 -0
  169. package/src/import/extractors/ui/next-app-router.js +83 -0
  170. package/src/import/extractors/ui/next-pages-router.js +141 -0
  171. package/src/import/extractors/ui/razor-pages.js +181 -0
  172. package/src/import/extractors/ui/react-native-screens.js +166 -0
  173. package/src/import/extractors/ui/react-router.js +139 -0
  174. package/src/import/extractors/ui/sveltekit.js +123 -0
  175. package/src/import/extractors/ui/swiftui.js +193 -0
  176. package/src/import/extractors/ui/uikit.js +175 -0
  177. package/src/import/extractors/verification/generic.js +290 -0
  178. package/src/import/extractors/workflows/generic.js +137 -0
  179. package/src/import/index.js +7 -0
  180. package/src/import/provenance.js +158 -0
  181. package/src/new-project.js +2107 -0
  182. package/src/parser.js +439 -0
  183. package/src/policy/review-boundaries.js +165 -0
  184. package/src/project-config.js +535 -0
  185. package/src/proofs/backend-parity.js +19 -0
  186. package/src/proofs/contract-audit.js +220 -0
  187. package/src/proofs/ios-parity.js +7 -0
  188. package/src/proofs/issues-parity.js +10 -0
  189. package/src/proofs/web-parity.js +50 -0
  190. package/src/realization/api/build-api-realization.js +5 -0
  191. package/src/realization/api/index.js +1 -0
  192. package/src/realization/backend/build-backend-runtime-realization.js +82 -0
  193. package/src/realization/backend/index.d.ts +1 -0
  194. package/src/realization/backend/index.js +4 -0
  195. package/src/realization/db/build-db-realization.js +17 -0
  196. package/src/realization/db/index.js +3 -0
  197. package/src/realization/db/migration-plan.js +5 -0
  198. package/src/realization/db/snapshot.js +5 -0
  199. package/src/realization/ui/build-ui-shared-realization.js +305 -0
  200. package/src/realization/ui/build-web-realization.js +189 -0
  201. package/src/realization/ui/index.js +2 -0
  202. package/src/reconcile/docs.js +280 -0
  203. package/src/reconcile/index.js +3 -0
  204. package/src/reconcile/journeys.js +441 -0
  205. package/src/resolver/docs.js +1 -0
  206. package/src/resolver/enrich/acceptance-criterion.js +14 -0
  207. package/src/resolver/enrich/bug.js +12 -0
  208. package/src/resolver/enrich/component.js +2 -0
  209. package/src/resolver/enrich/index.js +1 -0
  210. package/src/resolver/enrich/pitch.js +18 -0
  211. package/src/resolver/enrich/requirement.js +20 -0
  212. package/src/resolver/enrich/task.js +16 -0
  213. package/src/resolver/expressions.js +1 -0
  214. package/src/resolver/index.js +2422 -0
  215. package/src/resolver/normalize.js +1 -0
  216. package/src/resolver.js +1 -0
  217. package/src/sdlc/adopt.js +65 -0
  218. package/src/sdlc/check.js +86 -0
  219. package/src/sdlc/dod/acceptance-criterion.js +22 -0
  220. package/src/sdlc/dod/bug.js +26 -0
  221. package/src/sdlc/dod/document.js +23 -0
  222. package/src/sdlc/dod/index.js +25 -0
  223. package/src/sdlc/dod/pitch.js +23 -0
  224. package/src/sdlc/dod/requirement.js +34 -0
  225. package/src/sdlc/dod/task.js +39 -0
  226. package/src/sdlc/explain.js +116 -0
  227. package/src/sdlc/history.js +80 -0
  228. package/src/sdlc/paths.js +11 -0
  229. package/src/sdlc/release.js +106 -0
  230. package/src/sdlc/scaffold.js +89 -0
  231. package/src/sdlc/status-filter.js +54 -0
  232. package/src/sdlc/transition.js +112 -0
  233. package/src/sdlc/transitions/acceptance-criterion.js +28 -0
  234. package/src/sdlc/transitions/bug.js +31 -0
  235. package/src/sdlc/transitions/document.js +29 -0
  236. package/src/sdlc/transitions/index.js +56 -0
  237. package/src/sdlc/transitions/pitch.js +34 -0
  238. package/src/sdlc/transitions/requirement.js +31 -0
  239. package/src/sdlc/transitions/task.js +34 -0
  240. package/src/template-trust.js +597 -0
  241. package/src/validator/expressions.js +1 -0
  242. package/src/validator/index.js +3424 -0
  243. package/src/validator/kinds.js +346 -0
  244. package/src/validator/per-kind/acceptance-criterion.js +91 -0
  245. package/src/validator/per-kind/bug.js +77 -0
  246. package/src/validator/per-kind/component.js +274 -0
  247. package/src/validator/per-kind/domain.js +205 -0
  248. package/src/validator/per-kind/pitch.js +101 -0
  249. package/src/validator/per-kind/requirement.js +75 -0
  250. package/src/validator/per-kind/task.js +96 -0
  251. package/src/validator/registry.js +1 -0
  252. package/src/validator/utils.js +12 -0
  253. package/src/validator.js +1 -0
  254. package/src/workflows.js +7597 -0
  255. package/src/workspace-docs.js +265 -0
  256. package/template-helpers/react.js +5 -0
  257. package/template-helpers/sveltekit.js +5 -0
@@ -0,0 +1,163 @@
1
+ import {
2
+ generateServerBundle,
3
+ generateWebBundle,
4
+ getDefaultEnvironmentProjections,
5
+ resolveRuntimeTopology,
6
+ runtimeUrls
7
+ } from "./shared.js";
8
+ import { getExampleImplementation } from "../../example-implementation.js";
9
+ import { mergeBundleFiles } from "./bundle-shared.js";
10
+
11
+ function compileCheckName(graph, options = {}) {
12
+ try {
13
+ return getExampleImplementation(graph, options).runtime.reference.compileCheck.name;
14
+ } catch {
15
+ return "Topogram Compile Check";
16
+ }
17
+ }
18
+
19
+ function runtimeReferenceFor(graph, options = {}) {
20
+ try {
21
+ return getExampleImplementation(graph, options).runtime.reference;
22
+ } catch {
23
+ return { environment: { databaseName: "topogram_app", envExample: "" }, demoEnv: { userId: "" }, ports: { server: 3000, web: 5173 } };
24
+ }
25
+ }
26
+
27
+ function buildCompileCheckPlan(graph, options = {}) {
28
+ const topology = resolveRuntimeTopology(graph, options);
29
+ const { apiProjection, uiProjection, dbProjection } = getDefaultEnvironmentProjections(graph, options);
30
+ const apiChecks = topology.apiComponents.map((component, index) => ({
31
+ id: index === 0 ? "server_typecheck" : `server_typecheck_${component.id}`,
32
+ cwd: topology.serviceDir(component),
33
+ install: "npm install --no-audit --no-fund",
34
+ command: "npm run check"
35
+ }));
36
+ const webChecks = topology.webComponents.flatMap((component, index) => [
37
+ {
38
+ id: index === 0 ? "web_typecheck" : `web_typecheck_${component.id}`,
39
+ cwd: topology.webDir(component),
40
+ install: "npm install --no-audit --no-fund",
41
+ command: "npm run check"
42
+ },
43
+ {
44
+ id: index === 0 ? "web_build" : `web_build_${component.id}`,
45
+ cwd: topology.webDir(component),
46
+ install: "npm install --no-audit --no-fund",
47
+ command: "npm run build"
48
+ }
49
+ ]);
50
+ return {
51
+ type: "compile_check_plan",
52
+ name: compileCheckName(graph, options),
53
+ projections: {
54
+ api: apiProjection?.id || null,
55
+ ui: uiProjection?.id || null,
56
+ db: dbProjection?.id || null
57
+ },
58
+ topology: {
59
+ components: topology.components.map((component) => ({
60
+ id: component.id,
61
+ type: component.type,
62
+ projection: component.projection.id,
63
+ generator: component.generator
64
+ }))
65
+ },
66
+ checks: [...apiChecks, ...webChecks]
67
+ };
68
+ }
69
+
70
+ function renderCompileCheckEnvExample(graph, options = {}) {
71
+ const runtimeReference = runtimeReferenceFor(graph, options);
72
+ const topology = resolveRuntimeTopology(graph, options);
73
+ const { dbProjection } = getDefaultEnvironmentProjections(graph, options);
74
+ const urls = runtimeUrls(runtimeReference, topology);
75
+ if (dbProjection?.platform === "db_sqlite") {
76
+ return `DATABASE_URL=./var/${runtimeReference.environment.databaseName || "topogram_app"}.sqlite
77
+ PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}
78
+ PUBLIC_TOPOGRAM_DEMO_USER_ID=${runtimeReference.demoEnv.userId}
79
+ ${runtimeReference.environment.envExample || ""}
80
+ `;
81
+ }
82
+ return `DATABASE_URL=postgresql://postgres:postgres@localhost:5432/${runtimeReference.environment.databaseName || "topogram_app"}?schema=public
83
+ PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}
84
+ PUBLIC_TOPOGRAM_DEMO_USER_ID=${runtimeReference.demoEnv.userId}
85
+ ${runtimeReference.environment.envExample || ""}
86
+ `;
87
+ }
88
+
89
+ function renderCompileCheckReadme(graph, options = {}) {
90
+ return `# ${compileCheckName(graph, options).replace("Plan", "Bundle")}
91
+
92
+ This bundle verifies that the generated server and web projects typecheck and build.
93
+
94
+ ## Checks
95
+
96
+ - server TypeScript check
97
+ - web TypeScript check
98
+ - web production build
99
+
100
+ ## Usage
101
+
102
+ 1. Copy \`.env.example\` to \`.env\` if needed
103
+ 2. Run \`bash ./scripts/check.sh\`
104
+ `;
105
+ }
106
+
107
+ function renderCompileCheckScript(plan) {
108
+ const lines = [
109
+ "#!/usr/bin/env bash",
110
+ "set -euo pipefail",
111
+ "",
112
+ 'SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"',
113
+ 'ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"',
114
+ 'ENV_FILE="${TOPOGRAM_ENV_FILE:-$ROOT_DIR/.env}"',
115
+ "",
116
+ 'if [[ -f "$ENV_FILE" ]]; then',
117
+ " set -a",
118
+ ' . "$ENV_FILE"',
119
+ " set +a",
120
+ "fi",
121
+ ""
122
+ ];
123
+ if (plan.checks.length === 0) {
124
+ lines.push('echo "No API or web components are configured; compile check is a no-op."');
125
+ }
126
+ for (const check of plan.checks) {
127
+ const label = check.id.includes("web")
128
+ ? check.id.includes("build") ? "Building generated web" : "Checking generated web"
129
+ : "Checking generated server";
130
+ lines.push(`echo "${label} (${check.cwd})..."`);
131
+ lines.push(`echo "Installing dependencies (${check.cwd})..."`);
132
+ lines.push(`(cd "$ROOT_DIR/${check.cwd}" && ${check.install})`);
133
+ lines.push(`echo "Running ${check.command} (${check.cwd})..."`);
134
+ lines.push(`(cd "$ROOT_DIR/${check.cwd}" && ${check.command})`);
135
+ lines.push("");
136
+ }
137
+ lines.push('echo "Compile checks passed."');
138
+ return `${lines.join("\n")}\n`;
139
+ }
140
+
141
+ export function generateCompileCheckBundle(graph, options = {}) {
142
+ const plan = buildCompileCheckPlan(graph, options);
143
+ const topology = resolveRuntimeTopology(graph, options);
144
+ const files = {
145
+ ".env.example": renderCompileCheckEnvExample(graph, options),
146
+ "README.md": renderCompileCheckReadme(graph, options),
147
+ "compile-check-plan.json": `${JSON.stringify(plan, null, 2)}\n`,
148
+ "scripts/check.sh": renderCompileCheckScript(plan)
149
+ };
150
+ for (const component of topology.apiComponents) {
151
+ const serverBundle = generateServerBundle(graph, component.projection.id, { ...options, component });
152
+ mergeBundleFiles(files, topology.serviceDir(component), serverBundle);
153
+ }
154
+ for (const component of topology.webComponents) {
155
+ const webBundle = generateWebBundle(graph, component.projection.id, { ...options, component });
156
+ mergeBundleFiles(files, topology.webDir(component), webBundle);
157
+ }
158
+ return files;
159
+ }
160
+
161
+ export function generateCompileCheckPlan(graph, options = {}) {
162
+ return buildCompileCheckPlan(graph, options);
163
+ }
@@ -0,0 +1,287 @@
1
+ import {
2
+ generateDbBundle,
3
+ generateServerBundle,
4
+ generateWebBundle,
5
+ getDefaultEnvironmentProjections,
6
+ resolveRuntimeTopology
7
+ } from "./shared.js";
8
+ import { getExampleImplementation } from "../../example-implementation.js";
9
+ import { mergeNamedBundles, renderRootEnvFileShellScript, renderRootShellScript } from "./bundle-shared.js";
10
+ import { generatorProfile as manifestGeneratorProfile } from "../registry.js";
11
+
12
+ function projectionHintProfile(projection, fallback) {
13
+ for (const entry of projection.generatorDefaults || []) {
14
+ if (entry.key === "profile" && entry.value != null) {
15
+ return entry.value;
16
+ }
17
+ }
18
+ return fallback;
19
+ }
20
+
21
+ function slugifyAppName(name) {
22
+ return String(name || "topogram-app")
23
+ .toLowerCase()
24
+ .replace(/[^a-z0-9]+/g, "-")
25
+ .replace(/^-+|-+$/g, "")
26
+ .slice(0, 60) || "topogram-app";
27
+ }
28
+
29
+ function buildDeploymentPlan(graph, options = {}) {
30
+ const runtimeReference = getExampleImplementation(graph, options).runtime.reference;
31
+ const topology = resolveRuntimeTopology(graph, options);
32
+ const { apiProjection, uiProjection, dbProjection } = getDefaultEnvironmentProjections(graph, options);
33
+ const profile = options.profileId || "fly_io";
34
+ const supportedProfiles = ["fly_io", "railway"];
35
+ const webProfile = manifestGeneratorProfile(topology.primaryWeb?.generator?.id, null) || projectionHintProfile(uiProjection, "sveltekit");
36
+ const databaseTarget = dbProjection.platform === "db_sqlite"
37
+ ? "sqlite_file"
38
+ : profile === "fly_io"
39
+ ? "managed_postgres"
40
+ : "railway_postgres";
41
+ if (!supportedProfiles.includes(profile)) {
42
+ throw new Error(`Unsupported deployment profile '${profile}'`);
43
+ }
44
+
45
+ return {
46
+ type: "deployment_plan",
47
+ deployment: {
48
+ name: runtimeReference.appBundle.name.replace("App Bundle", "Deployment Stack"),
49
+ profile
50
+ },
51
+ projections: {
52
+ api: apiProjection.id,
53
+ ui: uiProjection.id,
54
+ db: dbProjection.id
55
+ },
56
+ topology: {
57
+ components: topology.components.map((component) => ({
58
+ id: component.id,
59
+ type: component.type,
60
+ projection: component.projection.id,
61
+ generator: component.generator,
62
+ port: component.port ?? null,
63
+ api: component.api || null,
64
+ database: component.database || null
65
+ }))
66
+ },
67
+ directories: {
68
+ server: topology.serviceDir(topology.primaryApi),
69
+ web: topology.webDir(topology.primaryWeb),
70
+ db: topology.dbDir(topology.primaryDb)
71
+ },
72
+ runtime: {
73
+ server: manifestGeneratorProfile(topology.primaryApi?.generator?.id, "hono"),
74
+ web: webProfile,
75
+ orm: "prisma",
76
+ serverPort: topology.primaryApi?.port || 3000
77
+ },
78
+ targets: {
79
+ server: profile === "fly_io" ? "fly.io" : "railway",
80
+ web: "vercel",
81
+ database: databaseTarget
82
+ },
83
+ requiredEnv: ["DATABASE_URL", "PUBLIC_TOPOGRAM_API_BASE_URL"],
84
+ recommendedCommands: {
85
+ deployServer: profile === "fly_io" ? "fly deploy" : "railway up",
86
+ deployWeb: "vercel deploy",
87
+ migrate: "npm run db:migrate"
88
+ }
89
+ };
90
+ }
91
+
92
+ function renderDeploymentEnvExample(plan) {
93
+ return `# Deployment profile
94
+ TOPOGRAM_DEPLOY_PROFILE=${plan.deployment.profile}
95
+
96
+ # Shared runtime variables
97
+ DATABASE_URL=
98
+ PUBLIC_TOPOGRAM_API_BASE_URL=
99
+
100
+ # Optional server runtime values
101
+ PORT=${plan.runtime.serverPort}
102
+ NODE_ENV=production
103
+ `;
104
+ }
105
+
106
+ function renderDeploymentReadme(plan) {
107
+ const platformNotes = plan.deployment.profile === "fly_io"
108
+ ? `## Fly.io Server Deploy
109
+
110
+ - Review \`fly.toml\`
111
+ - Set secrets with \`fly secrets set DATABASE_URL=...\`
112
+ - Deploy with \`${plan.recommendedCommands.deployServer}\`
113
+ `
114
+ : `## Railway Server Deploy
115
+
116
+ - Review \`railway.json\`
117
+ - Set environment variables in Railway
118
+ - Deploy with \`${plan.recommendedCommands.deployServer}\`
119
+ `;
120
+
121
+ return `# ${plan.deployment.name}
122
+
123
+ This bundle packages deployment helpers for the generated runtime.
124
+
125
+ - \`${plan.directories.server}/\`: generated Hono + Prisma server scaffold
126
+ - \`${plan.directories.web}/\`: generated ${plan.runtime.web === "react" ? "Vite + React Router" : "SvelteKit"} web scaffold
127
+ - platform deployment files for \`${plan.deployment.profile}\`
128
+ - a Vercel config for the web app
129
+
130
+ ${platformNotes}
131
+ ## Web Deploy
132
+
133
+ - Review \`${plan.directories.web}/vercel.json\`
134
+ - Set \`PUBLIC_TOPOGRAM_API_BASE_URL\`
135
+ - Deploy with \`${plan.recommendedCommands.deployWeb}\`
136
+
137
+ ## Database Migrations
138
+
139
+ - Run \`${plan.recommendedCommands.migrate}\` against the target database before or during deploy
140
+ - The generated server bundle includes Prisma schema and DB lifecycle scripts for greenfield or brownfield environments
141
+ `;
142
+ }
143
+
144
+ function renderDeploymentPackageJson(plan) {
145
+ return `${JSON.stringify({
146
+ name: "topogram-deployment-bundle",
147
+ private: true,
148
+ scripts: {
149
+ "deploy:check": "bash ./scripts/deploy-check.sh",
150
+ "deploy:server": plan.recommendedCommands.deployServer,
151
+ "deploy:web": plan.recommendedCommands.deployWeb,
152
+ "db:migrate": "bash ./scripts/deploy-migrate.sh"
153
+ }
154
+ }, null, 2)}\n`;
155
+ }
156
+
157
+ function renderDeploymentCheckScript(plan) {
158
+ return renderRootEnvFileShellScript([
159
+ "for name in DATABASE_URL PUBLIC_TOPOGRAM_API_BASE_URL; do",
160
+ ' if [[ -z "${!name:-}" ]]; then',
161
+ ' echo "Missing required deployment variable: $name" >&2',
162
+ " exit 1",
163
+ " fi",
164
+ "done",
165
+ "",
166
+ `echo "Deployment configuration looks ready for ${plan.deployment.profile}."`
167
+ ], {
168
+ blankLineAfterRoot: false,
169
+ includeScriptDir: false,
170
+ rootDirExpression: 'ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"'
171
+ });
172
+ }
173
+
174
+ function renderDeploymentMigrateScript(plan) {
175
+ return renderRootShellScript([
176
+ `cd "$ROOT_DIR/${plan.directories.server}"`,
177
+ "npm install",
178
+ "npm exec -- prisma generate --schema prisma/schema.prisma",
179
+ "npm exec -- prisma db push --schema prisma/schema.prisma --skip-generate"
180
+ ]);
181
+ }
182
+
183
+ function renderServerDockerfile(plan) {
184
+ return `FROM node:22-alpine
185
+ WORKDIR /app
186
+
187
+ COPY package.json ./
188
+ RUN npm install
189
+
190
+ COPY tsconfig.json ./
191
+ COPY prisma ./prisma
192
+ COPY src ./src
193
+
194
+ RUN npm exec -- prisma generate --schema prisma/schema.prisma
195
+
196
+ ENV PORT=${plan.runtime.serverPort}
197
+ EXPOSE ${plan.runtime.serverPort}
198
+
199
+ CMD ["npm", "run", "dev"]
200
+ `;
201
+ }
202
+
203
+ function renderFlyToml(plan) {
204
+ return `app = "${slugifyAppName(plan.deployment.name)}"
205
+ primary_region = "ord"
206
+
207
+ [build]
208
+ dockerfile = "${plan.directories.server}/Dockerfile"
209
+
210
+ [env]
211
+ PORT = "${plan.runtime.serverPort}"
212
+
213
+ [http_service]
214
+ internal_port = ${plan.runtime.serverPort}
215
+ force_https = true
216
+ auto_stop_machines = "stop"
217
+ auto_start_machines = true
218
+ min_machines_running = 0
219
+ `;
220
+ }
221
+
222
+ function renderRailwayJson(plan) {
223
+ return `${JSON.stringify({
224
+ "$schema": "https://railway.app/railway.schema.json",
225
+ build: {
226
+ builder: "DOCKERFILE",
227
+ dockerfilePath: `${plan.directories.server}/Dockerfile`
228
+ },
229
+ deploy: {
230
+ startCommand: "npm run dev",
231
+ restartPolicyType: "ON_FAILURE",
232
+ restartPolicyMaxRetries: 10
233
+ }
234
+ }, null, 2)}\n`;
235
+ }
236
+
237
+ function renderVercelJson(plan) {
238
+ return `${JSON.stringify({
239
+ framework: plan.runtime.web === "react" ? "vite" : "sveltekit"
240
+ }, null, 2)}\n`;
241
+ }
242
+
243
+ export function generateDeploymentBundle(graph, options = {}) {
244
+ const plan = buildDeploymentPlan(graph, options);
245
+ const topology = resolveRuntimeTopology(graph, options);
246
+ const files = {
247
+ ".env.example": renderDeploymentEnvExample(plan),
248
+ "README.md": renderDeploymentReadme(plan),
249
+ "package.json": renderDeploymentPackageJson(plan),
250
+ "scripts/deploy-check.sh": renderDeploymentCheckScript(plan),
251
+ "scripts/deploy-migrate.sh": renderDeploymentMigrateScript(plan),
252
+ [`${plan.directories.server}/Dockerfile`]: renderServerDockerfile(plan),
253
+ [`${plan.directories.web}/vercel.json`]: renderVercelJson(plan)
254
+ };
255
+
256
+ if (plan.deployment.profile === "fly_io") {
257
+ files["fly.toml"] = renderFlyToml(plan);
258
+ }
259
+ if (plan.deployment.profile === "railway") {
260
+ files["railway.json"] = renderRailwayJson(plan);
261
+ }
262
+
263
+ for (const component of topology.apiComponents) {
264
+ const serverBundle = generateServerBundle(graph, component.projection.id, { ...options, component });
265
+ mergeNamedBundles(files, {
266
+ [topology.serviceDir(component)]: serverBundle
267
+ });
268
+ }
269
+ for (const component of topology.webComponents) {
270
+ const webBundle = generateWebBundle(graph, component.projection.id, { ...options, component });
271
+ mergeNamedBundles(files, {
272
+ [topology.webDir(component)]: webBundle
273
+ });
274
+ }
275
+ for (const component of topology.dbComponents) {
276
+ const dbBundle = generateDbBundle(graph, component.projection.id, { ...options, component });
277
+ mergeNamedBundles(files, {
278
+ [topology.dbDir(component)]: dbBundle
279
+ });
280
+ }
281
+
282
+ return files;
283
+ }
284
+
285
+ export function generateDeploymentPlan(graph, options = {}) {
286
+ return buildDeploymentPlan(graph, options);
287
+ }