@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,635 @@
1
+ import { generateDbLifecyclePlan } from "../surfaces/databases/lifecycle-shared.js";
2
+ import { getExampleImplementation } from "../../example-implementation.js";
3
+ import {
4
+ generateDbBundle,
5
+ generateServerBundle,
6
+ generateWebBundle,
7
+ dbEnvVarsForComponent,
8
+ getDefaultEnvironmentProjections,
9
+ resolveRuntimeTopology,
10
+ runtimePorts,
11
+ runtimeUrls
12
+ } from "./shared.js";
13
+ import { mergeNamedBundles, renderEnvAwareShellScript, renderLoadEnvScript, renderRootShellScript } from "./bundle-shared.js";
14
+ import { generatorProfile as manifestGeneratorProfile } from "../registry.js";
15
+
16
+ function projectionHintProfile(projection, fallback) {
17
+ if (!projection) {
18
+ return fallback;
19
+ }
20
+ for (const entry of projection.generatorDefaults || []) {
21
+ if (entry.key === "profile" && entry.value != null) {
22
+ return entry.value;
23
+ }
24
+ }
25
+ return fallback;
26
+ }
27
+
28
+ function runtimeReferenceFor(graph, options = {}) {
29
+ try {
30
+ return getExampleImplementation(graph, options).runtime.reference;
31
+ } catch {
32
+ return {
33
+ environment: { name: "Topogram Runtime", databaseName: "topogram_app", envExample: "" },
34
+ ports: { server: 3000, web: 5173 },
35
+ demoEnv: { userId: "11111111-1111-4111-8111-111111111111" }
36
+ };
37
+ }
38
+ }
39
+
40
+ function buildEnvironmentPlan(graph, options = {}) {
41
+ const runtimeReference = runtimeReferenceFor(graph, options);
42
+ const topology = resolveRuntimeTopology(graph, options);
43
+ const { apiProjection, uiProjection, dbProjection } = getDefaultEnvironmentProjections(graph, options);
44
+ const dbLifecycle = dbProjection ? generateDbLifecyclePlan(graph, { ...options, projectionId: dbProjection.id }) : null;
45
+ const profile = options.profileId || (dbProjection?.platform === "db_sqlite" || !dbProjection ? "local_process" : "local_docker");
46
+ const usesDocker = profile === "local_docker";
47
+ const webProfile = manifestGeneratorProfile(topology.primaryWeb?.generator?.id, null) || projectionHintProfile(uiProjection, "sveltekit");
48
+ const isSqlite = dbProjection?.platform === "db_sqlite";
49
+ const ports = runtimePorts(runtimeReference, topology);
50
+
51
+ return {
52
+ type: "environment_plan",
53
+ environment: {
54
+ id: [apiProjection?.id, uiProjection?.id, dbProjection?.id].filter(Boolean).join("__") || "topogram_runtime",
55
+ name: runtimeReference.environment.name,
56
+ mode: "local_dev",
57
+ profile
58
+ },
59
+ topology: {
60
+ components: topology.components.map((component) => ({
61
+ id: component.id,
62
+ type: component.type,
63
+ projection: component.projection.id,
64
+ generator: component.generator,
65
+ port: component.port ?? null,
66
+ api: component.api || null,
67
+ database: component.database || null
68
+ }))
69
+ },
70
+ projections: {
71
+ api: {
72
+ id: apiProjection?.id || null,
73
+ platform: apiProjection?.platform || null
74
+ },
75
+ ui: {
76
+ id: uiProjection?.id || null,
77
+ platform: uiProjection?.platform || null
78
+ },
79
+ db: {
80
+ id: dbProjection?.id || null,
81
+ platform: dbProjection?.platform || null,
82
+ profile: dbProjection?.generatorDefaults?.profile || dbProjection?.profile || null
83
+ }
84
+ },
85
+ generators: {
86
+ server: "hono-server",
87
+ web: "sveltekit-app",
88
+ dbLifecycle: "db-lifecycle-bundle"
89
+ },
90
+ runtimeProfiles: {
91
+ server: "hono",
92
+ web: webProfile,
93
+ orm: dbLifecycle?.runtimeProfile || null,
94
+ database: dbLifecycle?.dbProfile || null
95
+ },
96
+ orchestration: {
97
+ usesDocker,
98
+ database: !dbProjection
99
+ ? "none"
100
+ : isSqlite
101
+ ? "local_process_sqlite"
102
+ : usesDocker
103
+ ? "docker_postgres"
104
+ : "local_process_postgres"
105
+ },
106
+ directories: {
107
+ server: topology.primaryApi ? topology.serviceDir(topology.primaryApi) : null,
108
+ web: topology.primaryWeb ? topology.webDir(topology.primaryWeb) : null,
109
+ db: topology.primaryDb ? topology.dbDir(topology.primaryDb) : null,
110
+ scripts: "scripts"
111
+ },
112
+ components: {
113
+ apis: topology.apiComponents.map((component) => ({
114
+ id: component.id,
115
+ projection: component.projection.id,
116
+ port: component.port || ports.server,
117
+ dir: topology.serviceDir(component),
118
+ database: component.database,
119
+ databaseEnv: component.databaseComponent
120
+ ? dbEnvVarsForComponent(component.databaseComponent, { primary: component.databaseComponent?.id === topology.primaryDb?.id })
121
+ : null
122
+ })),
123
+ webs: topology.webComponents.map((component) => ({
124
+ id: component.id,
125
+ projection: component.projection.id,
126
+ port: component.port || ports.web,
127
+ dir: topology.webDir(component),
128
+ api: component.api
129
+ })),
130
+ databases: topology.dbComponents.map((component) => ({
131
+ id: component.id,
132
+ projection: component.projection.id,
133
+ platform: component.projection.platform,
134
+ port: component.port,
135
+ dir: topology.dbDir(component),
136
+ env: dbEnvVarsForComponent(component, { primary: component.id === topology.primaryDb?.id })
137
+ }))
138
+ },
139
+ ports: {
140
+ database: !dbProjection || isSqlite ? null : topology.primaryDb?.port || 5432,
141
+ server: ports.server,
142
+ web: ports.web
143
+ },
144
+ files: {
145
+ rootEnv: ".env.example",
146
+ dockerCompose: usesDocker ? "docker-compose.yml" : null,
147
+ readme: "README.md",
148
+ packageJson: "package.json"
149
+ },
150
+ commands: {
151
+ bootstrapDb: "./scripts/bootstrap-db.sh",
152
+ dev: "./scripts/stack-dev.sh",
153
+ dockerDb: usesDocker ? "./scripts/docker-db.sh" : null,
154
+ dockerStack: usesDocker ? "./scripts/docker-stack.sh" : null
155
+ },
156
+ runtimeReference
157
+ };
158
+ }
159
+
160
+ function renderEnvironmentEnvExample(plan) {
161
+ const demo = plan.runtimeReference.demoEnv;
162
+ const databaseName = plan.runtimeReference.environment.databaseName || "topogram_app";
163
+ const urls = runtimeUrls(plan.runtimeReference, {
164
+ primaryApi: { port: plan.ports.server },
165
+ primaryWeb: { port: plan.ports.web }
166
+ });
167
+ const extraDatabaseLines = plan.components.databases
168
+ .filter((component) => component.id !== plan.components.databases[0]?.id)
169
+ .map((component) => {
170
+ const fallbackName = `${databaseName}_${component.id}`;
171
+ if (component.platform === "db_sqlite") {
172
+ return `${component.env.databaseUrl}=file:./var/${component.id}.sqlite`;
173
+ }
174
+ return [
175
+ `${component.env.dbPort}=${component.port || 5432}`,
176
+ `${component.env.postgresDb}=${fallbackName}`,
177
+ `${component.env.databaseUrl}=postgresql://\${POSTGRES_USER}@localhost:${component.port || 5432}/${fallbackName}?schema=public`,
178
+ `${component.env.databaseAdminUrl}=postgresql://\${POSTGRES_USER}@localhost:${component.port || 5432}/postgres`
179
+ ].join("\n");
180
+ })
181
+ .filter(Boolean)
182
+ .join("\n");
183
+ const commonLines = `# Environment profile
184
+ TOPOGRAM_ENVIRONMENT_PROFILE=${plan.environment.profile}
185
+
186
+ # Local stack ports
187
+ ${plan.components.apis.length ? `SERVER_PORT=${plan.ports.server}\n` : ""}${plan.components.webs.length ? `WEB_PORT=${plan.ports.web}\n` : ""}${plan.components.webs.length && plan.components.apis.length ? `PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}\n` : ""}${plan.components.webs.length ? `TOPOGRAM_CORS_ORIGINS=${urls.web},http://127.0.0.1:${plan.ports.web}\n` : ""}PUBLIC_TOPOGRAM_DEMO_USER_ID=${demo.userId}
188
+ TOPOGRAM_DEMO_USER_ID=${demo.userId}
189
+ ${plan.runtimeReference.environment.envExample || ""}
190
+ TOPOGRAM_SEED_DEMO=true
191
+ `;
192
+ if (!plan.projections.db.platform) {
193
+ return commonLines;
194
+ }
195
+ if (plan.projections.db.platform === "db_sqlite") {
196
+ return `# Environment profile
197
+ TOPOGRAM_ENVIRONMENT_PROFILE=${plan.environment.profile}
198
+
199
+ # Local stack ports
200
+ SERVER_PORT=${plan.ports.server}
201
+ WEB_PORT=${plan.ports.web}
202
+
203
+ # Local SQLite defaults
204
+ DATABASE_URL=file:./var/${databaseName}.sqlite
205
+ ${extraDatabaseLines ? `${extraDatabaseLines}\n` : ""}PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}
206
+ TOPOGRAM_CORS_ORIGINS=${urls.web},http://127.0.0.1:${plan.ports.web}
207
+ PUBLIC_TOPOGRAM_DEMO_USER_ID=${demo.userId}
208
+ TOPOGRAM_DEMO_USER_ID=${demo.userId}
209
+ ${plan.runtimeReference.environment.envExample || ""}
210
+ TOPOGRAM_SEED_DEMO=true
211
+ `;
212
+ }
213
+
214
+ return `# Environment profile
215
+ TOPOGRAM_ENVIRONMENT_PROFILE=${plan.environment.profile}
216
+
217
+ # Local stack ports
218
+ DB_PORT=${plan.ports.database || 5432}
219
+ SERVER_PORT=${plan.ports.server}
220
+ WEB_PORT=${plan.ports.web}
221
+
222
+ # Local Postgres defaults
223
+ POSTGRES_DB=${databaseName}
224
+ POSTGRES_USER=\${USER:-postgres}
225
+ POSTGRES_PASSWORD=postgres
226
+
227
+ # Local shell/runtime defaults
228
+ DATABASE_URL=postgresql://\${POSTGRES_USER}@localhost:${plan.ports.database || 5432}/${databaseName}?schema=public
229
+ DATABASE_ADMIN_URL=postgresql://\${POSTGRES_USER}@localhost:${plan.ports.database || 5432}/postgres
230
+ ${extraDatabaseLines ? `${extraDatabaseLines}\n` : ""}PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}
231
+ TOPOGRAM_CORS_ORIGINS=${urls.web},http://127.0.0.1:${plan.ports.web}
232
+ PUBLIC_TOPOGRAM_DEMO_USER_ID=${demo.userId}
233
+ TOPOGRAM_DEMO_USER_ID=${demo.userId}
234
+ ${plan.runtimeReference.environment.envExample || ""}
235
+ TOPOGRAM_SEED_DEMO=true
236
+ `;
237
+ }
238
+
239
+ function renderEnvironmentPackageJson(plan) {
240
+ const scripts = {
241
+ "db:bootstrap": "bash ./scripts/bootstrap-db.sh",
242
+ "dev:server": "bash ./scripts/server-dev.sh",
243
+ "dev:web": "bash ./scripts/web-dev.sh",
244
+ dev: "bash ./scripts/stack-dev.sh"
245
+ };
246
+ if (plan.orchestration.usesDocker) {
247
+ scripts["docker:db"] = "bash ./scripts/docker-db.sh";
248
+ scripts["docker:stack"] = "bash ./scripts/docker-stack.sh";
249
+ }
250
+ return `${JSON.stringify({
251
+ name: "topogram-runtime-stack",
252
+ private: true,
253
+ scripts
254
+ }, null, 2)}\n`;
255
+ }
256
+
257
+ function apiDatabaseExportLines(component) {
258
+ if (!component?.databaseEnv) {
259
+ return [];
260
+ }
261
+ const env = component.databaseEnv;
262
+ return [
263
+ `if [[ -n "\${${env.databaseUrl}:-}" ]]; then export DATABASE_URL="\${${env.databaseUrl}}"; fi`,
264
+ `if [[ -n "\${${env.databaseAdminUrl}:-}" ]]; then export DATABASE_ADMIN_URL="\${${env.databaseAdminUrl}}"; fi`
265
+ ];
266
+ }
267
+
268
+ function renderEnvironmentReadme(plan) {
269
+ const hasDb = plan.components.databases.length > 0;
270
+ const hasApi = plan.components.apis.length > 0;
271
+ const hasWeb = plan.components.webs.length > 0;
272
+ const localProcessNotes = !hasDb
273
+ ? "- This bundle has no generated database surface."
274
+ : plan.projections.db.platform === "db_sqlite"
275
+ ? "- SQLite is file-backed for this bundle; no separate DB server is required."
276
+ : `- Make sure the Postgres server is already running before \`${plan.commands.bootstrapDb}\`.\n- \`DATABASE_URL\` and \`DATABASE_ADMIN_URL\` should point at your local or managed Postgres instance.`;
277
+ const dockerSection = plan.orchestration.usesDocker
278
+ ? `## Alternative Docker Workflow
279
+
280
+ - Start only the database: \`${plan.commands.dockerDb}\`
281
+ - Start the database, server, and web app in containers: \`${plan.commands.dockerStack}\`
282
+ `
283
+ : `## Local Process Notes
284
+
285
+ - Install Node.js and npm locally before using this bundle.
286
+ ${localProcessNotes}
287
+ `;
288
+
289
+ return `# ${plan.environment.name}
290
+
291
+ This bundle packages the generated runtime into one local environment:
292
+
293
+ ${hasApi ? "- `services/<api-id>/`: generated API service scaffolds\n" : ""}${hasWeb ? `- \`web/<web-id>/\`: generated ${plan.runtimeProfiles.web === "react" ? "Vite + React Router" : plan.runtimeProfiles.web === "vanilla" ? "vanilla HTML/CSS/JS" : "SvelteKit"} web scaffolds\n` : ""}${hasDb ? "- `db/<db-id>/`: generated DB lifecycle bundles\n" : ""}${plan.files.dockerCompose ? `- \`${plan.files.dockerCompose}\`: local Postgres container` : hasDb ? (plan.projections.db.platform === "db_sqlite" ? "- local SQLite file orchestration (no Docker files generated)" : "- local-process Postgres orchestration (no Docker files generated)") : "- no DB orchestration is generated"}
294
+
295
+ ## Quick Start
296
+
297
+ 1. Copy \`.env.example\` to \`.env\` if you want to customize defaults
298
+ 2. Start the database:
299
+ - ${!hasDb ? "not applicable" : plan.projections.db.platform === "db_sqlite" ? "no separate DB service is required" : plan.orchestration.usesDocker ? `\`${plan.commands.dockerDb}\`` : "use your local Postgres service"}
300
+ 3. Bootstrap or migrate the database:
301
+ - \`${plan.commands.bootstrapDb}\`
302
+ 4. Start the stack:
303
+ - \`${plan.commands.dev}\`
304
+
305
+ ## Demo Seed Data
306
+
307
+ - Bootstrap seeds demo data by default
308
+ - Set \`TOPOGRAM_SEED_DEMO=false\` to skip demo seeding
309
+ - Default seeded IDs come from \`.env.example\`
310
+
311
+ ${dockerSection}
312
+
313
+ ## Notes
314
+
315
+ - ${hasApi && hasDb ? `The generated server expects ${plan.projections.db.platform === "db_sqlite" ? "SQLite plus Prisma." : "Postgres plus Prisma."}` : hasApi ? "The generated server is stateless." : "No server surface is generated."}
316
+ - ${hasWeb && hasApi ? "The generated web app talks to `PUBLIC_TOPOGRAM_API_BASE_URL`." : hasWeb ? "The generated web app is standalone." : "No web surface is generated."}
317
+ - If \`.env\` is missing, generated scripts fall back to \`.env.example\`.
318
+ - The DB lifecycle scripts remain the source of truth for greenfield bootstrap and brownfield migration.
319
+ `;
320
+ }
321
+
322
+ function renderEnvironmentLoadEnvScript() {
323
+ return renderLoadEnvScript({ searchParentEnv: true });
324
+ }
325
+
326
+ function renderEnvironmentBootstrapDbScript(plan) {
327
+ const dbBootstrapLines = plan.components.databases.map((component) => {
328
+ const env = component.env;
329
+ const runtimeApi = plan.components.apis.find((apiComponent) => apiComponent.database === component.id);
330
+ const assignments = [
331
+ `DATABASE_URL="\${${env.databaseUrl}:-}"`,
332
+ `DATABASE_ADMIN_URL="\${${env.databaseAdminUrl}:-}"`,
333
+ runtimeApi ? `TOPOGRAM_RUNTIME_SERVER_DIR="$ROOT_DIR/${runtimeApi.dir}"` : null
334
+ ].filter(Boolean).join(" ");
335
+ return `(cd "$ROOT_DIR/${component.dir}" && TOPOGRAM_ENV_FILE=/dev/null ${assignments} bash ./scripts/db-bootstrap-or-migrate.sh)`;
336
+ });
337
+ const primaryApi = plan.components.apis[0];
338
+ if (plan.components.databases.length === 0) {
339
+ return renderEnvAwareShellScript([
340
+ 'echo "No database components are configured; skipping DB bootstrap."'
341
+ ]);
342
+ }
343
+ return renderEnvAwareShellScript([
344
+ `if [[ "\${TOPOGRAM_ENVIRONMENT_PROFILE:-${plan.environment.profile}}" == "local_docker" ]]; then`,
345
+ " if ! command -v docker >/dev/null 2>&1; then",
346
+ ' echo "Docker is required for the local_docker profile, but it is not installed." >&2',
347
+ ' echo "Set TOPOGRAM_ENVIRONMENT_PROFILE=local_process and point DATABASE_URL at a working local database, or install Docker." >&2',
348
+ " exit 1",
349
+ " fi",
350
+ ' docker compose -f "$ROOT_DIR/docker-compose.yml" up -d db',
351
+ "fi",
352
+ ...dbBootstrapLines,
353
+ 'if [[ "${TOPOGRAM_SEED_DEMO:-true}" != "false" ]]; then',
354
+ ...apiDatabaseExportLines(primaryApi),
355
+ ...(primaryApi?.database
356
+ ? [`(cd "$ROOT_DIR/${primaryApi.dir}" && npm install && npm exec -- prisma generate --schema prisma/schema.prisma && npm exec -- prisma db push --schema prisma/schema.prisma --skip-generate && npm run seed:demo)`]
357
+ : ['echo "No DB-backed API component is configured; skipping demo seed."']),
358
+ "fi"
359
+ ]);
360
+ }
361
+
362
+ function componentScriptOptions() {
363
+ return {
364
+ rootDirExpression: 'ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"',
365
+ loadEnvScript: '"$ROOT_DIR/scripts/load-env.sh"'
366
+ };
367
+ }
368
+
369
+ function renderEnvironmentServerDevScript(plan, component = plan.components.apis[0], options = {}) {
370
+ if (!component) {
371
+ return renderEnvAwareShellScript(['echo "No API components are configured."']);
372
+ }
373
+ const guardPortsScript = options.componentScript ? '"$ROOT_DIR/scripts/guard-ports.mjs"' : '"$SCRIPT_DIR/guard-ports.mjs"';
374
+ return renderEnvAwareShellScript([
375
+ `node ${guardPortsScript} api`,
376
+ "",
377
+ ...apiDatabaseExportLines(component),
378
+ `export PORT="\${${component.id.toUpperCase()}_PORT:-\${SERVER_PORT:-${component.port}}}"`,
379
+ `export TOPOGRAM_CORS_ORIGINS="\${TOPOGRAM_CORS_ORIGINS:-http://localhost:\${WEB_PORT:-${plan.ports.web}},http://127.0.0.1:\${WEB_PORT:-${plan.ports.web}}}"`,
380
+ "",
381
+ `cd "$ROOT_DIR/${component.dir}"`,
382
+ "npm install",
383
+ ...(component.database ? ["npm exec -- prisma generate --schema prisma/schema.prisma"] : []),
384
+ "npm run dev"
385
+ ], options.componentScript ? componentScriptOptions() : {});
386
+ }
387
+
388
+ function renderEnvironmentWebDevScript(plan, component = plan.components.webs[0], options = {}) {
389
+ if (!component) {
390
+ return renderEnvAwareShellScript(['echo "No web components are configured."']);
391
+ }
392
+ const apiComponent = plan.components.apis.find((entry) => entry.id === component.api) || plan.components.apis[0];
393
+ const guardPortsScript = options.componentScript ? '"$ROOT_DIR/scripts/guard-ports.mjs"' : '"$SCRIPT_DIR/guard-ports.mjs"';
394
+ return renderEnvAwareShellScript([
395
+ `node ${guardPortsScript} web`,
396
+ "",
397
+ ...(apiComponent ? [`export PUBLIC_TOPOGRAM_API_BASE_URL="\${PUBLIC_TOPOGRAM_API_BASE_URL:-http://localhost:\${${apiComponent.id.toUpperCase()}_PORT:-\${SERVER_PORT:-${apiComponent.port}}}}"`] : []),
398
+ `export TOPOGRAM_CORS_ORIGINS="\${TOPOGRAM_CORS_ORIGINS:-http://localhost:\${${component.id.toUpperCase()}_PORT:-\${WEB_PORT:-${component.port}}},http://127.0.0.1:\${${component.id.toUpperCase()}_PORT:-\${WEB_PORT:-${component.port}}}}"`,
399
+ "",
400
+ `cd "$ROOT_DIR/${component.dir}"`,
401
+ "npm install",
402
+ `npm run dev -- --host "\${WEB_HOST:-127.0.0.1}" --port "\${${component.id.toUpperCase()}_PORT:-\${WEB_PORT:-${component.port}}}"`,
403
+ ], options.componentScript ? componentScriptOptions() : {});
404
+ }
405
+
406
+ function renderEnvironmentStackDevScript(plan) {
407
+ const startLines = [
408
+ ...plan.components.apis.map((component) => `bash "$SCRIPT_DIR/services/${component.id}-dev.sh" &\nPIDS+=($!)`),
409
+ ...plan.components.webs.map((component) => `bash "$SCRIPT_DIR/web/${component.id}-dev.sh" &\nPIDS+=($!)`)
410
+ ];
411
+ return `#!/usr/bin/env bash
412
+ set -euo pipefail
413
+
414
+ SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
415
+ PIDS=()
416
+
417
+ node "$SCRIPT_DIR/guard-ports.mjs" stack
418
+
419
+ if [[ "\${TOPOGRAM_SKIP_STACK_BOOTSTRAP:-false}" != "true" ]]; then
420
+ bash "$SCRIPT_DIR/bootstrap-db.sh"
421
+ fi
422
+
423
+ ${startLines.length ? startLines.join("\n") : 'echo "No long-running dev services are configured."'}
424
+
425
+ kill_tree() {
426
+ local pid="$1"
427
+ local child
428
+ while IFS= read -r child; do
429
+ [[ -n "$child" ]] && kill_tree "$child"
430
+ done < <(pgrep -P "$pid" 2>/dev/null || true)
431
+ kill "$pid" >/dev/null 2>&1 || true
432
+ }
433
+
434
+ cleanup() {
435
+ if [[ "\${#PIDS[@]}" -gt 0 ]]; then
436
+ for pid in "\${PIDS[@]}"; do
437
+ kill_tree "$pid"
438
+ done
439
+ fi
440
+ }
441
+
442
+ trap cleanup EXIT INT TERM
443
+ ${startLines.length ? "wait" : ""}
444
+ `;
445
+ }
446
+
447
+ function renderEnvironmentGuardPortsScript(plan) {
448
+ const ports = [
449
+ ...plan.components.apis.map((component) => ({ id: component.id, type: "api", env: `${component.id.toUpperCase()}_PORT`, fallbackEnv: "SERVER_PORT", port: component.port })),
450
+ ...plan.components.webs.map((component) => ({ id: component.id, type: "web", env: `${component.id.toUpperCase()}_PORT`, fallbackEnv: "WEB_PORT", port: component.port }))
451
+ ];
452
+ return `#!/usr/bin/env node
453
+ import net from "node:net";
454
+
455
+ const role = process.argv[2] || "stack";
456
+ const ports = ${JSON.stringify(ports, null, 2)};
457
+ const expectedService = ${JSON.stringify(plan.runtimeReference.serviceName || "")};
458
+
459
+ function effectivePort(entry) {
460
+ return Number(process.env[entry.env] || process.env[entry.fallbackEnv] || entry.port);
461
+ }
462
+
463
+ function portInUse(port) {
464
+ return new Promise((resolve) => {
465
+ const server = net.createServer();
466
+ server.once("error", (error) => {
467
+ resolve(Boolean(error && error.code === "EADDRINUSE"));
468
+ });
469
+ server.once("listening", () => {
470
+ server.close(() => resolve(false));
471
+ });
472
+ server.listen(port, "127.0.0.1");
473
+ });
474
+ }
475
+
476
+ async function readHealth(port) {
477
+ try {
478
+ const response = await fetch(\`http://127.0.0.1:\${port}/health\`);
479
+ const body = await response.json().catch(() => null);
480
+ return { status: response.status, body };
481
+ } catch {
482
+ return null;
483
+ }
484
+ }
485
+
486
+ async function failForServerPort(port) {
487
+ const health = await readHealth(port);
488
+ if (health?.body?.service && expectedService && health.body.service !== expectedService) {
489
+ console.error(\`Port \${port} is already serving \${health.body.service}, not \${expectedService}.\`);
490
+ console.error("Stop the other stack or override SERVER_PORT/PUBLIC_TOPOGRAM_API_BASE_URL before retrying.");
491
+ process.exit(1);
492
+ }
493
+ if (health?.body?.service) {
494
+ console.error(\`Port \${port} is already in use by \${health.body.service}.\`);
495
+ } else {
496
+ console.error(\`Port \${port} is already in use.\`);
497
+ }
498
+ process.exit(1);
499
+ }
500
+
501
+ async function failForWebPort(port) {
502
+ console.error(\`Port \${port} is already in use.\`);
503
+ console.error("Stop the other web dev server or override WEB_PORT before retrying.");
504
+ process.exit(1);
505
+ }
506
+
507
+ for (const entry of ports) {
508
+ if (role !== "stack" && role !== entry.type) {
509
+ continue;
510
+ }
511
+ const port = effectivePort(entry);
512
+ if (await portInUse(port)) {
513
+ if (entry.type === "api") {
514
+ await failForServerPort(port);
515
+ }
516
+ await failForWebPort(port);
517
+ }
518
+ }
519
+ `;
520
+ }
521
+
522
+ function renderEnvironmentDockerDbScript() {
523
+ return renderRootShellScript(['docker compose -f "$ROOT_DIR/docker-compose.yml" up -d db']);
524
+ }
525
+
526
+ function renderEnvironmentDockerStackScript() {
527
+ return renderRootShellScript(['docker compose -f "$ROOT_DIR/docker-compose.yml" up --build']);
528
+ }
529
+
530
+ function renderEnvironmentDockerCompose(plan) {
531
+ return `services:
532
+ db:
533
+ image: postgres:16-alpine
534
+ restart: unless-stopped
535
+ environment:
536
+ POSTGRES_DB: \${POSTGRES_DB:-${plan.runtimeReference.environment.databaseName || "topogram_app"}}
537
+ POSTGRES_USER: \${POSTGRES_USER:-postgres}
538
+ POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-postgres}
539
+ ports:
540
+ - "\${DB_PORT:-5432}:5432"
541
+ volumes:
542
+ - postgres-data:/var/lib/postgresql/data
543
+
544
+ server:
545
+ image: node:22-alpine
546
+ working_dir: /app
547
+ depends_on:
548
+ - db
549
+ environment:
550
+ DATABASE_URL: postgresql://\${POSTGRES_USER:-postgres}:\${POSTGRES_PASSWORD:-postgres}@db:5432/\${POSTGRES_DB:-${plan.runtimeReference.environment.databaseName || "topogram_app"}}?schema=public
551
+ PORT: \${SERVER_PORT:-${plan.ports.server}}
552
+ TOPOGRAM_CORS_ORIGINS: http://localhost:\${WEB_PORT:-${plan.ports.web}},http://127.0.0.1:\${WEB_PORT:-${plan.ports.web}}
553
+ ports:
554
+ - "127.0.0.1:\${SERVER_PORT:-${plan.ports.server}}:\${SERVER_PORT:-${plan.ports.server}}"
555
+ volumes:
556
+ - ./${plan.directories.server}:/app
557
+ command: >
558
+ sh -lc "npm install &&
559
+ npm exec -- prisma generate --schema prisma/schema.prisma &&
560
+ npm run dev"
561
+
562
+ web:
563
+ image: node:22-alpine
564
+ working_dir: /app
565
+ depends_on:
566
+ - server
567
+ environment:
568
+ PUBLIC_TOPOGRAM_API_BASE_URL: http://localhost:\${SERVER_PORT:-${plan.ports.server}}
569
+ ports:
570
+ - "127.0.0.1:\${WEB_PORT:-${plan.ports.web}}:\${WEB_PORT:-${plan.ports.web}}"
571
+ volumes:
572
+ - ./${plan.directories.web}:/app
573
+ command: >
574
+ sh -lc "npm install &&
575
+ npm run dev -- --host 0.0.0.0 --port \${WEB_PORT:-${plan.ports.web}}"
576
+
577
+ volumes:
578
+ postgres-data:
579
+ `;
580
+ }
581
+
582
+ export function generateEnvironmentBundle(graph, options = {}) {
583
+ const plan = buildEnvironmentPlan(graph, options);
584
+ const topology = resolveRuntimeTopology(graph, options);
585
+ const files = {
586
+ ".env.example": renderEnvironmentEnvExample(plan),
587
+ ".gitignore": "node_modules/\n.env\npostgres-data/\n",
588
+ "README.md": renderEnvironmentReadme(plan),
589
+ "package.json": renderEnvironmentPackageJson(plan),
590
+ "scripts/load-env.sh": renderEnvironmentLoadEnvScript(),
591
+ "scripts/bootstrap-db.sh": renderEnvironmentBootstrapDbScript(plan),
592
+ "scripts/server-dev.sh": renderEnvironmentServerDevScript(plan),
593
+ "scripts/web-dev.sh": renderEnvironmentWebDevScript(plan),
594
+ "scripts/stack-dev.sh": renderEnvironmentStackDevScript(plan),
595
+ "scripts/guard-ports.mjs": renderEnvironmentGuardPortsScript(plan)
596
+ };
597
+
598
+ for (const component of plan.components.apis) {
599
+ files[`scripts/services/${component.id}-dev.sh`] = renderEnvironmentServerDevScript(plan, component, { componentScript: true });
600
+ }
601
+ for (const component of plan.components.webs) {
602
+ files[`scripts/web/${component.id}-dev.sh`] = renderEnvironmentWebDevScript(plan, component, { componentScript: true });
603
+ }
604
+
605
+ if (plan.orchestration.usesDocker) {
606
+ files["docker-compose.yml"] = renderEnvironmentDockerCompose(plan);
607
+ files["scripts/docker-db.sh"] = renderEnvironmentDockerDbScript();
608
+ files["scripts/docker-stack.sh"] = renderEnvironmentDockerStackScript();
609
+ }
610
+
611
+ for (const component of topology.apiComponents) {
612
+ const serverBundle = generateServerBundle(graph, component.projection.id, { ...options, component });
613
+ mergeNamedBundles(files, {
614
+ [topology.serviceDir(component)]: serverBundle
615
+ });
616
+ }
617
+ for (const component of topology.webComponents) {
618
+ const webBundle = generateWebBundle(graph, component.projection.id, { ...options, component });
619
+ mergeNamedBundles(files, {
620
+ [topology.webDir(component)]: webBundle
621
+ });
622
+ }
623
+ for (const component of topology.dbComponents) {
624
+ const dbBundle = generateDbBundle(graph, component.projection.id, { ...options, component });
625
+ mergeNamedBundles(files, {
626
+ [topology.dbDir(component)]: dbBundle
627
+ });
628
+ }
629
+
630
+ return files;
631
+ }
632
+
633
+ export function generateEnvironmentPlan(graph, options = {}) {
634
+ return buildEnvironmentPlan(graph, options);
635
+ }
@@ -0,0 +1,32 @@
1
+ import { generateEnvironmentBundle, generateEnvironmentPlan } from "./environment.js";
2
+ import { generateDeploymentBundle, generateDeploymentPlan } from "./deployment.js";
3
+ import { generateRuntimeSmokeBundle, generateRuntimeSmokePlan } from "./smoke.js";
4
+ import { generateRuntimeCheckBundle, generateRuntimeCheckPlan } from "./runtime-check.js";
5
+ import { generateCompileCheckBundle, generateCompileCheckPlan } from "./compile-check.js";
6
+ import { generateAppBundle, generateAppBundlePlan } from "./app-bundle.js";
7
+ import { generateNativeParityBundle, generateNativeParityPlan } from "../native/parity-bundle.js";
8
+
9
+ const RUNTIME_TARGETS = {
10
+ "environment-plan": generateEnvironmentPlan,
11
+ "environment-bundle": generateEnvironmentBundle,
12
+ "deployment-plan": generateDeploymentPlan,
13
+ "deployment-bundle": generateDeploymentBundle,
14
+ "runtime-smoke-plan": generateRuntimeSmokePlan,
15
+ "runtime-smoke-bundle": generateRuntimeSmokeBundle,
16
+ "runtime-check-plan": generateRuntimeCheckPlan,
17
+ "runtime-check-bundle": generateRuntimeCheckBundle,
18
+ "compile-check-plan": generateCompileCheckPlan,
19
+ "compile-check-bundle": generateCompileCheckBundle,
20
+ "app-bundle-plan": generateAppBundlePlan,
21
+ "app-bundle": generateAppBundle,
22
+ "native-parity-plan": generateNativeParityPlan,
23
+ "native-parity-bundle": generateNativeParityBundle
24
+ };
25
+
26
+ export function generateRuntimeTarget(target, graph, options = {}) {
27
+ const generator = RUNTIME_TARGETS[target];
28
+ if (!generator) {
29
+ throw new Error(`Unsupported runtime generator target '${target}'`);
30
+ }
31
+ return generator(graph, options);
32
+ }