@topogram/cli 0.3.64 → 0.3.66
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.
- package/package.json +1 -1
- package/src/adoption/plan/index.js +716 -0
- package/src/adoption/plan.js +12 -703
- package/src/adoption/reporting.js +1 -1
- package/src/agent-brief.js +7 -21
- package/src/agent-ops/query-builders/auth.js +375 -0
- package/src/agent-ops/query-builders/change-risk/change-plan.js +123 -0
- package/src/agent-ops/query-builders/change-risk/import-plan.js +49 -0
- package/src/agent-ops/query-builders/change-risk/maintained.js +286 -0
- package/src/agent-ops/query-builders/change-risk/review-packets.js +123 -0
- package/src/agent-ops/query-builders/change-risk/risk.js +189 -0
- package/src/agent-ops/query-builders/change-risk.js +25 -0
- package/src/agent-ops/query-builders/common.js +149 -0
- package/src/agent-ops/query-builders/maintained-risk.js +539 -0
- package/src/agent-ops/query-builders/maintained-shared.js +120 -0
- package/src/agent-ops/query-builders/multi-agent.js +547 -0
- package/src/agent-ops/query-builders/projection-impacts.js +514 -0
- package/src/agent-ops/query-builders/work-packets.js +417 -0
- package/src/agent-ops/query-builders/workflow-context-shared.js +300 -0
- package/src/agent-ops/query-builders/workflow-context.js +398 -0
- package/src/agent-ops/query-builders/workflow-presets-core.js +677 -0
- package/src/agent-ops/query-builders/workflow-presets.js +341 -0
- package/src/agent-ops/query-builders.d.ts +26 -26
- package/src/agent-ops/query-builders.js +42 -5021
- package/src/archive/jsonl.js +2 -2
- package/src/archive/resolver-bridge.js +1 -1
- package/src/archive/unarchive.js +2 -1
- package/src/catalog/constants.js +10 -0
- package/src/catalog/copy.js +65 -0
- package/src/catalog/diagnostics.js +15 -0
- package/src/catalog/entries.js +42 -0
- package/src/catalog/files.js +67 -0
- package/src/catalog/provenance.js +123 -0
- package/src/catalog/source.js +150 -0
- package/src/catalog/validation.js +252 -0
- package/src/catalog.d.ts +2 -0
- package/src/catalog.js +18 -746
- package/src/cli/command-parsers/project.js +3 -0
- package/src/cli/command-parsers/shared.js +1 -1
- package/src/cli/commands/agent.js +2 -2
- package/src/cli/commands/catalog/check.js +31 -0
- package/src/cli/commands/catalog/copy.js +59 -0
- package/src/cli/commands/catalog/doctor.js +248 -0
- package/src/cli/commands/catalog/help.js +21 -0
- package/src/cli/commands/catalog/list.js +52 -0
- package/src/cli/commands/catalog/runner.js +92 -0
- package/src/cli/commands/catalog/shared.js +17 -0
- package/src/cli/commands/catalog/show.js +134 -0
- package/src/cli/commands/catalog.js +30 -615
- package/src/cli/commands/check.js +3 -3
- package/src/cli/commands/doctor.js +2 -9
- package/src/cli/commands/generator-policy/package-info.js +162 -0
- package/src/cli/commands/generator-policy/payloads.js +372 -0
- package/src/cli/commands/generator-policy/printers.js +159 -0
- package/src/cli/commands/generator-policy/runner.js +81 -0
- package/src/cli/commands/generator-policy/shared.js +39 -0
- package/src/cli/commands/generator-policy.js +15 -783
- package/src/cli/commands/import/adopt.js +170 -0
- package/src/cli/commands/import/check.js +91 -0
- package/src/cli/commands/import/diff.js +84 -0
- package/src/cli/commands/import/help.js +47 -0
- package/src/cli/commands/import/paths.js +269 -0
- package/src/cli/commands/import/plan.js +292 -0
- package/src/cli/commands/import/refresh.js +471 -0
- package/src/cli/commands/import/status-history.js +196 -0
- package/src/cli/commands/import/workspace.js +233 -0
- package/src/cli/commands/import.js +33 -1732
- package/src/cli/commands/migrate.js +153 -0
- package/src/cli/commands/package/constants.js +17 -0
- package/src/cli/commands/package/doctor.js +240 -0
- package/src/cli/commands/package/help.js +27 -0
- package/src/cli/commands/package/lockfile.js +135 -0
- package/src/cli/commands/package/npm.js +97 -0
- package/src/cli/commands/package/reporting.js +35 -0
- package/src/cli/commands/package/runner.js +33 -0
- package/src/cli/commands/package/shared.js +9 -0
- package/src/cli/commands/package/update-cli.js +252 -0
- package/src/cli/commands/package/versions.js +35 -0
- package/src/cli/commands/package.js +29 -813
- package/src/cli/commands/query/change-plan.js +68 -0
- package/src/cli/commands/query/definitions.js +202 -0
- package/src/cli/commands/query/import-adopt.js +121 -0
- package/src/cli/commands/query/runner/artifacts.js +102 -0
- package/src/cli/commands/query/runner/boundaries.js +211 -0
- package/src/cli/commands/query/runner/change.js +182 -0
- package/src/cli/commands/query/runner/import-adopt.js +111 -0
- package/src/cli/commands/query/runner/index.js +31 -0
- package/src/cli/commands/query/runner/output.js +12 -0
- package/src/cli/commands/query/runner/workflow.js +241 -0
- package/src/cli/commands/query/runner.js +3 -0
- package/src/cli/commands/query/workflow-context.js +5 -0
- package/src/cli/commands/query/workspace.js +270 -0
- package/src/cli/commands/query.js +9 -1300
- package/src/cli/commands/source.js +3 -12
- package/src/cli/commands/template/baseline.js +100 -0
- package/src/cli/commands/template/check.js +467 -0
- package/src/cli/commands/template/constants.js +8 -0
- package/src/cli/commands/template/diagnostics.js +26 -0
- package/src/cli/commands/template/help.js +28 -0
- package/src/cli/commands/template/lifecycle.js +404 -0
- package/src/cli/commands/template/list-show.js +287 -0
- package/src/cli/commands/template/policy.js +422 -0
- package/src/cli/commands/template/shared.js +127 -0
- package/src/cli/commands/template/updates.js +352 -0
- package/src/cli/commands/template-runner.js +6 -6
- package/src/cli/commands/template.js +41 -2143
- package/src/cli/commands/trust.js +1 -1
- package/src/cli/commands/workflow.js +6 -1
- package/src/cli/dispatcher.js +6 -1
- package/src/cli/help.js +15 -14
- package/src/cli/migration-guidance.js +1 -1
- package/src/cli/output-safety.js +2 -1
- package/src/cli/path-normalization.js +3 -13
- package/src/generator/api/contracts.js +497 -0
- package/src/generator/api/metadata.js +221 -0
- package/src/generator/api/openapi.js +559 -0
- package/src/generator/api/schema.js +124 -0
- package/src/generator/api/types.d.ts +98 -0
- package/src/generator/api.js +3 -1195
- package/src/generator/context/domain-page.js +1 -1
- package/src/generator/context/shared/domain-sdlc.js +282 -0
- package/src/generator/context/shared/maintained-boundary.js +665 -0
- package/src/generator/context/shared/metrics.js +85 -0
- package/src/generator/context/shared/primitives.js +64 -0
- package/src/generator/context/shared/relationships.js +453 -0
- package/src/generator/context/shared/summaries.js +263 -0
- package/src/generator/context/shared/types.d.ts +207 -0
- package/src/generator/context/shared.d.ts +42 -0
- package/src/generator/context/shared.js +80 -1390
- package/src/generator/context/slice/core.js +397 -0
- package/src/generator/context/slice/sdlc.js +417 -0
- package/src/generator/context/slice/ui-packets.js +183 -0
- package/src/generator/context/slice.js +2 -859
- package/src/generator/context/task-mode.js +2 -2
- package/src/generator/registry/index.js +507 -0
- package/src/generator/registry.js +18 -504
- package/src/generator/runtime/environment/index.js +666 -0
- package/src/generator/runtime/environment.js +4 -666
- package/src/generator/runtime/runtime-check/index.js +554 -0
- package/src/generator/runtime/runtime-check.js +4 -554
- package/src/generator/runtime/shared/index.js +572 -0
- package/src/generator/runtime/shared.js +19 -570
- package/src/generator/sdlc/doc-page.js +1 -1
- package/src/generator/shared.d.ts +2 -0
- package/src/generator/surfaces/databases/lifecycle-shared.js +1 -1
- package/src/generator/surfaces/native/swiftui-templates/README.generated.md +1 -1
- package/src/generator/surfaces/shared.d.ts +3 -0
- package/src/generator/widget-conformance/behavior-report.js +258 -0
- package/src/generator/widget-conformance/checks.js +371 -0
- package/src/generator/widget-conformance/projection-context.js +200 -0
- package/src/generator/widget-conformance/report.js +166 -0
- package/src/generator/widget-conformance/types.d.ts +121 -0
- package/src/generator/widget-conformance.js +3 -824
- package/src/import/core/context.d.ts +3 -0
- package/src/import/core/context.js +5 -7
- package/src/import/core/contracts.d.ts +1 -0
- package/src/import/core/registry.d.ts +4 -0
- package/src/import/core/runner/candidates.js +337 -0
- package/src/import/core/runner/options.js +22 -0
- package/src/import/core/runner/reports.js +51 -0
- package/src/import/core/runner/run.js +79 -0
- package/src/import/core/runner/tracks.js +150 -0
- package/src/import/core/runner/ui-drafts.js +393 -0
- package/src/import/core/runner.js +3 -698
- package/src/import/core/shared/api-routes.js +221 -0
- package/src/import/core/shared/candidates.js +97 -0
- package/src/import/core/shared/files.js +177 -0
- package/src/import/core/shared/next-app.js +389 -0
- package/src/import/core/shared/types.d.ts +51 -0
- package/src/import/core/shared/ui-routes.js +230 -0
- package/src/import/core/shared.js +60 -861
- package/src/new-project/constants.js +128 -0
- package/src/new-project/create.js +90 -0
- package/src/new-project/json.js +28 -0
- package/src/new-project/metadata.js +96 -0
- package/src/new-project/package-spec.js +161 -0
- package/src/new-project/project-files.js +351 -0
- package/src/new-project/template-policy.js +269 -0
- package/src/new-project/template-resolution.js +370 -0
- package/src/new-project/template-snapshots.js +442 -0
- package/src/new-project/template-updates.js +512 -0
- package/src/new-project/types.d.ts +83 -0
- package/src/new-project.js +6 -2277
- package/src/parser.d.ts +87 -1
- package/src/parser.js +118 -0
- package/src/policy/review-boundaries.d.ts +15 -0
- package/src/project-config/index.js +591 -0
- package/src/project-config.js +19 -561
- package/src/resolver/enrich/acceptance-criterion.js +2 -0
- package/src/resolver/enrich/bug.js +2 -0
- package/src/resolver/enrich/pitch.js +2 -0
- package/src/resolver/enrich/requirement.js +2 -0
- package/src/resolver/enrich/task.js +2 -0
- package/src/resolver/index.js +19 -2089
- package/src/resolver/normalize.js +384 -1
- package/src/resolver/plans.js +168 -0
- package/src/resolver/projections-api.js +494 -0
- package/src/resolver/projections-db.js +133 -0
- package/src/resolver/projections-ui.js +317 -0
- package/src/resolver/shapes.js +251 -0
- package/src/resolver/shared.js +278 -0
- package/src/resolver/widgets.js +132 -0
- package/src/sdlc/adopt.js +6 -5
- package/src/sdlc/paths.js +3 -5
- package/src/sdlc/scaffold.js +2 -1
- package/src/template-trust/constants.js +62 -0
- package/src/template-trust/content.js +258 -0
- package/src/template-trust/diff.js +92 -0
- package/src/template-trust/policy.js +61 -0
- package/src/template-trust/record.js +90 -0
- package/src/template-trust/status.js +182 -0
- package/src/template-trust.js +24 -687
- package/src/text-helpers.d.ts +1 -0
- package/src/topogram-types.d.ts +69 -0
- package/src/validator/common.js +488 -0
- package/src/validator/data-model.js +237 -0
- package/src/validator/docs.js +167 -0
- package/src/validator/expressions.js +146 -1
- package/src/validator/index.d.ts +23 -0
- package/src/validator/index.js +32 -3585
- package/src/validator/kinds.d.ts +41 -0
- package/src/validator/kinds.js +2 -0
- package/src/validator/model-helpers.js +46 -0
- package/src/validator/per-kind/acceptance-criterion.js +5 -0
- package/src/validator/per-kind/bug.js +6 -0
- package/src/validator/per-kind/domain.js +15 -2
- package/src/validator/per-kind/pitch.js +7 -0
- package/src/validator/per-kind/requirement.js +5 -0
- package/src/validator/per-kind/task.js +7 -0
- package/src/validator/per-kind/widget.js +14 -0
- package/src/validator/projections/api-http-async.js +410 -0
- package/src/validator/projections/api-http-authz.js +88 -0
- package/src/validator/projections/api-http-core.js +205 -0
- package/src/validator/projections/api-http-policies.js +339 -0
- package/src/validator/projections/api-http-responses.js +233 -0
- package/src/validator/projections/api-http.js +44 -0
- package/src/validator/projections/db.js +353 -0
- package/src/validator/projections/generator-defaults.js +45 -0
- package/src/validator/projections/helpers.js +87 -0
- package/src/validator/projections/ui-helpers.js +214 -0
- package/src/validator/projections/ui-navigation.js +344 -0
- package/src/validator/projections/ui-structure.js +364 -0
- package/src/validator/projections/ui-widgets.js +493 -0
- package/src/validator/projections/ui.js +46 -0
- package/src/validator/registry.js +48 -1
- package/src/validator/utils.d.ts +20 -0
- package/src/validator/utils.js +115 -12
- package/src/widget-behavior.d.ts +1 -0
- package/src/workflows/import-app/api/collect.js +221 -0
- package/src/workflows/import-app/api/openapi.js +257 -0
- package/src/workflows/import-app/api/routes.js +327 -0
- package/src/workflows/import-app/api/sources.js +22 -0
- package/src/workflows/import-app/api.js +2 -797
- package/src/workflows/reconcile/adoption-plan/build.js +212 -0
- package/src/workflows/reconcile/adoption-plan/dependencies.js +75 -0
- package/src/workflows/reconcile/adoption-plan/outputs.js +153 -0
- package/src/workflows/reconcile/adoption-plan/paths.js +58 -0
- package/src/workflows/reconcile/adoption-plan/projection-patches.js +177 -0
- package/src/workflows/reconcile/adoption-plan/reasons.js +107 -0
- package/src/workflows/reconcile/adoption-plan.js +30 -740
- package/src/workflows/reconcile/auth/closures.js +115 -0
- package/src/workflows/reconcile/auth/formatters.js +142 -0
- package/src/workflows/reconcile/auth/inference.js +330 -0
- package/src/workflows/reconcile/auth/roles.js +122 -0
- package/src/workflows/reconcile/auth.js +35 -690
- package/src/workflows/reconcile/bundle-core/index.js +600 -0
- package/src/workflows/reconcile/bundle-core.js +12 -598
- package/src/workflows/reconcile/candidate-model.js +18 -2
- package/src/workflows/reconcile/canonical-surface.js +1 -1
- package/src/workflows/reconcile/impacts/adoption-plan.js +196 -0
- package/src/workflows/reconcile/impacts/indexes.js +105 -0
- package/src/workflows/reconcile/impacts/patches.js +252 -0
- package/src/workflows/reconcile/impacts/reports.js +80 -0
- package/src/workflows/reconcile/impacts.js +14 -623
- package/src/workflows/reconcile/renderers.js +41 -6
- package/src/workflows/shared.js +5 -11
- package/src/workspace-docs.d.ts +29 -0
- package/src/workspace-paths.js +328 -0
|
@@ -1,666 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
generateNativeBundle,
|
|
6
|
-
generateServerBundle,
|
|
7
|
-
generateWebBundle,
|
|
8
|
-
dbEnvVarsForComponent,
|
|
9
|
-
getDefaultEnvironmentProjections,
|
|
10
|
-
resolveRuntimeTopology,
|
|
11
|
-
runtimeDemoUserId,
|
|
12
|
-
runtimePorts,
|
|
13
|
-
runtimeUrls
|
|
14
|
-
} from "./shared.js";
|
|
15
|
-
import { mergeNamedBundles, renderEnvAwareShellScript, renderLoadEnvScript, renderRootShellScript } from "./bundle-shared.js";
|
|
16
|
-
import { generatorProfile as manifestGeneratorProfile } from "../registry.js";
|
|
17
|
-
|
|
18
|
-
function projectionHintProfile(projection, fallback) {
|
|
19
|
-
if (!projection) {
|
|
20
|
-
return fallback;
|
|
21
|
-
}
|
|
22
|
-
for (const entry of projection.generatorDefaults || []) {
|
|
23
|
-
if (entry.key === "profile" && entry.value != null) {
|
|
24
|
-
return entry.value;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return fallback;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function runtimeReferenceFor(graph, options = {}) {
|
|
31
|
-
try {
|
|
32
|
-
return getExampleImplementation(graph, options).runtime.reference;
|
|
33
|
-
} catch {
|
|
34
|
-
return {
|
|
35
|
-
environment: { name: "Topogram Runtime", databaseName: "topogram_app", envExample: "" },
|
|
36
|
-
ports: { server: 3000, web: 5173 },
|
|
37
|
-
demoEnv: { userId: "11111111-1111-4111-8111-111111111111" }
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function buildEnvironmentPlan(graph, options = {}) {
|
|
43
|
-
const runtimeReference = runtimeReferenceFor(graph, options);
|
|
44
|
-
const topology = resolveRuntimeTopology(graph, options);
|
|
45
|
-
const { apiProjection, uiProjection, dbProjection } = getDefaultEnvironmentProjections(graph, options);
|
|
46
|
-
const dbLifecycle = dbProjection ? generateDbLifecyclePlan(graph, { ...options, projectionId: dbProjection.id }) : null;
|
|
47
|
-
const dbEngine = dbLifecycle?.engine || null;
|
|
48
|
-
const profile = options.profileId || (dbEngine === "sqlite" || !dbProjection ? "local_process" : "local_docker");
|
|
49
|
-
const usesDocker = profile === "local_docker";
|
|
50
|
-
const webProfile = manifestGeneratorProfile(topology.primaryWeb?.generator?.id, null) || projectionHintProfile(uiProjection, "sveltekit");
|
|
51
|
-
const isSqlite = dbEngine === "sqlite";
|
|
52
|
-
const ports = runtimePorts(runtimeReference, topology);
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
type: "environment_plan",
|
|
56
|
-
environment: {
|
|
57
|
-
id: [apiProjection?.id, uiProjection?.id, dbProjection?.id].filter(Boolean).join("__") || "topogram_runtime",
|
|
58
|
-
name: runtimeReference.environment.name,
|
|
59
|
-
mode: "local_dev",
|
|
60
|
-
profile
|
|
61
|
-
},
|
|
62
|
-
topology: {
|
|
63
|
-
runtimes: topology.runtimes.map((runtime) => ({
|
|
64
|
-
id: runtime.id,
|
|
65
|
-
kind: runtime.kind,
|
|
66
|
-
projection: runtime.projection.id,
|
|
67
|
-
generator: runtime.generator,
|
|
68
|
-
port: runtime.port ?? null,
|
|
69
|
-
uses_api: runtime.api || null,
|
|
70
|
-
uses_database: runtime.database || null
|
|
71
|
-
}))
|
|
72
|
-
},
|
|
73
|
-
projections: {
|
|
74
|
-
api: {
|
|
75
|
-
id: apiProjection?.id || null,
|
|
76
|
-
type: apiProjection?.type || null
|
|
77
|
-
},
|
|
78
|
-
ui: {
|
|
79
|
-
id: uiProjection?.id || null,
|
|
80
|
-
type: uiProjection?.type || null
|
|
81
|
-
},
|
|
82
|
-
db: {
|
|
83
|
-
id: dbProjection?.id || null,
|
|
84
|
-
type: dbProjection?.type || null,
|
|
85
|
-
engine: dbEngine,
|
|
86
|
-
profile: dbProjection?.generatorDefaults?.profile || dbProjection?.profile || null
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
generators: {
|
|
90
|
-
server: "hono-server",
|
|
91
|
-
web: "sveltekit-app",
|
|
92
|
-
dbLifecycle: "db-lifecycle-bundle"
|
|
93
|
-
},
|
|
94
|
-
runtimeProfiles: {
|
|
95
|
-
server: "hono",
|
|
96
|
-
web: webProfile,
|
|
97
|
-
orm: dbLifecycle?.runtimeProfile || null,
|
|
98
|
-
database: dbLifecycle?.dbProfile || null
|
|
99
|
-
},
|
|
100
|
-
orchestration: {
|
|
101
|
-
usesDocker,
|
|
102
|
-
database: !dbProjection
|
|
103
|
-
? "none"
|
|
104
|
-
: isSqlite
|
|
105
|
-
? "local_process_sqlite"
|
|
106
|
-
: usesDocker
|
|
107
|
-
? "docker_postgres"
|
|
108
|
-
: "local_process_postgres"
|
|
109
|
-
},
|
|
110
|
-
directories: {
|
|
111
|
-
server: topology.primaryApi ? topology.serviceDir(topology.primaryApi) : null,
|
|
112
|
-
web: topology.primaryWeb ? topology.webDir(topology.primaryWeb) : null,
|
|
113
|
-
db: topology.primaryDb ? topology.dbDir(topology.primaryDb) : null,
|
|
114
|
-
native: topology.nativeRuntimes[0] ? topology.nativeDir(topology.nativeRuntimes[0]) : null,
|
|
115
|
-
scripts: "scripts"
|
|
116
|
-
},
|
|
117
|
-
runtimes: {
|
|
118
|
-
apis: topology.apiRuntimes.map((component) => ({
|
|
119
|
-
id: component.id,
|
|
120
|
-
projection: component.projection.id,
|
|
121
|
-
port: component.port || ports.server,
|
|
122
|
-
dir: topology.serviceDir(component),
|
|
123
|
-
uses_database: component.database,
|
|
124
|
-
databaseEnv: component.databaseRuntime
|
|
125
|
-
? dbEnvVarsForComponent(component.databaseRuntime, { primary: component.databaseRuntime?.id === topology.primaryDb?.id })
|
|
126
|
-
: null
|
|
127
|
-
})),
|
|
128
|
-
webs: topology.webRuntimes.map((component) => ({
|
|
129
|
-
id: component.id,
|
|
130
|
-
projection: component.projection.id,
|
|
131
|
-
port: component.port || ports.web,
|
|
132
|
-
dir: topology.webDir(component),
|
|
133
|
-
uses_api: component.api
|
|
134
|
-
})),
|
|
135
|
-
natives: topology.nativeRuntimes.map((component) => ({
|
|
136
|
-
id: component.id,
|
|
137
|
-
projection: component.projection.id,
|
|
138
|
-
dir: topology.nativeDir(component),
|
|
139
|
-
uses_api: component.api
|
|
140
|
-
})),
|
|
141
|
-
databases: topology.dbRuntimes.map((component) => ({
|
|
142
|
-
id: component.id,
|
|
143
|
-
projection: component.projection.id,
|
|
144
|
-
type: component.projection.type,
|
|
145
|
-
engine: component.id === topology.primaryDb?.id ? dbEngine : null,
|
|
146
|
-
port: component.port,
|
|
147
|
-
dir: topology.dbDir(component),
|
|
148
|
-
env: dbEnvVarsForComponent(component, { primary: component.id === topology.primaryDb?.id })
|
|
149
|
-
}))
|
|
150
|
-
},
|
|
151
|
-
ports: {
|
|
152
|
-
database: !dbProjection || isSqlite ? null : topology.primaryDb?.port || 5432,
|
|
153
|
-
server: ports.server,
|
|
154
|
-
web: ports.web
|
|
155
|
-
},
|
|
156
|
-
files: {
|
|
157
|
-
rootEnv: ".env.example",
|
|
158
|
-
dockerCompose: usesDocker ? "docker-compose.yml" : null,
|
|
159
|
-
readme: "README.md",
|
|
160
|
-
packageJson: "package.json"
|
|
161
|
-
},
|
|
162
|
-
commands: {
|
|
163
|
-
bootstrapDb: "./scripts/bootstrap-db.sh",
|
|
164
|
-
dev: "./scripts/stack-dev.sh",
|
|
165
|
-
dockerDb: usesDocker ? "./scripts/docker-db.sh" : null,
|
|
166
|
-
dockerStack: usesDocker ? "./scripts/docker-stack.sh" : null
|
|
167
|
-
},
|
|
168
|
-
runtimeReference
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function renderEnvironmentEnvExample(plan) {
|
|
173
|
-
const demoUserId = runtimeDemoUserId(plan.runtimeReference);
|
|
174
|
-
const databaseName = plan.runtimeReference.environment.databaseName || "topogram_app";
|
|
175
|
-
const urls = runtimeUrls(plan.runtimeReference, {
|
|
176
|
-
primaryApi: { port: plan.ports.server },
|
|
177
|
-
primaryWeb: { port: plan.ports.web }
|
|
178
|
-
});
|
|
179
|
-
const extraDatabaseLines = plan.runtimes.databases
|
|
180
|
-
.filter((component) => component.id !== plan.runtimes.databases[0]?.id)
|
|
181
|
-
.map((component) => {
|
|
182
|
-
const fallbackName = `${databaseName}_${component.id}`;
|
|
183
|
-
if (component.engine === "sqlite") {
|
|
184
|
-
return `${component.env.databaseUrl}=file:./var/${component.id}.sqlite`;
|
|
185
|
-
}
|
|
186
|
-
return [
|
|
187
|
-
`${component.env.dbPort}=${component.port || 5432}`,
|
|
188
|
-
`${component.env.postgresDb}=${fallbackName}`,
|
|
189
|
-
`${component.env.databaseUrl}=postgresql://\${POSTGRES_USER}@localhost:${component.port || 5432}/${fallbackName}?schema=public`,
|
|
190
|
-
`${component.env.databaseAdminUrl}=postgresql://\${POSTGRES_USER}@localhost:${component.port || 5432}/postgres`
|
|
191
|
-
].join("\n");
|
|
192
|
-
})
|
|
193
|
-
.filter(Boolean)
|
|
194
|
-
.join("\n");
|
|
195
|
-
const commonLines = `# Environment profile
|
|
196
|
-
TOPOGRAM_ENVIRONMENT_PROFILE=${plan.environment.profile}
|
|
197
|
-
|
|
198
|
-
# Local stack ports
|
|
199
|
-
${plan.runtimes.apis.length ? `SERVER_PORT=${plan.ports.server}\n` : ""}${plan.runtimes.webs.length ? `WEB_PORT=${plan.ports.web}\n` : ""}${plan.runtimes.webs.length && plan.runtimes.apis.length ? `PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}\n` : ""}${plan.runtimes.webs.length ? `TOPOGRAM_CORS_ORIGINS=${urls.web},http://127.0.0.1:${plan.ports.web}\n` : ""}PUBLIC_TOPOGRAM_DEMO_USER_ID=${demoUserId}
|
|
200
|
-
TOPOGRAM_DEMO_USER_ID=${demoUserId}
|
|
201
|
-
${plan.runtimeReference.environment.envExample || ""}
|
|
202
|
-
TOPOGRAM_SEED_DEMO=true
|
|
203
|
-
`;
|
|
204
|
-
if (!plan.projections.db.type) {
|
|
205
|
-
return commonLines;
|
|
206
|
-
}
|
|
207
|
-
if (plan.projections.db.engine === "sqlite") {
|
|
208
|
-
return `# Environment profile
|
|
209
|
-
TOPOGRAM_ENVIRONMENT_PROFILE=${plan.environment.profile}
|
|
210
|
-
|
|
211
|
-
# Local stack ports
|
|
212
|
-
SERVER_PORT=${plan.ports.server}
|
|
213
|
-
WEB_PORT=${plan.ports.web}
|
|
214
|
-
|
|
215
|
-
# Local SQLite defaults
|
|
216
|
-
DATABASE_URL=file:./var/${databaseName}.sqlite
|
|
217
|
-
${extraDatabaseLines ? `${extraDatabaseLines}\n` : ""}PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}
|
|
218
|
-
TOPOGRAM_CORS_ORIGINS=${urls.web},http://127.0.0.1:${plan.ports.web}
|
|
219
|
-
PUBLIC_TOPOGRAM_DEMO_USER_ID=${demoUserId}
|
|
220
|
-
TOPOGRAM_DEMO_USER_ID=${demoUserId}
|
|
221
|
-
${plan.runtimeReference.environment.envExample || ""}
|
|
222
|
-
TOPOGRAM_SEED_DEMO=true
|
|
223
|
-
`;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return `# Environment profile
|
|
227
|
-
TOPOGRAM_ENVIRONMENT_PROFILE=${plan.environment.profile}
|
|
228
|
-
|
|
229
|
-
# Local stack ports
|
|
230
|
-
DB_PORT=${plan.ports.database || 5432}
|
|
231
|
-
SERVER_PORT=${plan.ports.server}
|
|
232
|
-
WEB_PORT=${plan.ports.web}
|
|
233
|
-
|
|
234
|
-
# Local Postgres defaults
|
|
235
|
-
POSTGRES_DB=${databaseName}
|
|
236
|
-
POSTGRES_USER=\${USER:-postgres}
|
|
237
|
-
POSTGRES_PASSWORD=postgres
|
|
238
|
-
|
|
239
|
-
# Local shell/runtime defaults
|
|
240
|
-
DATABASE_URL=postgresql://\${POSTGRES_USER}@localhost:${plan.ports.database || 5432}/${databaseName}?schema=public
|
|
241
|
-
DATABASE_ADMIN_URL=postgresql://\${POSTGRES_USER}@localhost:${plan.ports.database || 5432}/postgres
|
|
242
|
-
${extraDatabaseLines ? `${extraDatabaseLines}\n` : ""}PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}
|
|
243
|
-
TOPOGRAM_CORS_ORIGINS=${urls.web},http://127.0.0.1:${plan.ports.web}
|
|
244
|
-
PUBLIC_TOPOGRAM_DEMO_USER_ID=${demoUserId}
|
|
245
|
-
TOPOGRAM_DEMO_USER_ID=${demoUserId}
|
|
246
|
-
${plan.runtimeReference.environment.envExample || ""}
|
|
247
|
-
TOPOGRAM_SEED_DEMO=true
|
|
248
|
-
`;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
function renderEnvironmentPackageJson(plan) {
|
|
252
|
-
const scripts = {
|
|
253
|
-
"db:bootstrap": "bash ./scripts/bootstrap-db.sh",
|
|
254
|
-
"dev:server": "bash ./scripts/server-dev.sh",
|
|
255
|
-
"dev:web": "bash ./scripts/web-dev.sh",
|
|
256
|
-
dev: "bash ./scripts/stack-dev.sh"
|
|
257
|
-
};
|
|
258
|
-
if (plan.orchestration.usesDocker) {
|
|
259
|
-
scripts["docker:db"] = "bash ./scripts/docker-db.sh";
|
|
260
|
-
scripts["docker:stack"] = "bash ./scripts/docker-stack.sh";
|
|
261
|
-
}
|
|
262
|
-
return `${JSON.stringify({
|
|
263
|
-
name: "topogram-runtime-stack",
|
|
264
|
-
private: true,
|
|
265
|
-
scripts
|
|
266
|
-
}, null, 2)}\n`;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function apiDatabaseExportLines(component) {
|
|
270
|
-
if (!component?.databaseEnv) {
|
|
271
|
-
return [];
|
|
272
|
-
}
|
|
273
|
-
const env = component.databaseEnv;
|
|
274
|
-
return [
|
|
275
|
-
`if [[ -n "\${${env.databaseUrl}:-}" ]]; then export DATABASE_URL="\${${env.databaseUrl}}"; fi`,
|
|
276
|
-
`if [[ -n "\${${env.databaseAdminUrl}:-}" ]]; then export DATABASE_ADMIN_URL="\${${env.databaseAdminUrl}}"; fi`
|
|
277
|
-
];
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function renderEnvironmentReadme(plan) {
|
|
281
|
-
const hasDb = plan.runtimes.databases.length > 0;
|
|
282
|
-
const hasApi = plan.runtimes.apis.length > 0;
|
|
283
|
-
const hasWeb = plan.runtimes.webs.length > 0;
|
|
284
|
-
const hasNative = plan.runtimes.natives.length > 0;
|
|
285
|
-
const localProcessNotes = !hasDb
|
|
286
|
-
? "- This bundle has no generated database surface."
|
|
287
|
-
: plan.projections.db.engine === "sqlite"
|
|
288
|
-
? "- SQLite is file-backed for this bundle; no separate DB server is required."
|
|
289
|
-
: `- 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.`;
|
|
290
|
-
const dockerSection = plan.orchestration.usesDocker
|
|
291
|
-
? `## Alternative Docker Workflow
|
|
292
|
-
|
|
293
|
-
- Start only the database: \`${plan.commands.dockerDb}\`
|
|
294
|
-
- Start the database, server, and web app in containers: \`${plan.commands.dockerStack}\`
|
|
295
|
-
`
|
|
296
|
-
: `## Local Process Notes
|
|
297
|
-
|
|
298
|
-
- Install Node.js and npm locally before using this bundle.
|
|
299
|
-
${localProcessNotes}
|
|
300
|
-
`;
|
|
301
|
-
|
|
302
|
-
return `# ${plan.environment.name}
|
|
303
|
-
|
|
304
|
-
This bundle packages the generated runtime into one local environment:
|
|
305
|
-
|
|
306
|
-
${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` : ""}${hasNative ? "- `native/<native-id>/`: generated native app scaffolds\n" : ""}${hasDb ? "- `db/<db-id>/`: generated DB lifecycle bundles\n" : ""}${plan.files.dockerCompose ? `- \`${plan.files.dockerCompose}\`: local Postgres container` : hasDb ? (plan.projections.db.engine === "sqlite" ? "- local SQLite file orchestration (no Docker files generated)" : "- local-process Postgres orchestration (no Docker files generated)") : "- no DB orchestration is generated"}
|
|
307
|
-
|
|
308
|
-
## Quick Start
|
|
309
|
-
|
|
310
|
-
1. Copy \`.env.example\` to \`.env\` if you want to customize defaults
|
|
311
|
-
2. Start the database:
|
|
312
|
-
- ${!hasDb ? "not applicable" : plan.projections.db.engine === "sqlite" ? "no separate DB service is required" : plan.orchestration.usesDocker ? `\`${plan.commands.dockerDb}\`` : "use your local Postgres service"}
|
|
313
|
-
3. Bootstrap or migrate the database:
|
|
314
|
-
- \`${plan.commands.bootstrapDb}\`
|
|
315
|
-
4. Start the stack:
|
|
316
|
-
- \`${plan.commands.dev}\`
|
|
317
|
-
|
|
318
|
-
## Demo Seed Data
|
|
319
|
-
|
|
320
|
-
- Bootstrap seeds demo data by default
|
|
321
|
-
- Set \`TOPOGRAM_SEED_DEMO=false\` to skip demo seeding
|
|
322
|
-
- Default seeded IDs come from \`.env.example\`
|
|
323
|
-
|
|
324
|
-
${dockerSection}
|
|
325
|
-
|
|
326
|
-
## Notes
|
|
327
|
-
|
|
328
|
-
- ${hasApi && hasDb ? `The generated server expects ${plan.projections.db.engine === "sqlite" ? "SQLite plus Prisma." : "Postgres plus Prisma."}` : hasApi ? "The generated server is stateless." : "No server surface is generated."}
|
|
329
|
-
- ${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."}
|
|
330
|
-
- ${hasNative ? "Native app scaffolds use the same UI surface contracts as web surfaces." : "No native surface is generated."}
|
|
331
|
-
- If \`.env\` is missing, generated scripts fall back to \`.env.example\`.
|
|
332
|
-
- The DB lifecycle scripts remain the source of truth for greenfield bootstrap and brownfield migration.
|
|
333
|
-
`;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
function renderEnvironmentLoadEnvScript() {
|
|
337
|
-
return renderLoadEnvScript({ searchParentEnv: true });
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function renderEnvironmentBootstrapDbScript(plan) {
|
|
341
|
-
const dbBootstrapLines = plan.runtimes.databases.map((component) => {
|
|
342
|
-
const env = component.env;
|
|
343
|
-
const runtimeApi = plan.runtimes.apis.find((apiRuntime) => apiRuntime.uses_database === component.id);
|
|
344
|
-
const assignments = [
|
|
345
|
-
`DATABASE_URL="\${${env.databaseUrl}:-}"`,
|
|
346
|
-
`DATABASE_ADMIN_URL="\${${env.databaseAdminUrl}:-}"`,
|
|
347
|
-
runtimeApi ? `TOPOGRAM_RUNTIME_SERVER_DIR="$ROOT_DIR/${runtimeApi.dir}"` : null
|
|
348
|
-
].filter(Boolean).join(" ");
|
|
349
|
-
return `(cd "$ROOT_DIR/${component.dir}" && TOPOGRAM_ENV_FILE=/dev/null ${assignments} bash ./scripts/db-bootstrap-or-migrate.sh)`;
|
|
350
|
-
});
|
|
351
|
-
const primaryApi = plan.runtimes.apis[0];
|
|
352
|
-
if (plan.runtimes.databases.length === 0) {
|
|
353
|
-
return renderEnvAwareShellScript([
|
|
354
|
-
'echo "No database runtimes are configured; skipping DB bootstrap."'
|
|
355
|
-
]);
|
|
356
|
-
}
|
|
357
|
-
return renderEnvAwareShellScript([
|
|
358
|
-
`if [[ "\${TOPOGRAM_ENVIRONMENT_PROFILE:-${plan.environment.profile}}" == "local_docker" ]]; then`,
|
|
359
|
-
" if ! command -v docker >/dev/null 2>&1; then",
|
|
360
|
-
' echo "Docker is required for the local_docker profile, but it is not installed." >&2',
|
|
361
|
-
' echo "Set TOPOGRAM_ENVIRONMENT_PROFILE=local_process and point DATABASE_URL at a working local database, or install Docker." >&2',
|
|
362
|
-
" exit 1",
|
|
363
|
-
" fi",
|
|
364
|
-
' docker compose -f "$ROOT_DIR/docker-compose.yml" up -d db',
|
|
365
|
-
"fi",
|
|
366
|
-
...dbBootstrapLines,
|
|
367
|
-
'if [[ "${TOPOGRAM_SEED_DEMO:-true}" != "false" ]]; then',
|
|
368
|
-
...apiDatabaseExportLines(primaryApi),
|
|
369
|
-
...(primaryApi?.uses_database
|
|
370
|
-
? [`(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)`]
|
|
371
|
-
: ['echo "No DB-backed API component is configured; skipping demo seed."']),
|
|
372
|
-
"fi"
|
|
373
|
-
]);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
function componentScriptOptions() {
|
|
377
|
-
return {
|
|
378
|
-
rootDirExpression: 'ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"',
|
|
379
|
-
loadEnvScript: '"$ROOT_DIR/scripts/load-env.sh"'
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
function runtimePortExpression(plan, runtimes, component, sharedEnvName) {
|
|
384
|
-
const runtimeEnvName = `${component.id.toUpperCase()}_PORT`;
|
|
385
|
-
const primaryRuntime = runtimes[0];
|
|
386
|
-
const fallback = primaryRuntime?.id === component.id
|
|
387
|
-
? `\${${sharedEnvName}:-${component.port}}`
|
|
388
|
-
: `${component.port}`;
|
|
389
|
-
return `\${${runtimeEnvName}:-${fallback}}`;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
function renderEnvironmentServerDevScript(plan, component = plan.runtimes.apis[0], options = {}) {
|
|
393
|
-
if (!component) {
|
|
394
|
-
return renderEnvAwareShellScript(['echo "No API runtimes are configured."']);
|
|
395
|
-
}
|
|
396
|
-
const guardPortsScript = options.componentScript ? '"$ROOT_DIR/scripts/guard-ports.mjs"' : '"$SCRIPT_DIR/guard-ports.mjs"';
|
|
397
|
-
const serverPortExpression = runtimePortExpression(plan, plan.runtimes.apis, component, "SERVER_PORT");
|
|
398
|
-
return renderEnvAwareShellScript([
|
|
399
|
-
`node ${guardPortsScript} api`,
|
|
400
|
-
"",
|
|
401
|
-
...apiDatabaseExportLines(component),
|
|
402
|
-
`export PORT="${serverPortExpression}"`,
|
|
403
|
-
`export TOPOGRAM_CORS_ORIGINS="\${TOPOGRAM_CORS_ORIGINS:-http://localhost:\${WEB_PORT:-${plan.ports.web}},http://127.0.0.1:\${WEB_PORT:-${plan.ports.web}}}"`,
|
|
404
|
-
"",
|
|
405
|
-
`cd "$ROOT_DIR/${component.dir}"`,
|
|
406
|
-
"npm install",
|
|
407
|
-
...(component.uses_database ? ["npm exec -- prisma generate --schema prisma/schema.prisma"] : []),
|
|
408
|
-
"npm run dev"
|
|
409
|
-
], options.componentScript ? componentScriptOptions() : {});
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
function renderEnvironmentWebDevScript(plan, component = plan.runtimes.webs[0], options = {}) {
|
|
413
|
-
if (!component) {
|
|
414
|
-
return renderEnvAwareShellScript(['echo "No web runtimes are configured."']);
|
|
415
|
-
}
|
|
416
|
-
const apiRuntime = plan.runtimes.apis.find((entry) => entry.id === component.uses_api) || plan.runtimes.apis[0];
|
|
417
|
-
const guardPortsScript = options.componentScript ? '"$ROOT_DIR/scripts/guard-ports.mjs"' : '"$SCRIPT_DIR/guard-ports.mjs"';
|
|
418
|
-
const webPortExpression = runtimePortExpression(plan, plan.runtimes.webs, component, "WEB_PORT");
|
|
419
|
-
return renderEnvAwareShellScript([
|
|
420
|
-
`node ${guardPortsScript} web`,
|
|
421
|
-
"",
|
|
422
|
-
...(apiRuntime ? [`export PUBLIC_TOPOGRAM_API_BASE_URL="\${PUBLIC_TOPOGRAM_API_BASE_URL:-http://localhost:\${${apiRuntime.id.toUpperCase()}_PORT:-\${SERVER_PORT:-${apiRuntime.port}}}}"`] : []),
|
|
423
|
-
`export TOPOGRAM_CORS_ORIGINS="\${TOPOGRAM_CORS_ORIGINS:-http://localhost:${webPortExpression},http://127.0.0.1:${webPortExpression}}"`,
|
|
424
|
-
"",
|
|
425
|
-
`cd "$ROOT_DIR/${component.dir}"`,
|
|
426
|
-
"npm install",
|
|
427
|
-
`npm run dev -- --host "\${WEB_HOST:-127.0.0.1}" --port "${webPortExpression}"`,
|
|
428
|
-
], options.componentScript ? componentScriptOptions() : {});
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
function renderEnvironmentStackDevScript(plan) {
|
|
432
|
-
const startLines = [
|
|
433
|
-
...plan.runtimes.apis.map((component) => `bash "$SCRIPT_DIR/services/${component.id}-dev.sh" &\nPIDS+=($!)`),
|
|
434
|
-
...plan.runtimes.webs.map((component) => `bash "$SCRIPT_DIR/web/${component.id}-dev.sh" &\nPIDS+=($!)`)
|
|
435
|
-
];
|
|
436
|
-
return `#!/usr/bin/env bash
|
|
437
|
-
set -euo pipefail
|
|
438
|
-
|
|
439
|
-
SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
|
440
|
-
PIDS=()
|
|
441
|
-
|
|
442
|
-
node "$SCRIPT_DIR/guard-ports.mjs" stack
|
|
443
|
-
|
|
444
|
-
if [[ "\${TOPOGRAM_SKIP_STACK_BOOTSTRAP:-false}" != "true" ]]; then
|
|
445
|
-
bash "$SCRIPT_DIR/bootstrap-db.sh"
|
|
446
|
-
fi
|
|
447
|
-
|
|
448
|
-
${startLines.length ? startLines.join("\n") : 'echo "No long-running dev services are configured."'}
|
|
449
|
-
|
|
450
|
-
kill_tree() {
|
|
451
|
-
local pid="$1"
|
|
452
|
-
local child
|
|
453
|
-
while IFS= read -r child; do
|
|
454
|
-
[[ -n "$child" ]] && kill_tree "$child"
|
|
455
|
-
done < <(pgrep -P "$pid" 2>/dev/null || true)
|
|
456
|
-
kill "$pid" >/dev/null 2>&1 || true
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
cleanup() {
|
|
460
|
-
if [[ "\${#PIDS[@]}" -gt 0 ]]; then
|
|
461
|
-
for pid in "\${PIDS[@]}"; do
|
|
462
|
-
kill_tree "$pid"
|
|
463
|
-
done
|
|
464
|
-
fi
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
trap cleanup EXIT INT TERM
|
|
468
|
-
${startLines.length ? "wait" : ""}
|
|
469
|
-
`;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
function renderEnvironmentGuardPortsScript(plan) {
|
|
473
|
-
const ports = [
|
|
474
|
-
...plan.runtimes.apis.map((component, index) => ({ id: component.id, type: "api", env: `${component.id.toUpperCase()}_PORT`, fallbackEnv: index === 0 ? "SERVER_PORT" : null, port: component.port })),
|
|
475
|
-
...plan.runtimes.webs.map((component, index) => ({ id: component.id, type: "web", env: `${component.id.toUpperCase()}_PORT`, fallbackEnv: index === 0 ? "WEB_PORT" : null, port: component.port }))
|
|
476
|
-
];
|
|
477
|
-
return `#!/usr/bin/env node
|
|
478
|
-
import net from "node:net";
|
|
479
|
-
|
|
480
|
-
const role = process.argv[2] || "stack";
|
|
481
|
-
const ports = ${JSON.stringify(ports, null, 2)};
|
|
482
|
-
const expectedService = ${JSON.stringify(plan.runtimeReference.serviceName || "")};
|
|
483
|
-
|
|
484
|
-
function effectivePort(entry) {
|
|
485
|
-
return Number(process.env[entry.env] || (entry.fallbackEnv ? process.env[entry.fallbackEnv] : "") || entry.port);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
function portInUse(port) {
|
|
489
|
-
return new Promise((resolve) => {
|
|
490
|
-
const server = net.createServer();
|
|
491
|
-
server.once("error", (error) => {
|
|
492
|
-
resolve(Boolean(error && error.code === "EADDRINUSE"));
|
|
493
|
-
});
|
|
494
|
-
server.once("listening", () => {
|
|
495
|
-
server.close(() => resolve(false));
|
|
496
|
-
});
|
|
497
|
-
server.listen(port, "127.0.0.1");
|
|
498
|
-
});
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
async function readHealth(port) {
|
|
502
|
-
try {
|
|
503
|
-
const response = await fetch(\`http://127.0.0.1:\${port}/health\`);
|
|
504
|
-
const body = await response.json().catch(() => null);
|
|
505
|
-
return { status: response.status, body };
|
|
506
|
-
} catch {
|
|
507
|
-
return null;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
async function failForServerPort(port) {
|
|
512
|
-
const health = await readHealth(port);
|
|
513
|
-
if (health?.body?.service && expectedService && health.body.service !== expectedService) {
|
|
514
|
-
console.error(\`Port \${port} is already serving \${health.body.service}, not \${expectedService}.\`);
|
|
515
|
-
console.error("Stop the other stack or override SERVER_PORT/PUBLIC_TOPOGRAM_API_BASE_URL before retrying.");
|
|
516
|
-
process.exit(1);
|
|
517
|
-
}
|
|
518
|
-
if (health?.body?.service) {
|
|
519
|
-
console.error(\`Port \${port} is already in use by \${health.body.service}.\`);
|
|
520
|
-
} else {
|
|
521
|
-
console.error(\`Port \${port} is already in use.\`);
|
|
522
|
-
}
|
|
523
|
-
process.exit(1);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
async function failForWebPort(port) {
|
|
527
|
-
console.error(\`Port \${port} is already in use.\`);
|
|
528
|
-
console.error("Stop the other web dev server or override WEB_PORT before retrying.");
|
|
529
|
-
process.exit(1);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
for (const entry of ports) {
|
|
533
|
-
if (role !== "stack" && role !== entry.type) {
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
|
-
const port = effectivePort(entry);
|
|
537
|
-
if (await portInUse(port)) {
|
|
538
|
-
if (entry.type === "api") {
|
|
539
|
-
await failForServerPort(port);
|
|
540
|
-
}
|
|
541
|
-
await failForWebPort(port);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
`;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
function renderEnvironmentDockerDbScript() {
|
|
548
|
-
return renderRootShellScript(['docker compose -f "$ROOT_DIR/docker-compose.yml" up -d db']);
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
function renderEnvironmentDockerStackScript() {
|
|
552
|
-
return renderRootShellScript(['docker compose -f "$ROOT_DIR/docker-compose.yml" up --build']);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function renderEnvironmentDockerCompose(plan) {
|
|
556
|
-
return `services:
|
|
557
|
-
db:
|
|
558
|
-
image: postgres:16-alpine
|
|
559
|
-
restart: unless-stopped
|
|
560
|
-
environment:
|
|
561
|
-
POSTGRES_DB: \${POSTGRES_DB:-${plan.runtimeReference.environment.databaseName || "topogram_app"}}
|
|
562
|
-
POSTGRES_USER: \${POSTGRES_USER:-postgres}
|
|
563
|
-
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-postgres}
|
|
564
|
-
ports:
|
|
565
|
-
- "\${DB_PORT:-5432}:5432"
|
|
566
|
-
volumes:
|
|
567
|
-
- postgres-data:/var/lib/postgresql/data
|
|
568
|
-
|
|
569
|
-
server:
|
|
570
|
-
image: node:22-alpine
|
|
571
|
-
working_dir: /app
|
|
572
|
-
depends_on:
|
|
573
|
-
- db
|
|
574
|
-
environment:
|
|
575
|
-
DATABASE_URL: postgresql://\${POSTGRES_USER:-postgres}:\${POSTGRES_PASSWORD:-postgres}@db:5432/\${POSTGRES_DB:-${plan.runtimeReference.environment.databaseName || "topogram_app"}}?schema=public
|
|
576
|
-
PORT: \${SERVER_PORT:-${plan.ports.server}}
|
|
577
|
-
TOPOGRAM_CORS_ORIGINS: http://localhost:\${WEB_PORT:-${plan.ports.web}},http://127.0.0.1:\${WEB_PORT:-${plan.ports.web}}
|
|
578
|
-
ports:
|
|
579
|
-
- "127.0.0.1:\${SERVER_PORT:-${plan.ports.server}}:\${SERVER_PORT:-${plan.ports.server}}"
|
|
580
|
-
volumes:
|
|
581
|
-
- ./${plan.directories.server}:/app
|
|
582
|
-
command: >
|
|
583
|
-
sh -lc "npm install &&
|
|
584
|
-
npm exec -- prisma generate --schema prisma/schema.prisma &&
|
|
585
|
-
npm run dev"
|
|
586
|
-
|
|
587
|
-
web:
|
|
588
|
-
image: node:22-alpine
|
|
589
|
-
working_dir: /app
|
|
590
|
-
depends_on:
|
|
591
|
-
- server
|
|
592
|
-
environment:
|
|
593
|
-
PUBLIC_TOPOGRAM_API_BASE_URL: http://localhost:\${SERVER_PORT:-${plan.ports.server}}
|
|
594
|
-
ports:
|
|
595
|
-
- "127.0.0.1:\${WEB_PORT:-${plan.ports.web}}:\${WEB_PORT:-${plan.ports.web}}"
|
|
596
|
-
volumes:
|
|
597
|
-
- ./${plan.directories.web}:/app
|
|
598
|
-
command: >
|
|
599
|
-
sh -lc "npm install &&
|
|
600
|
-
npm run dev -- --host 0.0.0.0 --port \${WEB_PORT:-${plan.ports.web}}"
|
|
601
|
-
|
|
602
|
-
volumes:
|
|
603
|
-
postgres-data:
|
|
604
|
-
`;
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
export function generateEnvironmentBundle(graph, options = {}) {
|
|
608
|
-
const plan = buildEnvironmentPlan(graph, options);
|
|
609
|
-
const topology = resolveRuntimeTopology(graph, options);
|
|
610
|
-
const files = {
|
|
611
|
-
".env.example": renderEnvironmentEnvExample(plan),
|
|
612
|
-
".gitignore": "node_modules/\n.env\npostgres-data/\n",
|
|
613
|
-
"README.md": renderEnvironmentReadme(plan),
|
|
614
|
-
"package.json": renderEnvironmentPackageJson(plan),
|
|
615
|
-
"scripts/load-env.sh": renderEnvironmentLoadEnvScript(),
|
|
616
|
-
"scripts/bootstrap-db.sh": renderEnvironmentBootstrapDbScript(plan),
|
|
617
|
-
"scripts/server-dev.sh": renderEnvironmentServerDevScript(plan),
|
|
618
|
-
"scripts/web-dev.sh": renderEnvironmentWebDevScript(plan),
|
|
619
|
-
"scripts/stack-dev.sh": renderEnvironmentStackDevScript(plan),
|
|
620
|
-
"scripts/guard-ports.mjs": renderEnvironmentGuardPortsScript(plan)
|
|
621
|
-
};
|
|
622
|
-
|
|
623
|
-
for (const component of plan.runtimes.apis) {
|
|
624
|
-
files[`scripts/services/${component.id}-dev.sh`] = renderEnvironmentServerDevScript(plan, component, { componentScript: true });
|
|
625
|
-
}
|
|
626
|
-
for (const component of plan.runtimes.webs) {
|
|
627
|
-
files[`scripts/web/${component.id}-dev.sh`] = renderEnvironmentWebDevScript(plan, component, { componentScript: true });
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
if (plan.orchestration.usesDocker) {
|
|
631
|
-
files["docker-compose.yml"] = renderEnvironmentDockerCompose(plan);
|
|
632
|
-
files["scripts/docker-db.sh"] = renderEnvironmentDockerDbScript();
|
|
633
|
-
files["scripts/docker-stack.sh"] = renderEnvironmentDockerStackScript();
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
for (const component of topology.apiRuntimes) {
|
|
637
|
-
const serverBundle = generateServerBundle(graph, component.projection.id, { ...options, component });
|
|
638
|
-
mergeNamedBundles(files, {
|
|
639
|
-
[topology.serviceDir(component)]: serverBundle
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
|
-
for (const component of topology.webRuntimes) {
|
|
643
|
-
const webBundle = generateWebBundle(graph, component.projection.id, { ...options, component });
|
|
644
|
-
mergeNamedBundles(files, {
|
|
645
|
-
[topology.webDir(component)]: webBundle
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
for (const component of topology.nativeRuntimes) {
|
|
649
|
-
const nativeBundle = generateNativeBundle(graph, component.projection.id, { ...options, component });
|
|
650
|
-
mergeNamedBundles(files, {
|
|
651
|
-
[topology.nativeDir(component)]: nativeBundle
|
|
652
|
-
});
|
|
653
|
-
}
|
|
654
|
-
for (const component of topology.dbRuntimes) {
|
|
655
|
-
const dbBundle = generateDbBundle(graph, component.projection.id, { ...options, component });
|
|
656
|
-
mergeNamedBundles(files, {
|
|
657
|
-
[topology.dbDir(component)]: dbBundle
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
return files;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
export function generateEnvironmentPlan(graph, options = {}) {
|
|
665
|
-
return buildEnvironmentPlan(graph, options);
|
|
666
|
-
}
|
|
1
|
+
export {
|
|
2
|
+
generateEnvironmentBundle,
|
|
3
|
+
generateEnvironmentPlan
|
|
4
|
+
} from "./environment/index.js";
|