@geekmidas/cli 10.0.0-alpha.76 → 10.0.0-alpha.78
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/dist/{LegacyStateStore-dyq07_Ir.mjs → LegacyStateStore-Ceq7A_75.mjs} +2 -2
- package/dist/{LegacyStateStore-dyq07_Ir.mjs.map → LegacyStateStore-Ceq7A_75.mjs.map} +1 -1
- package/dist/{LegacyStateStore-DwngfJxr.cjs → LegacyStateStore-Z-EKUGSX.cjs} +2 -2
- package/dist/{LegacyStateStore-DwngfJxr.cjs.map → LegacyStateStore-Z-EKUGSX.cjs.map} +1 -1
- package/dist/{LocalStateStore-B0NCde8Z.cjs → LocalStateStore-Bocb8dmZ.cjs} +2 -2
- package/dist/{LocalStateStore-B0NCde8Z.cjs.map → LocalStateStore-Bocb8dmZ.cjs.map} +1 -1
- package/dist/{LocalStateStore-DLxCCatI.mjs → LocalStateStore-C72bwpG2.mjs} +2 -2
- package/dist/{LocalStateStore-DLxCCatI.mjs.map → LocalStateStore-C72bwpG2.mjs.map} +1 -1
- package/dist/{S3StateStore-BOenb-3o.cjs → S3StateStore-35YfcdL_.cjs} +2 -2
- package/dist/{S3StateStore-BOenb-3o.cjs.map → S3StateStore-35YfcdL_.cjs.map} +1 -1
- package/dist/{S3StateStore-CGK_4hlo.mjs → S3StateStore-C9upSW5o.mjs} +2 -2
- package/dist/{S3StateStore-CGK_4hlo.mjs.map → S3StateStore-C9upSW5o.mjs.map} +1 -1
- package/dist/{SSMStateStore-DYeD__7p.cjs → SSMStateStore-W-Ps22AQ.cjs} +2 -2
- package/dist/{SSMStateStore-DYeD__7p.cjs.map → SSMStateStore-W-Ps22AQ.cjs.map} +1 -1
- package/dist/{SSMStateStore-B_5_RqY-.mjs → SSMStateStore-dOmFO6g1.mjs} +2 -2
- package/dist/{SSMStateStore-B_5_RqY-.mjs.map → SSMStateStore-dOmFO6g1.mjs.map} +1 -1
- package/dist/{add-j2yI3cbS.cjs → add-BvOICikp.cjs} +8 -8
- package/dist/{add-j2yI3cbS.cjs.map → add-BvOICikp.cjs.map} +1 -1
- package/dist/{add-B7IMXzou.mjs → add-D2XKszYP.mjs} +3 -3
- package/dist/{add-B7IMXzou.mjs.map → add-D2XKszYP.mjs.map} +1 -1
- package/dist/{bundler-Cf6OgaOe.cjs → bundler-C6T-1OLT.cjs} +5 -5
- package/dist/{bundler-Cf6OgaOe.cjs.map → bundler-C6T-1OLT.cjs.map} +1 -1
- package/dist/{bundler-DO6bHhnb.mjs → bundler-CtHzKqnZ.mjs} +5 -5
- package/dist/{bundler-DO6bHhnb.mjs.map → bundler-CtHzKqnZ.mjs.map} +1 -1
- package/dist/{cli-BCPOHmg9.mjs → cli-BgPyhx1i.mjs} +3 -3
- package/dist/{cli-BCPOHmg9.mjs.map → cli-BgPyhx1i.mjs.map} +1 -1
- package/dist/{cli-CNanUB7a.cjs → cli-K6Vj_pPW.cjs} +4 -4
- package/dist/{cli-CNanUB7a.cjs.map → cli-K6Vj_pPW.cjs.map} +1 -1
- package/dist/{compose-h_6oNnHC.cjs → compose-B7MC19eC.cjs} +668 -110
- package/dist/compose-B7MC19eC.cjs.map +1 -0
- package/dist/{compose-Dq4VCtu5.mjs → compose-DByK353T.mjs} +606 -66
- package/dist/compose-DByK353T.mjs.map +1 -0
- package/dist/{config-CqAvPP-f.d.mts → config-Bql0AmJt.d.mts} +3 -3
- package/dist/{config-CWyhLSY1.d.cts.map → config-Bql0AmJt.d.mts.map} +1 -1
- package/dist/{config-CWyhLSY1.d.cts → config-DlNpHK8p.d.cts} +3 -3
- package/dist/{config-CqAvPP-f.d.mts.map → config-DlNpHK8p.d.cts.map} +1 -1
- package/dist/config.cjs +1 -1
- package/dist/config.d.cts +3 -3
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +1 -1
- package/dist/deploy/public.cjs +12 -12
- package/dist/deploy/public.d.cts +3 -3
- package/dist/deploy/public.d.cts.map +1 -1
- package/dist/deploy/public.d.mts +3 -3
- package/dist/deploy/public.d.mts.map +1 -1
- package/dist/deploy/public.mjs +6 -6
- package/dist/{deploy-CVXEx7Nm.mjs → deploy-BerP7ppB.mjs} +5 -5
- package/dist/{deploy-CVXEx7Nm.mjs.map → deploy-BerP7ppB.mjs.map} +1 -1
- package/dist/{deploy-CT3R9de9.cjs → deploy-Dt42xiQA.cjs} +11 -11
- package/dist/{deploy-CT3R9de9.cjs.map → deploy-Dt42xiQA.cjs.map} +1 -1
- package/dist/{dokploy-eSGmlhhj.cjs → dokploy-NG9UrVrP.cjs} +2 -2
- package/dist/{dokploy-eSGmlhhj.cjs.map → dokploy-NG9UrVrP.cjs.map} +1 -1
- package/dist/{dokploy-CYm2ozFs.mjs → dokploy-ssZQRK2C.mjs} +2 -2
- package/dist/{dokploy-CYm2ozFs.mjs.map → dokploy-ssZQRK2C.mjs.map} +1 -1
- package/dist/{encryption-vS3Qzw73.cjs → encryption-CbMI24fI.cjs} +6 -6
- package/dist/{encryption-vS3Qzw73.cjs.map → encryption-CbMI24fI.cjs.map} +1 -1
- package/dist/{encryption-DC0rsR1W.mjs → encryption-De6MN0O5.mjs} +3 -3
- package/dist/{encryption-DC0rsR1W.mjs.map → encryption-De6MN0O5.mjs.map} +1 -1
- package/dist/{engine-C0N2d1i5.cjs → engine-CoCsNoW_.cjs} +73 -68
- package/dist/{engine-C0N2d1i5.cjs.map → engine-CoCsNoW_.cjs.map} +1 -1
- package/dist/{engine-BOO5TWZd.mjs → engine-DOjGIgl4.mjs} +9 -4
- package/dist/{engine-BOO5TWZd.mjs.map → engine-DOjGIgl4.mjs.map} +1 -1
- package/dist/{github-CEaFDL6t.mjs → github-CwX9z89E.mjs} +3 -3
- package/dist/{github-CEaFDL6t.mjs.map → github-CwX9z89E.mjs.map} +1 -1
- package/dist/{github-ClkdCOL9.cjs → github-D9vGqdLk.cjs} +7 -7
- package/dist/{github-ClkdCOL9.cjs.map → github-D9vGqdLk.cjs.map} +1 -1
- package/dist/{index-DOxmDKqv.d.mts → index-BXO0vlme.d.mts} +2 -2
- package/dist/{index-Ccu0f_2m.d.cts.map → index-BXO0vlme.d.mts.map} +1 -1
- package/dist/{index-Ccu0f_2m.d.cts → index-Rs0Fua-q.d.cts} +2 -2
- package/dist/{index-DOxmDKqv.d.mts.map → index-Rs0Fua-q.d.cts.map} +1 -1
- package/dist/index.cjs +180 -219
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +108 -147
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-BRbYtFXG.cjs → migrate-B4GBvCNY.cjs} +5 -5
- package/dist/{migrate-BRbYtFXG.cjs.map → migrate-B4GBvCNY.cjs.map} +1 -1
- package/dist/{migrate-D3NAT7Ns.mjs → migrate-BQ4wVKq_.mjs} +3 -3
- package/dist/{migrate-D3NAT7Ns.mjs.map → migrate-BQ4wVKq_.mjs.map} +1 -1
- package/dist/{migrate-DrO2p9Rl.mjs → migrate-CNKaQl3K.mjs} +3 -3
- package/dist/{migrate-DrO2p9Rl.mjs.map → migrate-CNKaQl3K.mjs.map} +1 -1
- package/dist/{migrate-D5_-ylI1.cjs → migrate-Cgoy58Sh.cjs} +8 -8
- package/dist/{migrate-D5_-ylI1.cjs.map → migrate-Cgoy58Sh.cjs.map} +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/reconcile/public.d.cts +1 -1
- package/dist/reconcile/public.d.mts +1 -1
- package/dist/{resolve-Fa1Ii0BK.cjs → resolve-CEaqRBzY.cjs} +3 -3
- package/dist/{resolve-Fa1Ii0BK.cjs.map → resolve-CEaqRBzY.cjs.map} +1 -1
- package/dist/{resolve-C1muFos5.mjs → resolve-mvaystiE.mjs} +3 -3
- package/dist/{resolve-C1muFos5.mjs.map → resolve-mvaystiE.mjs.map} +1 -1
- package/dist/{rollback-C9N5lKVf.cjs → rollback-F3P-rA-r.cjs} +7 -7
- package/dist/{rollback-C9N5lKVf.cjs.map → rollback-F3P-rA-r.cjs.map} +1 -1
- package/dist/{rollback-DeQzbRqM.mjs → rollback-S2C-Y9pB.mjs} +5 -5
- package/dist/{rollback-DeQzbRqM.mjs.map → rollback-S2C-Y9pB.mjs.map} +1 -1
- package/dist/{runtime-Cm7QA53H.d.cts → runtime-D9AxGzrw.d.cts} +2 -2
- package/dist/{runtime-BSvzPqXW.d.mts.map → runtime-D9AxGzrw.d.cts.map} +1 -1
- package/dist/{runtime-BSvzPqXW.d.mts → runtime-Dflki3Vs.d.mts} +2 -2
- package/dist/{runtime-Cm7QA53H.d.cts.map → runtime-Dflki3Vs.d.mts.map} +1 -1
- package/dist/{sniffer-Y6GQOCXB.cjs → sniffer-BjG-1mlD.cjs} +6 -6
- package/dist/{sniffer-Y6GQOCXB.cjs.map → sniffer-BjG-1mlD.cjs.map} +1 -1
- package/dist/{sniffer-DR8AlRCk.mjs → sniffer-ByWtH28i.mjs} +6 -6
- package/dist/{sniffer-DR8AlRCk.mjs.map → sniffer-ByWtH28i.mjs.map} +1 -1
- package/dist/{state-C2qi0iQw.cjs → stale-BsiPJI7Y.cjs} +111 -25
- package/dist/stale-BsiPJI7Y.cjs.map +1 -0
- package/dist/{state-CUecOCNJ.mjs → stale-C5FqIVc8.mjs} +106 -26
- package/dist/stale-C5FqIVc8.mjs.map +1 -0
- package/dist/target/public.cjs +1 -1
- package/dist/target/public.d.cts +2 -2
- package/dist/target/public.d.mts +2 -2
- package/dist/target/public.mjs +1 -1
- package/dist/{types-BdptGBFS.d.cts → types-ByY0zi3n.d.cts} +42 -3
- package/dist/types-ByY0zi3n.d.cts.map +1 -0
- package/dist/{types-OxE0OAn2.d.mts → types-DKxkXsKC.d.mts} +42 -3
- package/dist/types-DKxkXsKC.d.mts.map +1 -0
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-QUTqYmVy.mjs → workspace-BBdqmkch.mjs} +114 -17
- package/dist/workspace-BBdqmkch.mjs.map +1 -0
- package/dist/{workspace-DOm81oUo.cjs → workspace-Bp8FEkF-.cjs} +130 -15
- package/dist/workspace-Bp8FEkF-.cjs.map +1 -0
- package/package.json +15 -15
- package/dist/compose-Dq4VCtu5.mjs.map +0 -1
- package/dist/compose-h_6oNnHC.cjs.map +0 -1
- package/dist/state-C2qi0iQw.cjs.map +0 -1
- package/dist/state-CUecOCNJ.mjs.map +0 -1
- package/dist/types-BdptGBFS.d.cts.map +0 -1
- package/dist/types-OxE0OAn2.d.mts.map +0 -1
- package/dist/workspace-DOm81oUo.cjs.map +0 -1
- package/dist/workspace-QUTqYmVy.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-DOm81oUo.cjs","names":["isIP","configurableBuiltins","BUILTIN_TARGETS","isDeployTarget","builtinTarget","basename","isAbsolute","join"],"sources":["../src/secrets/providers.ts","../src/compose/logsConfig.ts","../src/workspace/stages.ts","../src/workspace/schema.ts","../src/workspace/types.ts","../src/workspace/publicEnv.ts","../src/workspace/index.ts"],"sourcesContent":["/**\n * The secrets stores gkm ships, by name — with no imports, so the config\n * loader can check `secrets.store` without loading any store.\n */\n\n/** The stores this package ships, by the name `secrets.store` gives them. */\nexport const SECRETS_STORE_PROVIDERS = [\n\t'file',\n\t'ssm',\n\t'secrets-manager',\n] as const;\n\nexport type SecretsStoreProvider = (typeof SECRETS_STORE_PROVIDERS)[number];\n\n/**\n * `secrets.store` names a provider this package does not ship — a typo such\n * as `'secretsmanager'` or `'ssm-parameter'`. Refused rather than read as the\n * file, which would leave a deployed stage's secrets on one machine.\n */\nexport class UnknownSecretsStoreProvider extends Error {\n\tconstructor(readonly provider: unknown) {\n\t\tsuper(\n\t\t\t`secrets.store names the provider ${JSON.stringify(provider)}, which is not one gkm ships. ` +\n\t\t\t\t`Use 'file', { provider: 'ssm', region }, { provider: 'secrets-manager', region }, ` +\n\t\t\t\t'or { provider: <an object with name, read() and write()> } in gkm.config.ts.',\n\t\t);\n\t\tthis.name = 'UnknownSecretsStoreProvider';\n\t}\n}\n\n/**\n * Refuse a `secrets.store` naming a provider that is not one, before the\n * schema reduces it to a generic validation failure.\n */\nexport function assertKnownSecretsStore(store: unknown): void {\n\tif (store === undefined) return;\n\tconst provider =\n\t\ttypeof store === 'object' && store !== null && 'provider' in store\n\t\t\t? (store as { provider: unknown }).provider\n\t\t\t: store;\n\tif (\n\t\ttypeof provider === 'string' &&\n\t\t!(SECRETS_STORE_PROVIDERS as readonly string[]).includes(provider)\n\t) {\n\t\tthrow new UnknownSecretsStoreProvider(provider);\n\t}\n}\n","/**\n * `deploy.compose.logs`, read: whether the stack runs OpenObserve, and how it\n * is reached.\n *\n * Kept apart from the stack so the workspace schema can check a config with\n * the same rules — and the same named errors — the stack applies.\n */\n\nimport { isIP } from 'node:net';\nimport type { ComposeLogsConfig } from '../workspace/types.js';\n\n/** The port OpenObserve listens on, and the one it is published on by default. */\nexport const LOGS_PORT = 5080;\n\n/** How many days of data OpenObserve keeps unless told otherwise. */\nexport const LOGS_RETENTION_DAYS = 30;\n\n/** The fewest days OpenObserve accepts: below it, it refuses to start. */\nexport const LOGS_MIN_RETENTION_DAYS = 3;\n\n/** `deploy.compose.logs`, with its defaults applied. */\nexport interface ResolvedLogs {\n\t/** The loopback port it is published on. Not published when `public` is set. */\n\tport: number;\n\tretentionDays: number;\n\t/** Served on `logs.<stage domain>`, to these addresses only. */\n\tpublic?: { allow: string[] };\n}\n\n/** `public` with no address to allow: nobody could reach it. */\nexport class LogsAllowEmpty extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'deploy.compose.logs.public.allow is empty, so the logs site would ' +\n\t\t\t\t'answer every request with 403. List the addresses that may reach it ' +\n\t\t\t\t\"(public: { allow: ['203.0.113.7', '10.0.0.0/8'] }), or remove public \" +\n\t\t\t\t'and reach it through an SSH tunnel.',\n\t\t);\n\t\tthis.name = 'LogsAllowEmpty';\n\t}\n}\n\n/** An `allow` entry that is not an IP address or a CIDR range. */\nexport class LogsAllowEntryInvalid extends Error {\n\tconstructor(readonly entry: string) {\n\t\tsuper(\n\t\t\t`deploy.compose.logs.public.allow has '${entry}', which is not an IP ` +\n\t\t\t\t\"address or a CIDR range. Write one like '203.0.113.7', \" +\n\t\t\t\t\"'10.0.0.0/8' or '2001:db8::/32'.\",\n\t\t);\n\t\tthis.name = 'LogsAllowEntryInvalid';\n\t}\n}\n\n/** A retention OpenObserve would refuse to start with. */\nexport class LogsRetentionInvalid extends Error {\n\tconstructor(readonly retentionDays: unknown) {\n\t\tsuper(\n\t\t\t`deploy.compose.logs.retentionDays is ${String(retentionDays)}; it must ` +\n\t\t\t\t`be a whole number of days, at least ${LOGS_MIN_RETENTION_DAYS} — ` +\n\t\t\t\t'OpenObserve refuses to start with fewer. Leave it out for ' +\n\t\t\t\t`${LOGS_RETENTION_DAYS}.`,\n\t\t);\n\t\tthis.name = 'LogsRetentionInvalid';\n\t}\n}\n\n/** A port that is not one. */\nexport class LogsPortInvalid extends Error {\n\tconstructor(readonly port: unknown) {\n\t\tsuper(\n\t\t\t`deploy.compose.logs.port is ${String(port)}; set it to a number ` +\n\t\t\t\t`between 1 and 65535, or leave it out for ${LOGS_PORT}.`,\n\t\t);\n\t\tthis.name = 'LogsPortInvalid';\n\t}\n}\n\n/** Whether `entry` is an IP address, or an address and a prefix length. */\nfunction isAddressOrRange(entry: string): boolean {\n\tconst [address = '', prefix, ...rest] = entry.split('/');\n\tif (rest.length > 0) return false;\n\tconst family = isIP(address);\n\tif (family === 0) return false;\n\tif (prefix === undefined) return true;\n\tif (!/^\\d{1,3}$/.test(prefix)) return false;\n\treturn Number(prefix) <= (family === 4 ? 32 : 128);\n}\n\n/**\n * `deploy.compose.logs` with its defaults, or undefined when it is off.\n * Throws the named error for the first thing wrong with it.\n */\nexport function resolveLogs(\n\tlogs: boolean | ComposeLogsConfig | undefined,\n): ResolvedLogs | undefined {\n\tif (!logs) return undefined;\n\tconst config: ComposeLogsConfig = logs === true ? {} : logs;\n\n\tconst port = config.port ?? LOGS_PORT;\n\tif (!Number.isInteger(port) || port < 1 || port > 65_535) {\n\t\tthrow new LogsPortInvalid(config.port);\n\t}\n\n\tconst retentionDays = config.retentionDays ?? LOGS_RETENTION_DAYS;\n\tif (\n\t\t!Number.isInteger(retentionDays) ||\n\t\tretentionDays < LOGS_MIN_RETENTION_DAYS\n\t) {\n\t\tthrow new LogsRetentionInvalid(config.retentionDays);\n\t}\n\n\tif (config.public) {\n\t\tconst allow = config.public.allow ?? [];\n\t\tif (allow.length === 0) throw new LogsAllowEmpty();\n\t\tfor (const entry of allow) {\n\t\t\tif (!isAddressOrRange(entry)) throw new LogsAllowEntryInvalid(entry);\n\t\t}\n\t\treturn { port, retentionDays, public: { allow: [...allow] } };\n\t}\n\n\treturn { port, retentionDays };\n}\n","import type { StagesConfig } from './types.js';\n\n/**\n * What `gkm test` runs as. The CLI's own, so no project stage may take it:\n * its containers are torn down and rebuilt between runs.\n */\nexport const TEST_STAGE = 'test';\n\n/** A stage becomes part of every physical name, so it has to fit in one. */\nconst STAGE_NAME = /^[a-z][a-z0-9-]*$/;\n\n/** Everything wrong with one stage name, or nothing. */\nfunction nameProblems(name: string): string[] {\n\tconst problems: string[] = [];\n\tif (!STAGE_NAME.test(name)) {\n\t\tproblems.push(\n\t\t\t`stage \"${name}\" must be lowercase letters, digits and hyphens, starting with a letter`,\n\t\t);\n\t}\n\tif (name === TEST_STAGE) {\n\t\tproblems.push(`stage \"${TEST_STAGE}\" is reserved for gkm test`);\n\t}\n\treturn problems;\n}\n\n/** Everything wrong with a list of deployed stages on its own. */\nexport function deployedProblems(deployed: readonly string[]): string[] {\n\tconst problems = deployed.flatMap(nameProblems);\n\tconst seen = new Set<string>();\n\tfor (const name of deployed) {\n\t\tif (seen.has(name)) problems.push(`stage \"${name}\" is listed twice`);\n\t\tseen.add(name);\n\t}\n\treturn problems;\n}\n\n/** Everything wrong with a stages block, or nothing. */\nexport function stageProblems(stages: StagesConfig | undefined): string[] {\n\tif (!stages) {\n\t\treturn [\n\t\t\t\"`stages` is required, e.g. stages: { local: 'dev', deployed: ['prod'] }\",\n\t\t];\n\t}\n\n\tconst { local, deployed, protected: kept = [] } = stages;\n\tconst problems = [...nameProblems(local), ...deployedProblems(deployed)];\n\n\tif (deployed.includes(local)) {\n\t\tproblems.push(\n\t\t\t`\"${local}\" is both the local stage and a deployed one; they would share secrets`,\n\t\t);\n\t}\n\n\tfor (const name of kept) {\n\t\tif (!deployed.includes(name)) {\n\t\t\tproblems.push(`protected stage \"${name}\" is not a deployed stage`);\n\t\t}\n\t}\n\n\treturn problems;\n}\n\n/** The stages block, or an error naming everything wrong with it. */\nexport function validateStages(stages: StagesConfig | undefined): StagesConfig {\n\tconst problems = stageProblems(stages);\n\tif (problems.length) {\n\t\tthrow new InvalidStages(problems);\n\t}\n\treturn stages!;\n}\n\n/** Refuses a stage the project does not deploy to. */\nexport function assertDeployedStage(stages: StagesConfig, stage: string): void {\n\tif (!stages.deployed.includes(stage)) {\n\t\tthrow new UndeclaredStage(stage, stages.deployed);\n\t}\n}\n\n/** A stages block — in gkm.config.ts or from `gkm init`'s flags — that breaks a rule. */\nexport class InvalidStages extends Error {\n\tconstructor(readonly problems: readonly string[]) {\n\t\tsuper(`Invalid stages:\\n ${problems.join('\\n ')}`);\n\t\tthis.name = 'InvalidStages';\n\t}\n}\n\n/**\n * A stage the project does not deploy to. Refused before anything is\n * provisioned, so a typo does not become a second environment.\n */\nexport class UndeclaredStage extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly deployed: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`\"${stage}\" is not a deployed stage. gkm.config.ts deploys to: ${deployed.join(', ') || 'nothing yet'}.`,\n\t\t);\n\t\tthis.name = 'UndeclaredStage';\n\t}\n}\n","import { z } from 'zod/v4';\nimport { resolveLogs } from '../compose/logsConfig.js';\nimport {\n\tBUILTIN_TARGETS,\n\tbuiltinTarget,\n\tconfigurableBuiltins,\n} from '../target/builtins.js';\nimport { isDeployTarget } from '../target/define.js';\nimport { stageProblems } from './stages.js';\n\n/** Routes are a glob, or a list of them. */\nconst RoutesSchema = z.union([z.string(), z.array(z.string())]);\n\n/**\n * Telescope configuration schema.\n */\nconst TelescopeConfigSchema = z.object({\n\tenabled: z.boolean().optional(),\n\tport: z.number().optional(),\n\tpath: z.string().optional(),\n\tignore: z.array(z.string()).optional(),\n\trecordBody: z.boolean().optional(),\n\tmaxEntries: z.number().optional(),\n\twebsocket: z.boolean().optional(),\n});\n\n/**\n * OpenAPI configuration schema.\n */\nconst OpenApiConfigSchema = z.object({\n\tenabled: z.boolean().optional(),\n\ttitle: z.string().optional(),\n\tversion: z.string().optional(),\n\tdescription: z.string().optional(),\n});\n\n/**\n * Hooks configuration schema.\n */\nconst HooksConfigSchema = z.object({\n\tserver: z.string().optional(),\n});\n\n/**\n * Backend framework schema for non-gkm apps.\n */\nexport const BackendFrameworkSchema = z.enum([\n\t'hono',\n\t'better-auth',\n\t'express',\n\t'fastify',\n]);\n\n/**\n * Backend framework types for apps that don't use gkm routes.\n * Derived from {@link BackendFrameworkSchema}.\n *\n * Used with `entry` to specify the framework for proper Docker builds.\n *\n * @example\n * ```ts\n * // Better Auth server\n * {\n * entry: './src/index.ts',\n * framework: 'better-auth',\n * port: 3001,\n * }\n *\n * // Hono app without gkm routes\n * {\n * entry: './src/server.ts',\n * framework: 'hono',\n * port: 3000,\n * }\n * ```\n */\nexport type BackendFramework = z.infer<typeof BackendFrameworkSchema>;\n\n/**\n * Frontend framework schema.\n */\nexport const FrontendFrameworkSchema = z.enum([\n\t'nextjs',\n\t'remix',\n\t'vite',\n\t'tanstack-start',\n]);\n\n/**\n * Frontend framework types. Derived from {@link FrontendFrameworkSchema}.\n *\n * @example\n * ```ts\n * // Next.js app\n * {\n * type: 'web',\n * framework: 'nextjs',\n * port: 3000,\n * }\n *\n * // Vite SPA\n * {\n * type: 'web',\n * framework: 'vite',\n * port: 5173,\n * }\n *\n * // TanStack Start (Vite-based, full-stack)\n * {\n * type: 'web',\n * framework: 'tanstack-start',\n * port: 3000,\n * }\n * ```\n */\nexport type FrontendFramework = z.infer<typeof FrontendFrameworkSchema>;\n\n/**\n * Mobile framework schema.\n */\nexport const MobileFrameworkSchema = z.enum(['expo']);\n\n/**\n * Mobile framework types. Derived from {@link MobileFrameworkSchema}.\n *\n * Mobile apps deploy via their own toolchain (e.g. EAS Build for Expo)\n * rather than Docker/Dokploy. Only build-time public env vars\n * (e.g. `EXPO_PUBLIC_*`) reach the device — server secrets are dropped.\n *\n * @example\n * ```ts\n * {\n * type: 'mobile',\n * framework: 'expo',\n * path: 'apps/mobile',\n * port: 8081,\n * dependencies: ['api'],\n * }\n * ```\n */\nexport type MobileFramework = z.infer<typeof MobileFrameworkSchema>;\n\n/**\n * Combined framework schema (backend, frontend, or mobile).\n */\nexport const FrameworkSchema = z.union([\n\tBackendFrameworkSchema,\n\tFrontendFrameworkSchema,\n\tMobileFrameworkSchema,\n]);\n\n/**\n * Any framework value across backend, frontend, or mobile.\n * Derived from {@link FrameworkSchema}.\n */\nexport type Framework = z.infer<typeof FrameworkSchema>;\n\n/** Frontend framework values, kept in sync with FrontendFrameworkSchema. */\nconst FRONTEND_FRAMEWORKS = [\n\t'nextjs',\n\t'remix',\n\t'vite',\n\t'tanstack-start',\n] as const;\n\n/** Mobile framework values, kept in sync with MobileFrameworkSchema. */\nconst MOBILE_FRAMEWORKS = ['expo'] as const;\n\n/**\n * A deploy target's name. Which names exist is the workspace's to say — the\n * built-ins and its own `deploy.targets` — so it is checked against both\n * once the whole config is read.\n */\nconst DeployTargetSchema = z.string().min(1);\n\n/**\n * The built-in targets a config may name, both deployed by `gkm deploy`:\n * `dokploy`, and `sst`, which runs `sst deploy` on the manifest `gkm build`\n * writes.\n */\nconst SUPPORTED_DEPLOY_TARGETS = configurableBuiltins();\n\n/** Built-in names reserved for targets that do not exist yet. */\nconst PHASE_2_DEPLOY_TARGETS = Object.entries(BUILTIN_TARGETS)\n\t.filter(([, target]) => target.status === 'planned')\n\t.map(([name]) => name);\n\n/**\n * Whether a config may name `target`: a usable built-in, or one of its own\n * `deploy.targets`.\n */\nexport function isDeployTargetSupported(\n\ttarget: string,\n\ttargets: Record<string, unknown> = {},\n): boolean {\n\treturn (\n\t\tSUPPORTED_DEPLOY_TARGETS.includes(target) || Object.hasOwn(targets, target)\n\t);\n}\n\n/**\n * Check if a deploy target is planned for Phase 2.\n */\nexport function isPhase2DeployTarget(target: string): boolean {\n\treturn PHASE_2_DEPLOY_TARGETS.includes(target);\n}\n\n/**\n * Get error message for unsupported deploy targets.\n */\nexport function getDeployTargetError(target: string, appName?: string): string {\n\tif (isPhase2DeployTarget(target)) {\n\t\tconst context = appName ? ` for app \"${appName}\"` : '';\n\t\treturn `Deploy target \"${target}\"${context} is coming in Phase 2. Currently \"dokploy\" and \"sst\" are supported.`;\n\t}\n\treturn `Unknown deploy target: ${target}. Built in: ${SUPPORTED_DEPLOY_TARGETS.join(', ')}; any other is named in deploy.targets with the package that provides it. Coming in Phase 2: ${PHASE_2_DEPLOY_TARGETS.join(', ')}.`;\n}\n\n/**\n * One `deploy.targets` entry: a package name, a target object, or either\n * with options.\n */\nconst DeployTargetEntrySchema = z.union([\n\tz.string().min(1, 'A target package name cannot be empty'),\n\tz.custom((value) => isDeployTarget(value), {\n\t\tmessage:\n\t\t\t'Expected a package name, a target (defineTarget), or [package or target, options]',\n\t}),\n\tz.tuple([\n\t\tz.union([\n\t\t\tz.string().min(1),\n\t\t\tz.custom((value) => isDeployTarget(value), {\n\t\t\t\tmessage: 'Expected a package name or a target (defineTarget)',\n\t\t\t}),\n\t\t]),\n\t\tz.unknown(),\n\t]),\n]);\n\n/**\n * Dokploy workspace configuration schema.\n * Supports either a single endpoint or per-stage endpoints.\n */\nconst DokployWorkspaceConfigSchema = z\n\t.object({\n\t\t/** Single endpoint for all stages */\n\t\tendpoint: z.url('Dokploy endpoint must be a valid URL').optional(),\n\t\t/** Per-stage endpoints (stage name -> endpoint URL) */\n\t\tendpoints: z\n\t\t\t.record(z.string(), z.url('Endpoint must be a valid URL'))\n\t\t\t.optional(),\n\t\tregistryId: z.string().optional(),\n\t\tverify: z\n\t\t\t.object({\n\t\t\t\tdeploymentTimeoutMs: z.number().int().positive().optional(),\n\t\t\t\thealthCheckPath: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.startsWith('/', 'healthCheckPath must start with /')\n\t\t\t\t\t.optional(),\n\t\t\t\thealthyAfter: z.number().int().positive().optional(),\n\t\t\t\tintervalMs: z.number().int().positive().optional(),\n\t\t\t\thealthTimeoutMs: z.number().int().positive().optional(),\n\t\t\t})\n\t\t\t.optional(),\n\t})\n\t.refine((data) => data.endpoint || data.endpoints, {\n\t\tmessage: 'Either endpoint or endpoints must be provided',\n\t});\n\n// =============================================================================\n// AWS Regions (needed by DNS and State providers)\n// =============================================================================\n\n/**\n * Valid AWS regions.\n */\nconst AwsRegionSchema = z.enum([\n\t'us-east-1',\n\t'us-east-2',\n\t'us-west-1',\n\t'us-west-2',\n\t'af-south-1',\n\t'ap-east-1',\n\t'ap-south-1',\n\t'ap-south-2',\n\t'ap-southeast-1',\n\t'ap-southeast-2',\n\t'ap-southeast-3',\n\t'ap-southeast-4',\n\t'ap-northeast-1',\n\t'ap-northeast-2',\n\t'ap-northeast-3',\n\t'ca-central-1',\n\t'eu-central-1',\n\t'eu-central-2',\n\t'eu-west-1',\n\t'eu-west-2',\n\t'eu-west-3',\n\t'eu-south-1',\n\t'eu-south-2',\n\t'eu-north-1',\n\t'me-south-1',\n\t'me-central-1',\n\t'sa-east-1',\n]);\n\n// =============================================================================\n// DNS Record Types (used by DnsProvider interface)\n// =============================================================================\n\n/**\n * DNS record types supported across providers.\n */\nexport const DnsRecordTypeSchema = z.enum([\n\t'A',\n\t'AAAA',\n\t'CNAME',\n\t'MX',\n\t'TXT',\n\t'NS',\n\t'SRV',\n\t'CAA',\n]);\n\n/**\n * A DNS record as returned by the provider.\n */\nexport const DnsRecordSchema = z.object({\n\t/** Subdomain name (e.g., 'api' for api.example.com, '@' for root) */\n\tname: z.string(),\n\t/** Record type */\n\ttype: DnsRecordTypeSchema,\n\t/** TTL in seconds */\n\tttl: z.number().int().positive(),\n\t/** Record values */\n\tvalues: z.array(z.string()),\n});\n\n/**\n * A DNS record to create or update.\n */\nexport const UpsertDnsRecordSchema = z.object({\n\t/** Subdomain name (e.g., 'api' for api.example.com, '@' for root) */\n\tname: z.string(),\n\t/** Record type */\n\ttype: DnsRecordTypeSchema,\n\t/** TTL in seconds */\n\tttl: z.number().int().positive(),\n\t/** Record value (IP address, hostname, etc.) */\n\tvalue: z.string(),\n});\n\n/**\n * Result of an upsert operation.\n */\nexport const UpsertResultSchema = z.object({\n\t/** The record that was upserted */\n\trecord: UpsertDnsRecordSchema,\n\t/** Whether the record was created (true) or updated (false) */\n\tcreated: z.boolean(),\n\t/** Whether the record already existed with the same value */\n\tunchanged: z.boolean(),\n});\n\n// =============================================================================\n// DNS Provider Configuration\n// =============================================================================\n\n/**\n * Hostinger DNS provider config (without domain - domain is the record key).\n */\nexport const HostingerDnsProviderSchema = z.object({\n\tprovider: z.literal('hostinger'),\n\t/** TTL in seconds (default: 300) */\n\tttl: z.number().int().positive().optional(),\n});\n\n/**\n * Route53 DNS provider config (without domain - domain is the record key).\n */\nexport const Route53DnsProviderSchema = z.object({\n\tprovider: z.literal('route53'),\n\t/** AWS region (optional - uses AWS_REGION env var if not provided) */\n\tregion: AwsRegionSchema.optional(),\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile: z.string().optional(),\n\t/** Hosted zone ID (optional - auto-detected from domain if not provided) */\n\thostedZoneId: z.string().optional(),\n\t/** TTL in seconds (default: 300) */\n\tttl: z.number().int().positive().optional(),\n});\n\n/**\n * Cloudflare DNS provider config (placeholder for future).\n */\nexport const CloudflareDnsProviderSchema = z.object({\n\tprovider: z.literal('cloudflare'),\n\t/** TTL in seconds (default: 300) */\n\tttl: z.number().int().positive().optional(),\n});\n\n/**\n * Manual DNS configuration (user handles DNS themselves).\n */\nexport const ManualDnsProviderSchema = z.object({\n\tprovider: z.literal('manual'),\n});\n\n/**\n * Custom DNS provider config (user-provided implementation).\n */\nexport const CustomDnsProviderSchema = z.object({\n\t/** Custom DnsProvider implementation */\n\tprovider: z.custom<{\n\t\tname: string;\n\t\tgetRecords: Function;\n\t\tupsertRecords: Function;\n\t}>(\n\t\t(val) =>\n\t\t\ttypeof val === 'object' &&\n\t\t\tval !== null &&\n\t\t\ttypeof (val as any).name === 'string' &&\n\t\t\ttypeof (val as any).getRecords === 'function' &&\n\t\t\ttypeof (val as any).upsertRecords === 'function',\n\t\t{\n\t\t\tmessage:\n\t\t\t\t'Custom DNS provider must implement name, getRecords(), and upsertRecords() methods',\n\t\t},\n\t),\n\t/** TTL in seconds (default: 300) */\n\tttl: z.number().int().positive().optional(),\n});\n\n/**\n * Built-in DNS provider config (discriminated union).\n */\nexport const BuiltInDnsProviderSchema = z.discriminatedUnion('provider', [\n\tHostingerDnsProviderSchema,\n\tRoute53DnsProviderSchema,\n\tCloudflareDnsProviderSchema,\n\tManualDnsProviderSchema,\n]);\n\n/**\n * Single DNS provider config (for one domain).\n */\nexport const DnsProviderSchema = z.union([\n\tBuiltInDnsProviderSchema,\n\tCustomDnsProviderSchema,\n]);\n\nexport type DnsProvider = z.infer<typeof DnsProviderSchema>;\n\n/**\n * DNS configuration schema.\n *\n * Maps root domains to their DNS provider configuration.\n * Example:\n * ```\n * dns: {\n * 'geekmidas.dev': { provider: 'hostinger' },\n * 'geekmidas.com': { provider: 'route53' },\n * }\n * ```\n *\n * Supported providers:\n * - 'hostinger': Use Hostinger DNS API\n * - 'route53': Use AWS Route53\n * - 'cloudflare': Use Cloudflare DNS API (future)\n * - 'manual': Don't create records, just print required records\n * - Custom: Provide a DnsProvider implementation\n */\nexport const DnsConfigSchema = z.record(z.string(), DnsProviderSchema);\n\n// Legacy single-domain config schemas (for backwards compatibility)\nexport const HostingerDnsConfigSchema = HostingerDnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const Route53DnsConfigSchema = Route53DnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const CloudflareDnsConfigSchema = CloudflareDnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const ManualDnsConfigSchema = ManualDnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const CustomDnsConfigSchema = CustomDnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const BuiltInDnsConfigSchema = z.discriminatedUnion('provider', [\n\tHostingerDnsConfigSchema,\n\tRoute53DnsConfigSchema,\n\tCloudflareDnsConfigSchema,\n\tManualDnsConfigSchema,\n]);\nexport const LegacyDnsConfigSchema = z.union([\n\tBuiltInDnsConfigSchema,\n\tCustomDnsConfigSchema,\n]);\n\n/**\n * Combined DNS config that supports both new multi-domain and legacy single-domain formats.\n */\nexport const DnsConfigWithLegacySchema = z.union([\n\tDnsConfigSchema,\n\tLegacyDnsConfigSchema,\n]);\n\nexport type DnsConfig = z.infer<typeof DnsConfigWithLegacySchema>;\n\n/**\n * Backups configuration schema.\n *\n * Configures automatic backup destinations for database services.\n * On first deploy, creates S3 bucket with unique name and IAM credentials.\n */\nexport const BackupsConfigSchema = z.object({\n\t/** Backup storage type (currently only 's3' supported) */\n\ttype: z.literal('s3'),\n\t/** AWS profile name for creating bucket/IAM resources */\n\tprofile: z.string().optional(),\n\t/** AWS region for the backup bucket */\n\tregion: AwsRegionSchema,\n\t/** Cron schedule for backups (default: '0 2 * * *' = 2 AM daily) */\n\tschedule: z.string().optional(),\n\t/** Number of backups to retain (default: 30) */\n\tretention: z.number().optional(),\n});\n\nexport type BackupsConfig = z.infer<typeof BackupsConfigSchema>;\n\n/**\n * `deploy.compose` — what the compose target runs beside the apps. The rules\n * past the shape are `resolveLogs`'s, so the schema and the stack refuse the\n * same configs with the same words.\n */\nconst ComposeWorkspaceConfigSchema = z.object({\n\tlogs: z\n\t\t.union([\n\t\t\tz.boolean(),\n\t\t\tz.object({\n\t\t\t\tport: z.number().optional(),\n\t\t\t\tretentionDays: z.number().optional(),\n\t\t\t\tpublic: z.object({ allow: z.array(z.string()) }).optional(),\n\t\t\t}),\n\t\t])\n\t\t.superRefine((logs, ctx) => {\n\t\t\ttry {\n\t\t\t\tresolveLogs(logs);\n\t\t\t} catch (error) {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t}\n\t\t})\n\t\t.optional(),\n});\n\n/**\n * Deploy configuration schema.\n */\nconst DeployConfigSchema = z.object({\n\tdefault: DeployTargetSchema.optional(),\n\ttargets: z\n\t\t.record(z.string(), DeployTargetEntrySchema)\n\t\t.superRefine((targets, ctx) => {\n\t\t\tfor (const name of Object.keys(targets)) {\n\t\t\t\t// The built-ins resolve first, so an entry under one of their\n\t\t\t\t// names would never be used — say so rather than ignore it.\n\t\t\t\tif (builtinTarget(name)) {\n\t\t\t\t\tctx.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `\"${name}\" is a built-in target; give this one a name of its own`,\n\t\t\t\t\t\tpath: [name],\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\t.optional(),\n\t/** Whose deploy this is, on a target shared with other workspaces. */\n\tnamespace: z\n\t\t.string()\n\t\t.regex(\n\t\t\t/^[a-z0-9]+(?:-[a-z0-9]+)*$/,\n\t\t\t\"deploy.namespace must be lowercase letters and digits with single '-' between them\",\n\t\t)\n\t\t.optional(),\n\t/** Each deployed stage's base domain (stage name -> domain). */\n\tdomains: z.record(z.string(), z.string()).optional(),\n\t/** Where every target pushes and pulls the apps' images. */\n\tregistry: z.string().min(1).optional(),\n\tdokploy: DokployWorkspaceConfigSchema.optional(),\n\tcompose: ComposeWorkspaceConfigSchema.optional(),\n\tdns: DnsConfigWithLegacySchema.optional(),\n\tbackups: BackupsConfigSchema.optional(),\n});\n\n/**\n * Models configuration schema.\n */\nconst ModelsConfigSchema = z.object({\n\tpath: z.string().optional(),\n\tschema: z.enum(['zod']).optional(),\n});\n\n/**\n * Shared configuration schema.\n */\nconst SharedConfigSchema = z.object({\n\tpackages: z.array(z.string()).optional(),\n\tmodels: ModelsConfigSchema.optional(),\n});\n\n/**\n * Secrets configuration schema.\n */\nconst StagesConfigSchema = z\n\t.object({\n\t\tlocal: z.string(),\n\t\tdeployed: z.array(z.string()),\n\t\tprotected: z.array(z.string()).optional(),\n\t})\n\t.superRefine((stages, ctx) => {\n\t\tfor (const message of stageProblems(stages)) {\n\t\t\tctx.addIssue({ code: 'custom', message });\n\t\t}\n\t});\n\nconst SecretsStoreSchema = z.union([\n\tz.literal('file'),\n\tz.object({ provider: z.literal('ssm'), region: AwsRegionSchema }).strict(),\n\tz\n\t\t.object({\n\t\t\tprovider: z.literal('secrets-manager'),\n\t\t\tregion: AwsRegionSchema,\n\t\t\tkmsKeyId: z.string().min(1).optional(),\n\t\t})\n\t\t.strict(),\n\tz.object({\n\t\t/** Any backend: an object implementing SecretsStore */\n\t\tprovider: z.custom<{ name: string; read: Function; write: Function }>(\n\t\t\t(val) =>\n\t\t\t\ttypeof val === 'object' &&\n\t\t\t\tval !== null &&\n\t\t\t\ttypeof (val as any).name === 'string' &&\n\t\t\t\ttypeof (val as any).read === 'function' &&\n\t\t\t\ttypeof (val as any).write === 'function',\n\t\t\t{ message: 'a secrets store has a name, read() and write()' },\n\t\t),\n\t}),\n]);\n\nconst SecretsConfigSchema = z.object({\n\tenabled: z.boolean().optional(),\n\tstore: SecretsStoreSchema.optional(),\n\talgorithm: z.string().optional(),\n\tkdf: z.enum(['scrypt', 'pbkdf2']).optional(),\n});\n\n// =============================================================================\n// State Provider Configuration\n// =============================================================================\n\n/**\n * Local state provider config.\n */\nconst LocalStateConfigSchema = z.object({\n\tprovider: z.literal('local'),\n});\n\n/**\n * SSM state provider config (requires region).\n */\nconst SSMStateConfigSchema = z.object({\n\tprovider: z.literal('ssm'),\n\t/** AWS region (required for SSM provider) */\n\tregion: AwsRegionSchema,\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile: z.string().optional(),\n});\n\n/**\n * S3 state provider config: conditional writes and a lock object in a bucket.\n */\nconst S3StateConfigSchema = z.object({\n\tprovider: z.literal('s3'),\n\t/** Bucket the state lives in (must already exist) */\n\tbucket: z.string().min(1),\n\t/** AWS region of the bucket */\n\tregion: AwsRegionSchema,\n\t/** Key prefix inside the bucket (default: 'gkm') */\n\tprefix: z.string().optional(),\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile: z.string().optional(),\n});\n\n/**\n * Custom state provider config (user-provided implementation).\n */\nconst CustomStateConfigSchema = z.object({\n\t/** Custom StateProvider implementation */\n\tprovider: z.custom<{ read: Function; write: Function }>(\n\t\t(val) =>\n\t\t\ttypeof val === 'object' &&\n\t\t\tval !== null &&\n\t\t\ttypeof (val as any).read === 'function' &&\n\t\t\ttypeof (val as any).write === 'function',\n\t\t{ message: 'Custom provider must implement read() and write() methods' },\n\t),\n});\n\n/**\n * Built-in state provider config (discriminated union).\n */\nconst BuiltInStateConfigSchema = z.discriminatedUnion('provider', [\n\tLocalStateConfigSchema,\n\tSSMStateConfigSchema,\n\tS3StateConfigSchema,\n]);\n\n/**\n * State configuration schema.\n *\n * Configures how deployment state is stored.\n * - 'local': Store in .gkm/deploy-{stage}.json (default)\n * - 'ssm': Store in AWS SSM Parameter Store (requires region)\n * - 's3': Store in an S3 bucket with conditional writes (requires bucket, region)\n * - Custom: Provide a StateStore, or a StateProvider with read/write methods\n */\nconst StateConfigSchema = z.union([\n\tBuiltInStateConfigSchema,\n\tCustomStateConfigSchema,\n]);\n\n/**\n * App configuration schema.\n */\nconst AppConfigSchema = z\n\t.object({\n\t\t// Core properties\n\t\ttype: z.enum(['backend', 'web', 'mobile']).optional().default('backend'),\n\t\tpath: z.string().min(1, 'App path is required'),\n\t\tport: z.number().int().positive('Port must be a positive integer'),\n\t\tdependencies: z.array(z.string()).optional(),\n\t\tdeploy: DeployTargetSchema.optional(),\n\n\t\t// Backend-specific (from GkmConfig)\n\t\tconstructs: RoutesSchema.optional(),\n\t\troutes: RoutesSchema.optional(),\n\t\tfunctions: RoutesSchema.optional(),\n\t\tcrons: RoutesSchema.optional(),\n\t\tqueues: RoutesSchema.optional(),\n\t\ttopics: RoutesSchema.optional(),\n\t\tsubscribers: RoutesSchema.optional(),\n\t\tenvParser: z.string().optional(),\n\t\tlogger: z.string().optional(),\n\t\thooks: HooksConfigSchema.optional(),\n\t\ttelescope: z\n\t\t\t.union([z.string(), z.boolean(), TelescopeConfigSchema])\n\t\t\t.optional(),\n\t\topenapi: z.union([z.boolean(), OpenApiConfigSchema]).optional(),\n\t\truntime: z.enum(['node', 'bun']).optional(),\n\t\tenv: z.union([z.string(), z.array(z.string())]).optional(),\n\n\t\t// Entry point for non-gkm apps (used by dev and docker build)\n\t\tentry: z.string().optional(),\n\n\t\t// Framework (backend, web, or mobile)\n\t\tframework: FrameworkSchema.optional(),\n\t\t// Web/mobile: config file paths for env sniffing (calls .parse() at import)\n\t\tconfig: z\n\t\t\t.object({\n\t\t\t\tclient: z.string().optional(),\n\t\t\t\tserver: z.string().optional(),\n\t\t\t})\n\t\t\t.optional(),\n\t})\n\t// Note: routes is optional for backend apps - some backends like auth servers don't use routes\n\t.refine(\n\t\t(data) => {\n\t\t\t// Web apps must have a web framework\n\t\t\tif (data.type === 'web') {\n\t\t\t\tif (\n\t\t\t\t\t!data.framework ||\n\t\t\t\t\t!(FRONTEND_FRAMEWORKS as readonly string[]).includes(data.framework)\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\t\t{\n\t\t\tmessage: `Web apps must have a valid web framework (${FRONTEND_FRAMEWORKS.join(', ')})`,\n\t\t\tpath: ['framework'],\n\t\t},\n\t)\n\t.refine(\n\t\t(data) => {\n\t\t\t// Mobile apps must have a mobile framework\n\t\t\tif (data.type === 'mobile') {\n\t\t\t\tif (\n\t\t\t\t\t!data.framework ||\n\t\t\t\t\t!(MOBILE_FRAMEWORKS as readonly string[]).includes(data.framework)\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\t\t{\n\t\t\tmessage: `Mobile apps must have a valid mobile framework (${MOBILE_FRAMEWORKS.join(', ')})`,\n\t\t\tpath: ['framework'],\n\t\t},\n\t);\n\n/** What `gkm test` hands a feature test. */\nconst TestConfigSchema = z\n\t.object({\n\t\t/** The folder of per-database factories; `test/factories` by default. */\n\t\tfactories: z.string().optional(),\n\t})\n\t.strict();\n\n/** How `gkm dev` is reached from outside the apps it runs. */\nconst DevConfigSchema = z\n\t.object({\n\t\t/** Browser origins that may read the discovery endpoint. */\n\t\tallowedOrigins: z\n\t\t\t.array(\n\t\t\t\tz.url().refine(\n\t\t\t\t\t// Checked even when `z.url()` has already failed it.\n\t\t\t\t\t(value) => URL.canParse(value) && new URL(value).origin === value,\n\t\t\t\t\t{\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t'An origin is a scheme, host and port only, e.g. https://console.example.com — no path or trailing slash',\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t)\n\t\t\t.optional(),\n\t\t/** The discovery endpoint's loopback port; 4983 by default. */\n\t\tdiscoveryPort: z.number().int().min(0).max(65535).optional(),\n\t})\n\t.strict();\n\n/**\n * Workspace configuration schema.\n */\nexport const WorkspaceConfigSchema = z\n\t.object({\n\t\tname: z.string().optional(),\n\t\t/**\n\t\t * Where the workspace's own constructs live, relative to its root.\n\t\t *\n\t\t * Infrastructure is a fact about the product, not about the process that\n\t\t * happens to import it — so a database two apps share is declared once,\n\t\t * at the top, and both find it. An app-level glob still works and is\n\t\t * additive, for something only one app uses.\n\t\t */\n\t\tconstructs: RoutesSchema.optional(),\n\t\t/**\n\t\t * Apps that nothing declares.\n\t\t *\n\t\t * Optional, and normally absent: a `site` is an app and so is a\n\t\t * `rest-api` that named one, so the list is read off the graph. This is\n\t\t * the escape hatch for what no construct describes — a mobile app, a\n\t\t * process someone runs by hand — and for overriding one field of a\n\t\t * derived app without restating it.\n\t\t */\n\t\tapps: z.record(z.string(), AppConfigSchema).optional(),\n\t\tshared: SharedConfigSchema.optional(),\n\t\tdeploy: DeployConfigSchema.optional(),\n\t\tstages: StagesConfigSchema,\n\t\tsecrets: SecretsConfigSchema.optional(),\n\t\tstate: StateConfigSchema.optional(),\n\t\ttest: TestConfigSchema.optional(),\n\t\tdev: DevConfigSchema.optional(),\n\t})\n\t// Strict, so a key this schema no longer has fails instead of being\n\t// dropped: a leftover `services:` block is a question the config used to\n\t// answer, and silently ignoring it would hide that the answer moved.\n\t.strict()\n\t.refine(\n\t\t(data) => {\n\t\t\t// Validate dependencies reference existing apps\n\t\t\tconst appNames = Object.keys(data.apps ?? {});\n\t\t\tfor (const [appName, app] of Object.entries(data.apps ?? {})) {\n\t\t\t\tfor (const dep of app.dependencies ?? []) {\n\t\t\t\t\tif (!appNames.includes(dep)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t// Prevent self-dependency\n\t\t\t\t\tif (dep === appName) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\t\t{\n\t\t\tmessage:\n\t\t\t\t'App dependencies must reference existing apps and cannot be self-referential',\n\t\t},\n\t)\n\t.refine(\n\t\t(data) => {\n\t\t\t// Check for circular dependencies\n\t\t\tconst appNames = Object.keys(data.apps ?? {});\n\t\t\tconst visited = new Set<string>();\n\t\t\tconst recStack = new Set<string>();\n\n\t\t\tfunction hasCycle(app: string): boolean {\n\t\t\t\tif (recStack.has(app)) return true;\n\t\t\t\tif (visited.has(app)) return false;\n\n\t\t\t\tvisited.add(app);\n\t\t\t\trecStack.add(app);\n\n\t\t\t\tconst deps = data.apps?.[app]?.dependencies ?? [];\n\t\t\t\tfor (const dep of deps) {\n\t\t\t\t\tif (hasCycle(dep)) return true;\n\t\t\t\t}\n\n\t\t\t\trecStack.delete(app);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tfor (const app of appNames) {\n\t\t\t\tvisited.clear();\n\t\t\t\trecStack.clear();\n\t\t\t\tif (hasCycle(app)) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\t\t{\n\t\t\tmessage: 'Circular dependencies detected between apps',\n\t\t},\n\t)\n\t.superRefine((data, ctx) => {\n\t\t// Validate deploy targets are supported\n\t\tconst defaultTarget = data.deploy?.default;\n\t\tconst ownTargets = data.deploy?.targets ?? {};\n\t\tif (defaultTarget && !isDeployTargetSupported(defaultTarget, ownTargets)) {\n\t\t\tctx.addIssue({\n\t\t\t\tcode: 'custom',\n\t\t\t\tmessage: getDeployTargetError(defaultTarget),\n\t\t\t\tpath: ['deploy', 'default'],\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const [appName, app] of Object.entries(data.apps ?? {})) {\n\t\t\tif (app.deploy && !isDeployTargetSupported(app.deploy, ownTargets)) {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: getDeployTargetError(app.deploy, appName),\n\t\t\t\t\tpath: ['apps', appName, 'deploy'],\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Validate workspace name is required for SSM state provider\n\t\tif (data.state?.provider === 'ssm' && !data.name) {\n\t\t\tctx.addIssue({\n\t\t\t\tcode: 'custom',\n\t\t\t\tmessage:\n\t\t\t\t\t'Workspace name is required when using SSM state provider. Add \"name\" to your gkm.config.ts.',\n\t\t\t\tpath: ['name'],\n\t\t\t});\n\t\t}\n\t});\n\n/**\n * Validate workspace configuration.\n * Throws ZodError with detailed messages on validation failure.\n */\nexport function validateWorkspaceConfig(\n\tconfig: unknown,\n): z.infer<typeof WorkspaceConfigSchema> {\n\treturn WorkspaceConfigSchema.parse(config);\n}\n\n/**\n * Safe validation that returns result instead of throwing.\n */\nexport function safeValidateWorkspaceConfig(config: unknown): {\n\tsuccess: boolean;\n\tdata?: z.infer<typeof WorkspaceConfigSchema>;\n\terror?: z.ZodError;\n} {\n\tconst result = WorkspaceConfigSchema.safeParse(config);\n\tif (result.success) {\n\t\treturn { success: true, data: result.data };\n\t}\n\treturn { success: false, error: result.error };\n}\n\n/**\n * Format Zod errors into user-friendly messages.\n */\nexport function formatValidationErrors(error: z.ZodError): string {\n\tconst messages = error.issues.map((issue: z.core.$ZodIssue) => {\n\t\tconst path = issue.path.join('.');\n\t\treturn path ? ` - ${path}: ${issue.message}` : ` - ${issue.message}`;\n\t});\n\n\treturn `Workspace configuration validation failed:\\n${messages.join('\\n')}`;\n}\n\nexport type ValidatedWorkspaceConfig = z.infer<typeof WorkspaceConfigSchema>;\n\nexport { SUPPORTED_DEPLOY_TARGETS, PHASE_2_DEPLOY_TARGETS };\n","import type { ConstructManifest } from '@geekmidas/manifest';\nimport type { AwsRegion, StateConfig } from '../deploy/StateProvider.js';\nimport type { DeployTargetEntry } from '../target/types';\n\nexport type { AwsRegion };\n\nimport type {\n\tGkmConfig,\n\tHooksConfig,\n\tOpenApiConfig,\n\tRoutes,\n\tRuntime,\n\tTelescopeConfig,\n} from '../types.js';\nimport type {\n\tBackendFramework,\n\tBackupsConfig,\n\tDnsConfig,\n\tDnsProvider,\n\tFramework,\n\tFrontendFramework,\n\tMobileFramework,\n} from './schema.js';\n\nexport type {\n\tBackendFramework,\n\tBackupsConfig,\n\tDnsConfig,\n\tDnsProvider,\n\tFramework,\n\tFrontendFramework,\n\tMobileFramework,\n};\n\n/**\n * Where a stage — or one app — deploys, by target name: a built-in\n * (`dokploy`, `sst`) or a key of `deploy.targets`.\n *\n * @example\n * ```ts\n * deploy: 'dokploy'\n * deploy: 'acme' // deploy: { targets: { acme: '@acme/gkm-target' } }\n * ```\n */\nexport type DeployTargetName = 'dokploy' | 'compose' | 'sst' | (string & {});\n\n/**\n * Each deployed stage's base domain — `deploy.domains`.\n *\n * A fact about the deployment rather than the platform, so every target reads\n * it. The root site answers on the base domain; every other surface on\n * `{subdomain}.{domain}`, where the subdomain is the construct's own\n * `subdomain`, or its id kebab-cased.\n *\n * @example\n * ```ts\n * deploy: {\n * domains: { production: 'myapp.com', staging: 'staging.myapp.com' },\n * }\n *\n * // production:\n * // - new StaticSite('Web', { root: true }) → myapp.com\n * // - new RestApi('Api', { subdomain: 'api' }) → api.myapp.com\n * // - new RestApi('Webhooks', { … }) → webhooks.myapp.com\n * ```\n */\nexport type DomainsConfig = Record<string, string>;\n\n/**\n * Per-app domain override configuration.\n *\n * Can be a single domain string (used for all stages) or\n * stage-specific domains.\n *\n * @example\n * ```ts\n * // Single domain for all stages\n * domain: 'api.custom.com'\n *\n * // Stage-specific domains\n * domain: {\n * development: 'api.dev.custom.com',\n * staging: 'api.staging.custom.com',\n * production: 'api.custom.com',\n * }\n * ```\n */\nexport type AppDomainConfig = string | Record<string, string>;\n\n/**\n * Dokploy workspace deployment configuration.\n *\n * Configures how the workspace is deployed to a Dokploy server.\n * One workspace maps to one Dokploy project with stage-based environments.\n * Project IDs are stored in deploy state (not config) and created on first deploy.\n *\n * @example Single endpoint for all stages:\n * ```ts\n * deploy: {\n * default: 'dokploy',\n * registry: 'ghcr.io/myorg',\n * dokploy: {\n * endpoint: 'https://dokploy.myserver.com',\n * },\n * }\n * ```\n *\n * @example Per-stage endpoints (different Dokploy servers):\n * ```ts\n * deploy: {\n * default: 'dokploy',\n * registry: 'ghcr.io/myorg',\n * dokploy: {\n * endpoints: {\n * development: 'https://dev-dokploy.myserver.com',\n * production: 'https://dokploy.myserver.com',\n * },\n * },\n * }\n * ```\n */\nexport interface DokployWorkspaceConfig {\n\t/** Dokploy API endpoint for all stages */\n\tendpoint?: string;\n\t/** Per-stage Dokploy API endpoints (overrides endpoint) */\n\tendpoints?: Record<string, string>;\n\t/** Registry ID in Dokploy (auto-configured) */\n\tregistryId?: string;\n\t/** How a release is waited for and checked before it counts as live. */\n\tverify?: DokployVerifyConfig;\n}\n\n/**\n * How a Dokploy release is waited for and checked.\n *\n * A release counts once Dokploy's deployment has finished and the app has\n * answered its health route `healthyAfter` times in a row. One that does not\n * is rolled back.\n */\nexport interface DokployVerifyConfig {\n\t/** How long Dokploy may take to finish a deployment. Default 10 minutes. */\n\tdeploymentTimeoutMs?: number;\n\t/**\n\t * The path each backend is checked on, under its domain. Default\n\t * `/health`, which every production server build serves. A site is\n\t * checked at `/`.\n\t */\n\thealthCheckPath?: string;\n\t/** Consecutive 2xx answers that make an app healthy. Default 3. */\n\thealthyAfter?: number;\n\t/** Between health checks, and the first wait between polls. Default 2s. */\n\tintervalMs?: number;\n\t/** How long an app may take to become healthy. Default 5 minutes. */\n\thealthTimeoutMs?: number;\n}\n\n/**\n * The `compose` target's own settings — `deploy.compose`. Read by `gkm\n * compose` and `gkm deploy --target compose` only; the Dokploy target never\n * consults it.\n *\n * @example\n * ```ts\n * deploy: { compose: { logs: true } }\n * deploy: { compose: { logs: { port: 5081, retentionDays: 14 } } }\n * deploy: { compose: { logs: { public: { allow: ['203.0.113.7', '10.0.0.0/8'] } } } }\n * ```\n */\nexport interface ComposeWorkspaceConfig {\n\t/**\n\t * Run OpenObserve in the stack and send every backend's logs and traces\n\t * to it. `true` for the defaults.\n\t */\n\tlogs?: boolean | ComposeLogsConfig;\n}\n\n/** How the stack's OpenObserve is run and reached — `deploy.compose.logs`. */\nexport interface ComposeLogsConfig {\n\t/**\n\t * The port it is published on, on 127.0.0.1 only — reached from another\n\t * machine through an SSH tunnel. Default 5080.\n\t */\n\tport?: number;\n\t/**\n\t * How many days of data it keeps (OpenObserve's\n\t * `ZO_COMPACT_DATA_RETENTION_DAYS`). At least 3; default 30.\n\t */\n\tretentionDays?: number;\n\t/**\n\t * Serve it on `logs.<stage domain>` through the stack's Caddy, to these\n\t * addresses only (IPs or CIDRs), instead of on a loopback port. Every\n\t * other address is answered 403.\n\t */\n\tpublic?: { allow: string[] };\n}\n\n/**\n * DNS provider types for automatic DNS record creation.\n */\nexport type DnsProviderType = 'hostinger' | 'route53' | 'cloudflare' | 'manual';\n\n/**\n * Deployment configuration for the workspace.\n *\n * @example\n * ```ts\n * // Minimal - just set default target\n * deploy: {\n * default: 'dokploy',\n * }\n *\n * // Full configuration with DNS and backups\n * deploy: {\n * default: 'dokploy',\n * registry: 'ghcr.io/myorg',\n * dokploy: {\n * endpoint: 'https://dokploy.myserver.com',\n * projectId: 'proj_abc123',\n * domains: {\n * production: 'myapp.com',\n * },\n * },\n * dns: {\n * provider: 'hostinger',\n * domain: 'myapp.com',\n * },\n * backups: {\n * type: 's3',\n * region: 'us-east-1',\n * },\n * }\n * ```\n */\nexport interface DeployConfig {\n\t/** Default deploy target for all apps (default: 'dokploy') */\n\tdefault?: DeployTargetName;\n\t/**\n\t * Targets that do not ship with the CLI, by the name `default` (or an\n\t * app's `deploy`) uses: a package whose default export is the target, a\n\t * target object, or either with its options.\n\t *\n\t * ```ts\n\t * targets: {\n\t * acme: '@acme/gkm-target',\n\t * fly: ['@acme/gkm-fly', { org: 'acme' }],\n\t * local: defineTarget({ … }),\n\t * }\n\t * ```\n\t *\n\t * A package is never guessed from a name: it is installed and listed here.\n\t * A built-in's name cannot be taken.\n\t */\n\ttargets?: Record<string, DeployTargetEntry>;\n\t/**\n\t * Whose deploy this is, on a target shared with other workspaces — an\n\t * organisation or a team, lowercase `[a-z0-9-]`. Two workspaces with the\n\t * same name deploying to one Dokploy are kept apart by it: it is in the\n\t * project's ownership marker, the image path\n\t * (`<registry>/<namespace>/<project>-<app>`) and every resource name.\n\t *\n\t * Defaults to the kebab-cased workspace name, which adds nothing to the\n\t * names a workspace was already deployed under.\n\t */\n\tnamespace?: string;\n\t/** Each deployed stage's base domain — see {@link DomainsConfig}. */\n\tdomains?: DomainsConfig;\n\t/**\n\t * The container registry the apps' images are pushed to and pulled from,\n\t * whatever the target — `ghcr.io/myorg`. An image is\n\t * `<registry>/<namespace>/<project>-<app>:<tag>`.\n\t */\n\tregistry?: string;\n\t/** Dokploy-specific configuration */\n\tdokploy?: DokployWorkspaceConfig;\n\t/** What the `compose` target runs beside the apps — see {@link ComposeWorkspaceConfig}. */\n\tcompose?: ComposeWorkspaceConfig;\n\t/** DNS configuration for automatic record creation */\n\tdns?: DnsConfig;\n\t/** Backup destination configuration for database services */\n\tbackups?: BackupsConfig;\n}\n\n/**\n * Models package configuration for shared schemas.\n *\n * Configures a shared models package containing Zod schemas\n * that can be used across backend and frontend apps.\n *\n * @example\n * ```ts\n * shared: {\n * models: {\n * path: 'packages/models',\n * schema: 'zod',\n * },\n * }\n * ```\n */\nexport interface ModelsConfig {\n\t/** Path to models package relative to workspace root (default: 'packages/models') */\n\tpath?: string;\n\t/**\n\t * Schema library to use (default: 'zod').\n\t * Currently only 'zod' is supported.\n\t * Future: any StandardSchema-compatible library\n\t */\n\tschema?: 'zod';\n}\n\n/**\n * Shared packages configuration.\n *\n * Configures shared packages in the monorepo that are\n * used by multiple apps.\n *\n * @example\n * ```ts\n * shared: {\n * packages: ['packages/*', 'libs/*'],\n * models: {\n * path: 'packages/models',\n * schema: 'zod',\n * },\n * }\n * ```\n */\nexport interface SharedConfig {\n\t/** Glob patterns for shared packages (default: ['packages/*']) */\n\tpackages?: string[];\n\t/** Models package configuration */\n\tmodels?: ModelsConfig;\n}\n\n/**\n * The project's stages, named by the project.\n *\n * Stage names were literals scattered through the CLI — `gkm dev` ran as\n * `development` and looked for `dev` secrets first, a deploy script said\n * `production` — so a team that called its stages anything else was\n * working against the tool. They are declared once, here, and read.\n *\n * @example\n * ```ts\n * stages: { local: 'dev', deployed: ['staging', 'prod'], protected: ['prod'] }\n * ```\n */\nexport interface StagesConfig {\n\t/**\n\t * What `gkm dev`, `exec`, `setup` and `test` run as: the stage whose\n\t * secrets are read locally, and whose containers carry no suffix.\n\t *\n\t * Never also a deployed stage. Secrets are stored per stage name, so a\n\t * deployed stage sharing it would share its secrets with every laptop.\n\t */\n\tlocal: string;\n\t/** The stages `gkm deploy --stage` accepts. */\n\tdeployed: string[];\n\t/**\n\t * Deployed stages whose resources outlive the stack that made them —\n\t * retained on removal and protected, e.g. the stage users are on.\n\t */\n\tprotected?: string[];\n}\n\n/**\n * Secrets encryption configuration.\n *\n * Configures how secrets are encrypted for deployment.\n * Secrets are stored encrypted in `.gkm/secrets/{stage}.json`\n * with keys stored separately in `~/.gkm/keys/{namespace}/{project}/{stage}.key`.\n *\n * @example\n * ```ts\n * secrets: {\n * enabled: true,\n * algorithm: 'aes-256-gcm',\n * kdf: 'scrypt',\n * }\n * ```\n */\nexport interface SecretsConfig {\n\t/** Enable encrypted secrets (default: true) */\n\tenabled?: boolean;\n\t/**\n\t * Where deployed stages' secrets live, so a deploy can reach them from\n\t * anywhere: `'file'` (default — the encrypted `.gkm/secrets/<stage>.json`,\n\t * which cannot serve CI while `.gkm/` is gitignored), `{ provider: 'ssm',\n\t * region }` (each stage's secrets in an SSM parameter in its own AWS\n\t * account, up to 8 KB), `{ provider: 'secrets-manager', region, kmsKeyId? }`\n\t * (a Secrets Manager secret in its own account, up to 64 KB), or any object\n\t * implementing `SecretsStore`. The local stage always uses the file.\n\t */\n\tstore?: import('../secrets/store.js').SecretsStoreConfig;\n\t/** Encryption algorithm (default: 'aes-256-gcm') */\n\talgorithm?: string;\n\t/** Key derivation function (default: 'scrypt') */\n\tkdf?: 'scrypt' | 'pbkdf2';\n}\n\n/**\n * Base app configuration properties (shared between input and normalized).\n *\n * @example\n * ```ts\n * // Backend app with gkm routes\n * api: {\n * type: 'backend',\n * path: 'apps/api',\n * port: 3000,\n * routes: './src/endpoints/**\\/*.ts',\n * envParser: './src/config/env',\n * logger: './src/config/logger',\n * }\n *\n * // Backend app with entry point (e.g., Better Auth)\n * auth: {\n * type: 'backend',\n * path: 'apps/auth',\n * port: 3001,\n * entry: './src/index.ts',\n * framework: 'better-auth',\n * requiredEnv: ['DATABASE_URL', 'BETTER_AUTH_SECRET'],\n * }\n *\n * // Frontend app\n * web: {\n * type: 'web',\n * path: 'apps/web',\n * port: 3002,\n * framework: 'nextjs',\n * dependencies: ['api', 'auth'],\n * }\n * ```\n */\ninterface AppConfigBase {\n\t/**\n\t * App type.\n\t * - 'backend': Server-side app (API, auth service, etc.)\n\t * - 'web': Client-side web app (Next.js, Vite, TanStack Start, etc.)\n\t * - 'mobile': Native mobile app (Expo). Built/deployed via its own\n\t * toolchain (e.g. EAS Build) — Docker/Dokploy steps are skipped.\n\t * @default 'backend'\n\t */\n\ttype?: 'backend' | 'web' | 'mobile';\n\n\t/**\n\t * Path to the app relative to workspace root.\n\t * @example 'apps/api', 'apps/web', 'services/auth'\n\t */\n\tpath: string;\n\n\t/**\n\t * Development server port.\n\t * Must be unique across all apps in the workspace.\n\t * @example 3000, 3001, 3002\n\t */\n\tport: number;\n\n\t/**\n\t * Per-app deploy target override.\n\t * Overrides `deploy.default` for this specific app.\n\t * @example 'dokploy', 'vercel'\n\t */\n\tdeploy?: DeployTargetName;\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Backend-specific (gkm routes mode)\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Constructs glob pattern — one glob, every kind.\n\t *\n\t * A glob per kind cannot find a resource, because a declared `ObjectStorage`\n\t * has no kind to be listed under. This one is what reconcile reads to derive\n\t * the containers a stage needs, and it replaces the per-kind globs below.\n\t *\n\t * @example './src/**\\/*.ts'\n\t */\n\tconstructs?: Routes;\n\n\t/**\n\t * Routes glob pattern for gkm endpoints.\n\t * @example './src/endpoints/**\\/*.ts'\n\t */\n\troutes?: Routes;\n\n\t/**\n\t * Functions glob pattern for Lambda functions.\n\t * @example './src/functions/**\\/*.ts'\n\t */\n\tfunctions?: Routes;\n\n\t/**\n\t * Crons glob pattern for scheduled tasks.\n\t * @example './src/crons/**\\/*.ts'\n\t */\n\tcrons?: Routes;\n\n\t/**\n\t * Queues glob pattern for point-to-point workers.\n\t * @example './src/queues/**\\/*.ts'\n\t */\n\tqueues?: Routes;\n\n\t/**\n\t * Topics glob pattern for the topics subscribers bind to.\n\t * @example './src/topics/**\\/*.ts'\n\t */\n\ttopics?: Routes;\n\n\t/**\n\t * Subscribers glob pattern for event handlers.\n\t * @example './src/subscribers/**\\/*.ts'\n\t */\n\tsubscribers?: Routes;\n\n\t/**\n\t * Path to environment parser module.\n\t * @example './src/config/env'\n\t */\n\tenvParser?: string;\n\n\t/**\n\t * Path to logger module.\n\t * @example './src/config/logger'\n\t */\n\tlogger?: string;\n\n\t/**\n\t * Server lifecycle hooks.\n\t * @example { beforeSetup: './src/hooks/setup.ts' }\n\t */\n\thooks?: HooksConfig;\n\n\t/**\n\t * Telescope request recording configuration.\n\t * @example true, './src/config/telescope', { enabled: true, path: '/__telescope' }\n\t */\n\ttelescope?: string | boolean | TelescopeConfig;\n\n\t/**\n\t * OpenAPI documentation configuration.\n\t * @example true, { output: './src/openapi.ts' }\n\t */\n\topenapi?: boolean | OpenApiConfig;\n\n\t/**\n\t * Runtime environment.\n\t * @default 'node'\n\t */\n\truntime?: Runtime;\n\n\t/**\n\t * Environment file(s) to load during development.\n\t * @example '.env', ['.env', '.env.local']\n\t */\n\tenv?: string | string[];\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Entry point mode (non-gkm apps)\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Entry file path for apps that don't use gkm routes.\n\t *\n\t * When specified, the app is run directly with tsx in development\n\t * and bundled with esbuild for production Docker builds.\n\t *\n\t * Use this for:\n\t * - Better Auth servers\n\t * - Custom Hono/Express apps\n\t * - Any backend that doesn't use gkm's endpoint builder\n\t *\n\t * @example './src/index.ts', './src/server.ts'\n\t */\n\tentry?: string;\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Frontend-specific\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Framework for the app.\n\t *\n\t * Backend frameworks: 'hono', 'better-auth', 'express', 'fastify'\n\t * Frontend frameworks: 'nextjs', 'remix', 'vite', 'tanstack-start'\n\t * Mobile frameworks: 'expo'\n\t *\n\t * @example 'nextjs', 'better-auth', 'hono', 'expo'\n\t */\n\tframework?: Framework;\n\n\t/**\n\t * Config file paths for frontend environment sniffing.\n\t *\n\t * Points to file(s) that call EnvironmentParser.parse() at import time.\n\t * The sniffer imports these files and captures all env vars accessed.\n\t *\n\t * Dependencies are auto-generated as public-prefixed URL vars based on\n\t * the app's framework (e.g. `NEXT_PUBLIC_{DEP}_URL` for Next.js,\n\t * `VITE_{DEP}_URL` for Vite/TanStack Start, `EXPO_PUBLIC_{DEP}_URL`\n\t * for Expo).\n\t *\n\t * @example\n\t * ```ts\n\t * config: {\n\t * client: './src/config/client.ts', // public-prefixed vars for browser\n\t * server: './src/config/server.ts', // server-only vars for SSR\n\t * }\n\t * ```\n\t */\n\tconfig?: {\n\t\t/** Client-side config (public-prefixed vars, available in browser/device) */\n\t\tclient?: string;\n\t\t/** Server-side config (all env vars, for SSR/API routes) */\n\t\tserver?: string;\n\t};\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Deployment\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Override domain for this app.\n\t *\n\t * By default, apps get `{appName}.{baseDomain}` (or just `{baseDomain}`\n\t * for the main frontend). Use this to specify a custom domain.\n\t *\n\t * @example\n\t * ```ts\n\t * // Single domain for all stages\n\t * domain: 'api.custom.com'\n\t *\n\t * // Stage-specific domains\n\t * domain: {\n\t * production: 'api.custom.com',\n\t * staging: 'api.staging.custom.com',\n\t * }\n\t * ```\n\t */\n\tdomain?: AppDomainConfig;\n\n\t/**\n\t * Required environment variables for entry-based apps.\n\t *\n\t * Use this instead of envParser for apps that don't use gkm routes.\n\t * The deploy command uses this list to filter which secrets to embed\n\t * in the Docker image.\n\t *\n\t * @example ['DATABASE_URL', 'BETTER_AUTH_SECRET', 'REDIS_URL']\n\t */\n\trequiredEnv?: string[];\n}\n\n/**\n * App configuration input with type-safe dependencies.\n *\n * @template TAppNames - Union of valid app names in the workspace (auto-inferred)\n *\n * @example\n * ```ts\n * // Dependencies are type-checked against app names\n * apps: {\n * api: { path: 'apps/api', port: 3000 },\n * auth: { path: 'apps/auth', port: 3001 },\n * web: {\n * path: 'apps/web',\n * port: 3002,\n * type: 'web',\n * dependencies: ['api', 'auth'], // ✓ Valid\n * // dependencies: ['invalid'], // ✗ Type error\n * },\n * }\n * ```\n */\nexport interface AppConfigInput<TAppNames extends string = string>\n\textends AppConfigBase {\n\t/** Dependencies on other apps in the workspace (type-safe) */\n\tdependencies?: TAppNames[];\n}\n\n/**\n * App configuration (legacy, for backwards compatibility).\n * @deprecated Use AppConfigInput for new code\n */\nexport interface AppConfig extends AppConfigBase {\n\t/** Dependencies on other apps in the workspace */\n\tdependencies?: string[];\n}\n\n/**\n * Base app input type for type inference.\n */\nexport type AppInput = AppConfigBase & {\n\tdependencies?: readonly string[];\n};\n\n/**\n * Apps record type for workspace configuration.\n */\nexport type AppsRecord = Record<string, AppInput>;\n\n/**\n * Constrain apps so dependencies only reference valid app names.\n * Dependencies must be an array of valid app names from the workspace.\n */\nexport type ConstrainedApps<TApps extends AppsRecord> = {\n\t[K in keyof TApps]: Omit<TApps[K], 'dependencies'> & {\n\t\tdependencies?: readonly (keyof TApps & string)[];\n\t};\n};\n\n/**\n * Full workspace input type with constrained dependencies.\n *\n * @example\n * ```ts\n * import { defineWorkspace } from '@geekmidas/cli';\n *\n * export default defineWorkspace({\n * name: 'my-app',\n * apps: {\n * api: {\n * path: 'apps/api',\n * port: 3000,\n * routes: './src/endpoints/**\\/*.ts',\n * },\n * web: {\n * type: 'web',\n * path: 'apps/web',\n * port: 3001,\n * framework: 'nextjs',\n * dependencies: ['api'],\n * },\n * },\n * deploy: {\n * default: 'dokploy',\n * },\n * });\n * ```\n */\nexport type WorkspaceInput<TApps extends AppsRecord> = {\n\t/**\n\t * Where the workspace's own constructs live, relative to its root.\n\t *\n\t * The product's infrastructure, declared once for every app that consumes\n\t * it. Additive with an app's own glob rather than replacing it.\n\t */\n\tconstructs?: Routes;\n\t/** Workspace name (defaults to root package.json name) */\n\tname?: string;\n\t/**\n\t * Apps that nothing declares.\n\t *\n\t * Normally absent: a `site` is an app and so is a `rest-api` that named\n\t * one, so the list is read off the graph. This is the escape hatch for what\n\t * no construct describes.\n\t */\n\tapps?: ConstrainedApps<TApps>;\n\t/** Shared packages configuration */\n\tshared?: SharedConfig;\n\t/** Deployment configuration */\n\tdeploy?: DeployConfig;\n\t/** The project's stages: which one is local, which deploy */\n\tstages: StagesConfig;\n\t/** Encrypted secrets configuration */\n\tsecrets?: SecretsConfig;\n\t/** State provider configuration (local filesystem by default, or SSM for team collaboration) */\n\tstate?: StateConfig;\n\t/** How `gkm dev` is reached: the discovery endpoint's port and origins */\n\tdev?: DevConfig;\n};\n\n/**\n * How `gkm dev` is reached from outside the apps it runs.\n */\nexport interface DevConfig {\n\t/**\n\t * Browser origins allowed to read the discovery endpoint — and this\n\t * workspace in it — e.g. `['https://console.example.com']`.\n\t *\n\t * Empty by default: any page the developer visits can send requests to a\n\t * loopback port, so none is answered unless it is listed here. A request\n\t * with no `Origin` (curl, a local process) still needs the session token.\n\t */\n\tallowedOrigins?: string[];\n\t/**\n\t * The loopback port the discovery endpoint listens on. Defaults to 4983;\n\t * `GKM_DISCOVERY_PORT` overrides it.\n\t */\n\tdiscoveryPort?: number;\n}\n\n/**\n * How `gkm test` builds what a feature test is handed.\n */\nexport interface TestConfig {\n\t/**\n\t * The folder of test factories, relative to the root.\n\t *\n\t * Defaults to `test/factories`. A factory belongs to a database, not to an\n\t * app, so each file is named after one — `database.ts` for `Database`,\n\t * `auth-database.ts` for `AuthDatabase` — and exports `createFactory(db)`.\n\t * Every app's harness builds them on each test's transactions, and a test is\n\t * handed them as `factories`, keyed by service name.\n\t */\n\tfactories?: string;\n}\n\n/**\n * Extract app names from apps record.\n */\nexport type InferAppNames<TApps extends AppsRecord> = keyof TApps & string;\n\n/**\n * Inferred workspace config with proper app name types.\n */\nexport type InferredWorkspaceConfig<TApps extends AppsRecord> = {\n\t/**\n\t * Where the workspace's own constructs live, relative to its root.\n\t *\n\t * The product's infrastructure, declared once for every app that consumes\n\t * it. Additive with an app's own glob rather than replacing it.\n\t */\n\tconstructs?: Routes;\n\tname?: string;\n\tapps?: {\n\t\t[K in keyof TApps]: Omit<TApps[K], 'dependencies'> & {\n\t\t\tdependencies?: InferAppNames<TApps>[];\n\t\t};\n\t};\n\tshared?: SharedConfig;\n\tdeploy?: DeployConfig;\n\tstages: StagesConfig;\n\tsecrets?: SecretsConfig;\n\tstate?: StateConfig;\n\ttest?: TestConfig;\n\tdev?: DevConfig;\n};\n\n// Legacy types for backwards compatibility\n/** @deprecated Use WorkspaceInput */\nexport type RawWorkspaceInput = {\n\tname?: string;\n\tapps: AppsRecord;\n\tshared?: SharedConfig;\n\tdeploy?: DeployConfig;\n\tsecrets?: SecretsConfig;\n\tstate?: StateConfig;\n};\n\n/** @deprecated Use WorkspaceInput */\nexport type WorkspaceConfigInput<\n\tT extends RawWorkspaceInput = RawWorkspaceInput,\n> = WorkspaceInput<T['apps']>;\n\n/**\n * Workspace configuration for multi-app monorepos.\n *\n * Use `defineWorkspace()` helper for type-safe configuration with\n * auto-completion and dependency validation.\n *\n * @example\n * ```ts\n * // gkm.config.ts\n * import { defineWorkspace } from '@geekmidas/cli';\n *\n * export default defineWorkspace({\n * name: 'my-saas',\n *\n * // App definitions\n * apps: {\n * // Backend API with gkm routes\n * api: {\n * path: 'apps/api',\n * port: 3000,\n * routes: './src/endpoints/**\\/*.ts',\n * envParser: './src/config/env',\n * logger: './src/config/logger',\n * telescope: true,\n * },\n *\n * // Better Auth service\n * auth: {\n * path: 'apps/auth',\n * port: 3001,\n * entry: './src/index.ts',\n * framework: 'better-auth',\n * requiredEnv: ['DATABASE_URL', 'BETTER_AUTH_SECRET'],\n * },\n *\n * // Next.js frontend\n * web: {\n * type: 'web',\n * path: 'apps/web',\n * port: 3002,\n * framework: 'nextjs',\n * dependencies: ['api', 'auth'],\n * },\n * },\n *\n * // Deployment configuration\n * deploy: {\n * default: 'dokploy',\n * registry: 'ghcr.io/myorg',\n * domains: {\n * production: 'myapp.com',\n * staging: 'staging.myapp.com',\n * },\n * dokploy: {\n * endpoint: 'https://dokploy.myserver.com',\n * projectId: 'proj_abc123',\n * },\n * },\n *\n * // Shared packages\n * shared: {\n * packages: ['packages/*'],\n * models: { path: 'packages/models' },\n * },\n * });\n * ```\n *\n * @deprecated Use WorkspaceInput with defineWorkspace for type inference\n */\nexport interface WorkspaceConfig {\n\t/**\n\t * Where the workspace's own constructs live, relative to its root.\n\t *\n\t * The product's infrastructure, declared once for every app that consumes\n\t * it. Additive with an app's own glob rather than replacing it.\n\t */\n\tconstructs?: Routes;\n\t/** Workspace name (defaults to root package.json name) */\n\tname?: string;\n\n\t/** Apps that nothing declares. Normally absent — see `WorkspaceInput`. */\n\tapps?: Record<string, AppConfig>;\n\n\t/** Shared packages configuration */\n\tshared?: SharedConfig;\n\n\t/** Default deployment configuration */\n\tdeploy?: DeployConfig;\n\n\t/** The project's stages: which one is local, which deploy */\n\tstages: StagesConfig;\n\n\t/** Encrypted secrets configuration */\n\tsecrets?: SecretsConfig;\n\n\t/** State provider configuration (local filesystem by default, or SSM for team collaboration) */\n\tstate?: StateConfig;\n\n\t/** What `gkm test` hands a feature test: where the factories are. */\n\ttest?: TestConfig;\n\n\t/** How `gkm dev` is reached: the discovery endpoint's port and origins. */\n\tdev?: DevConfig;\n}\n\n/**\n * Normalized app configuration with resolved defaults.\n *\n * This is the internal representation after processing user input.\n * All optional fields have been resolved to their defaults.\n */\nexport interface NormalizedAppConfig extends Omit<AppConfigBase, 'type'> {\n\t/**\n\t * Whether the base domain points at this site.\n\t *\n\t * Carried from the `site` declaration rather than configured, because which\n\t * site is primary is structural — its *hostname* is what varies by stage,\n\t * and that is `domain`.\n\t */\n\troot?: boolean;\n\n\t/**\n\t * The label this app answers on under a stage's domain, carried from its\n\t * declaration — `api` for `api.myapp.com`. Absent, the app's own name.\n\t */\n\tsubdomain?: string;\n\n\t/** App type (always defined after normalization) */\n\ttype: 'backend' | 'web' | 'mobile';\n\t/** Path to the app */\n\tpath: string;\n\t/** Development server port */\n\tport: number;\n\t/** Resolved dependencies array (empty array if none) */\n\tdependencies: string[];\n\t/** Resolved deploy target (app.deploy > deploy.default > 'dokploy') */\n\tresolvedDeployTarget: DeployTargetName;\n\t/** Entry file path for non-gkm apps */\n\tentry?: string;\n\t/** Framework for the app */\n\tframework?: Framework;\n\t/** Override domain for this app */\n\tdomain?: AppDomainConfig;\n\t/** Required environment variables for entry-based apps */\n\trequiredEnv?: string[];\n}\n\n/**\n * Normalized workspace configuration with resolved defaults.\n *\n * This is the internal representation after processing user input.\n * All optional fields have been resolved to their defaults.\n */\nexport interface NormalizedWorkspace {\n\t/** Workspace name (resolved from package.json if not specified) */\n\tname: string;\n\t/** Absolute path to workspace root */\n\troot: string;\n\t/**\n\t * The workspace's own constructs glob, relative to `root`.\n\t *\n\t * Where the product's shared infrastructure is declared. Additive with each\n\t * app's glob, so an app can still declare something only it uses.\n\t */\n\tconstructs?: Routes;\n\t/** Normalized app configurations */\n\tapps: Record<string, NormalizedAppConfig>;\n\t/** Deploy configuration (empty object if not specified) */\n\tdeploy: DeployConfig;\n\t/** Shared packages configuration (empty object if not specified) */\n\tshared: SharedConfig;\n\t/** The project's stages, as declared */\n\tstages: StagesConfig;\n\t/** Secrets configuration (empty object if not specified) */\n\tsecrets: SecretsConfig;\n\t/** State provider configuration (undefined = local filesystem) */\n\tstate?: StateConfig;\n\t/** What `gkm test` hands a feature test (empty object if not specified) */\n\ttest: TestConfig;\n\t/** How `gkm dev` is reached, when configured */\n\tdev?: DevConfig;\n}\n\n/**\n * Result of loading and processing a configuration.\n */\nexport interface LoadedConfig {\n\t/** Whether this is a single-app or workspace config */\n\ttype: 'single' | 'workspace';\n\t/** The raw configuration as loaded */\n\traw: GkmConfig | WorkspaceConfig;\n\t/** Normalized workspace (always available) */\n\tworkspace: NormalizedWorkspace;\n\t/**\n\t * What the workspace's constructs declared, when it has any.\n\t *\n\t * Read once at load, because the apps above are derived from it and a\n\t * caller that re-discovers would be answering the same question twice with\n\t * two chances to disagree. Absent when the workspace declares no constructs.\n\t */\n\tmanifest?: ConstructManifest;\n\t/**\n\t * The files each Worker's crons, queues and subscribers are declared in,\n\t * read in the same discovery — what says which app a worker is built from.\n\t */\n\tbackground?: Record<string, string[]>;\n}\n\n/**\n * Type guard to check if a config is a WorkspaceConfig.\n *\n * @example\n * ```ts\n * const config = await loadConfig();\n * if (isWorkspaceConfig(config)) {\n * // config.apps is available\n * console.log(Object.keys(config.apps));\n * }\n * ```\n */\nexport function isWorkspaceConfig(\n\tconfig: GkmConfig | WorkspaceConfig,\n): config is WorkspaceConfig {\n\tif (typeof config !== 'object' || config === null) return false;\n\n\t// `apps` used to be the only tell, and it stopped being one when apps\n\t// became derived: a workspace that declares its apps through constructs has\n\t// none. `constructs` at the top level is the other tell — a single-app\n\t// `defineConfig` has no notion of a workspace-wide glob — and either alone\n\t// is enough.\n\tif ('apps' in config && typeof config.apps === 'object') return true;\n\n\t// Only when nothing else says it is one app. `openapi`, `telescope`,\n\t// `hooks` and the rest configure a single process, so a config that\n\t// sets any of them is that process's — the workspace schema is strict,\n\t// and reading one of these as a workspace would reject what it was never\n\t// meant to accept.\n\treturn (\n\t\t'constructs' in config &&\n\t\tObject.keys(config).every((key) => WORKSPACE_KEYS.has(key))\n\t);\n}\n\n/** Every top-level key a workspace config has — see `WorkspaceConfigSchema`. */\nconst WORKSPACE_KEYS: ReadonlySet<string> = new Set([\n\t'name',\n\t'constructs',\n\t'apps',\n\t'shared',\n\t'deploy',\n\t'stages',\n\t'secrets',\n\t'state',\n]);\n","import type { Framework } from './types.js';\n\n/**\n * All public env-var prefixes the toolbox understands.\n *\n * Used when resolving incoming var names back to a dependency\n * (e.g. `VITE_AUTH_URL` → `auth`). A var matching any of these\n * is considered safe to inline into a client bundle.\n */\nexport const PUBLIC_ENV_PREFIXES = [\n\t'NEXT_PUBLIC_',\n\t'VITE_',\n\t'EXPO_PUBLIC_',\n] as const;\n\n/**\n * Resolve the public env-var prefix that a frontend framework's bundler\n * inlines into client code at build time:\n *\n * - `nextjs` → `NEXT_PUBLIC_`\n * - `vite` → `VITE_`\n * - `tanstack-start` → `VITE_` (uses Vite under the hood)\n * - `expo` → `EXPO_PUBLIC_`\n * - `remix` → `''` (Remix exposes via loaders, no prefix convention)\n *\n * For backend frameworks or unspecified, falls back to `NEXT_PUBLIC_` to\n * preserve the historical default. Backend apps never read these vars\n * from a bundle, so the prefix only affects scaffolding/symmetry.\n */\nexport function getPublicEnvPrefix(framework?: Framework): string {\n\tswitch (framework) {\n\t\tcase 'vite':\n\t\tcase 'tanstack-start':\n\t\t\treturn 'VITE_';\n\t\tcase 'expo':\n\t\t\treturn 'EXPO_PUBLIC_';\n\t\tcase 'remix':\n\t\t\treturn '';\n\t\tcase 'nextjs':\n\t\t\treturn 'NEXT_PUBLIC_';\n\t\tdefault:\n\t\t\treturn 'NEXT_PUBLIC_';\n\t}\n}\n\n/**\n * Strip a known public prefix from a var name.\n * Returns the un-prefixed name, or `null` if no prefix matched.\n */\nexport function stripPublicPrefix(name: string): string | null {\n\tfor (const prefix of PUBLIC_ENV_PREFIXES) {\n\t\tif (prefix && name.startsWith(prefix)) {\n\t\t\treturn name.slice(prefix.length);\n\t\t}\n\t}\n\treturn null;\n}\n","import { basename, isAbsolute, join } from 'node:path';\nimport { assertKnownSecretsStore } from '../secrets/providers.js';\nimport type { GkmConfig, Routes } from '../types.js';\nimport {\n\tformatValidationErrors,\n\tsafeValidateWorkspaceConfig,\n} from './schema.js';\nimport { validateStages } from './stages.js';\nimport type {\n\tAppConfig,\n\tAppsRecord,\n\tDeployTargetName,\n\tInferredWorkspaceConfig,\n\tLoadedConfig,\n\tNormalizedAppConfig,\n\tNormalizedWorkspace,\n\tWorkspaceConfig,\n\tWorkspaceInput,\n} from './types.js';\nimport { isWorkspaceConfig } from './types.js';\n\nexport {\n\tgetPublicEnvPrefix,\n\tPUBLIC_ENV_PREFIXES,\n\tstripPublicPrefix,\n} from './publicEnv.js';\nexport {\n\tformatValidationErrors,\n\tgetDeployTargetError,\n\tisDeployTargetSupported,\n\tisPhase2DeployTarget,\n\tPHASE_2_DEPLOY_TARGETS,\n\tSUPPORTED_DEPLOY_TARGETS,\n\tsafeValidateWorkspaceConfig,\n\tvalidateWorkspaceConfig,\n\tWorkspaceConfigSchema,\n} from './schema.js';\n// Re-export types\nexport type {\n\tAppConfig,\n\tAppConfigInput,\n\tAppInput,\n\tAppsRecord,\n\tBackendFramework,\n\tComposeLogsConfig,\n\tComposeWorkspaceConfig,\n\tConstrainedApps,\n\tDeployConfig,\n\tDeployTargetName,\n\tDokployWorkspaceConfig,\n\tFrontendFramework,\n\tInferAppNames,\n\tInferredWorkspaceConfig,\n\tLoadedConfig,\n\tMobileFramework,\n\tModelsConfig,\n\tNormalizedAppConfig,\n\tNormalizedWorkspace,\n\tSecretsConfig,\n\tSharedConfig,\n\tWorkspaceConfig,\n\tWorkspaceInput,\n} from './types.js';\nexport { isWorkspaceConfig } from './types.js';\n\n/**\n * Validate that all dependencies reference existing apps.\n * Returns the config if valid, throws otherwise.\n */\nfunction validateDependencies<TApps extends AppsRecord>(apps: TApps): void {\n\tconst appNames = new Set(Object.keys(apps));\n\n\tfor (const [appName, app] of Object.entries(apps)) {\n\t\tfor (const dep of app.dependencies ?? []) {\n\t\t\tif (!appNames.has(dep)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid dependency: App \"${appName}\" depends on \"${dep}\" which does not exist. ` +\n\t\t\t\t\t\t`Valid apps are: ${[...appNames].join(', ')}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (dep === appName) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid dependency: App \"${appName}\" cannot depend on itself.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Define workspace configuration with full TypeScript support and type inference.\n *\n * Uses `const` type parameter to infer literal app names, providing:\n * - Autocomplete for app names in dependencies\n * - Type errors for invalid dependency references\n * - Full type inference for the returned config\n *\n * @example\n * ```ts\n * // gkm.config.ts\n * import { defineWorkspace } from '@geekmidas/cli';\n *\n * export default defineWorkspace({\n * name: 'my-saas',\n * apps: {\n * api: {\n * type: 'backend',\n * path: 'apps/api',\n * port: 3000,\n * routes: './src/endpoints/**\\/*.ts',\n * envParser: './src/config/env',\n * logger: './src/logger',\n * },\n * web: {\n * type: 'web',\n * framework: 'nextjs',\n * path: 'apps/web',\n * port: 3001,\n * dependencies: ['api'], // <- autocomplete shows 'api' | 'web'\n * },\n * },\n * });\n *\n * // config.apps.api <- full type inference\n * // config.apps.foo <- TypeScript error\n * ```\n */\nexport function defineWorkspace<const TApps extends AppsRecord>(\n\tconfig: WorkspaceInput<TApps>,\n): InferredWorkspaceConfig<TApps> {\n\tassertNoMovedDeployKeys(config.deploy);\n\tassertKnownSecretsStore(config.secrets?.store);\n\n\t// Validate dependencies at runtime\n\tif (config.apps) validateDependencies(config.apps as unknown as TApps);\n\n\t// Validate with Zod schema\n\tconst result = safeValidateWorkspaceConfig(config);\n\tif (!result.success && result.error) {\n\t\tthrow new Error(formatValidationErrors(result.error));\n\t}\n\n\treturn config as unknown as InferredWorkspaceConfig<TApps>;\n}\n\n/**\n * Get the package name from package.json in the given directory.\n */\nfunction getPackageName(cwd: string): string | undefined {\n\ttry {\n\t\t// Dynamic import would be async, so we use require for sync operation\n\t\t// eslint-disable-next-line @typescript-eslint/no-require-imports\n\t\tconst pkg = require(`${cwd}/package.json`);\n\t\treturn pkg.name?.replace(/^@[^/]+\\//, ''); // Remove scope\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Normalize a workspace configuration with resolved defaults.\n */\nexport function normalizeWorkspace(\n\tconfig: WorkspaceConfig,\n\tcwd: string,\n): NormalizedWorkspace {\n\tconst name = config.name ?? getPackageName(cwd) ?? basename(cwd);\n\tconst defaultTarget = config.deploy?.default ?? 'dokploy';\n\n\tconst normalizedApps: Record<string, NormalizedAppConfig> = {};\n\n\tfor (const [appName, app] of Object.entries(config.apps ?? {})) {\n\t\tnormalizedApps[appName] = normalizeAppConfig(app, defaultTarget);\n\t}\n\n\treturn {\n\t\tname,\n\t\troot: cwd,\n\t\t...(config.constructs ? { constructs: config.constructs } : {}),\n\t\tapps: normalizedApps,\n\t\tdeploy: config.deploy ?? { default: 'dokploy' },\n\t\tshared: config.shared ?? { packages: ['packages/*'] },\n\t\tstages: validateStages(config.stages),\n\t\tsecrets: config.secrets ?? {},\n\t\tstate: config.state,\n\t\ttest: config.test ?? {},\n\t\t...(config.dev ? { dev: config.dev } : {}),\n\t};\n}\n\n/**\n * Normalize an app configuration with resolved defaults.\n * @param app - App configuration\n * @param defaultTarget - Default deploy target from workspace config\n */\nfunction normalizeAppConfig(\n\tapp: AppConfig,\n\tdefaultTarget: DeployTargetName,\n): NormalizedAppConfig {\n\treturn {\n\t\t...app,\n\t\ttype: app.type ?? 'backend',\n\t\tport: app.port,\n\t\tpath: app.path,\n\t\tdependencies: app.dependencies ?? [],\n\t\tresolvedDeployTarget: app.deploy ?? defaultTarget,\n\t};\n}\n\n/**\n * Wrap a single-app GkmConfig as a workspace.\n * This allows existing single-app configs to work seamlessly.\n */\nexport function wrapSingleAppAsWorkspace(\n\tconfig: GkmConfig,\n\tcwd: string,\n): NormalizedWorkspace {\n\t// `config.name` first, the way `defineWorkspace` already reads it. It was\n\t// only honoured on the workspace path, so the same field decided the scope\n\t// in a monorepo and was ignored in a single-app project.\n\tconst name = config.name ?? getPackageName(cwd) ?? basename(cwd);\n\n\t// A projection, not a default-filled stand-in.\n\t//\n\t// Every hardcoded value here was a field the config could already state and\n\t// this quietly dropped: the port it serves on, and the target it deploys to.\n\t// `resolvedDeployTarget` was the worse of the two — a project deploying to\n\t// Vercel was normalised into one that deploys to Dokploy, and nothing said\n\t// so.\n\tconst app: NormalizedAppConfig = {\n\t\ttype: 'backend',\n\t\tpath: '.',\n\t\tport: 3000,\n\t\tdependencies: [],\n\t\tresolvedDeployTarget: config.deploy?.default ?? 'dokploy',\n\t\tconstructs: config.constructs,\n\t\thooks: config.hooks,\n\t\ttelescope: config.telescope,\n\t\topenapi: config.openapi,\n\t\truntime: config.runtime,\n\t\tenv: config.env,\n\t};\n\n\treturn {\n\t\tname,\n\t\troot: cwd,\n\t\t// Keyed `api` because that is what a single-app backend is, and because\n\t\t// the key is what names the application: `applicationName(stage, name,\n\t\t// 'api')` is `production-<name>-api`, beside the\n\t\t// `production-<name>-database` its constructs get. One model, so the\n\t\t// deploy asks the workspace what its apps are called rather than asking\n\t\t// the filesystem.\n\t\tapps: { api: app },\n\t\t// Carried rather than replaced. A single-app project configures its\n\t\t// deploy in the same shape a workspace does — endpoint, registry,\n\t\t// domains — and hardcoding the default here silently discarded all of it,\n\t\t// which surfaced as `resolveHost` refusing to name a host for a stage the\n\t\t// config had no way to describe.\n\t\tdeploy: { default: 'dokploy', ...config.deploy },\n\t\tshared: { packages: [] },\n\t\tstages: validateStages(config.stages),\n\t\tsecrets: config.secrets ?? {},\n\t\ttest: config.test ?? {},\n\t\t...(config.dev ? { dev: config.dev } : {}),\n\t};\n}\n\n/**\n * Process a loaded configuration (either single-app or workspace).\n * Returns a normalized workspace in both cases.\n */\n/**\n * `deploy.dokploy.registry`, which became `deploy.registry`: every target\n * pushes and pulls through the one registry, so it is not Dokploy's to hold.\n *\n * Refused by name rather than dropped: the schema strips keys it does not\n * know, and a registry silently gone would deploy images under no registry.\n */\nexport class DokployRegistryMoved extends Error {\n\tconstructor(readonly registry: unknown) {\n\t\tsuper(\n\t\t\t`deploy.dokploy.registry is now deploy.registry, read by every deploy target. ` +\n\t\t\t\t`In gkm.config.ts, move it up one level: deploy: { registry: ${JSON.stringify(registry)}, dokploy: { … } }.`,\n\t\t);\n\t\tthis.name = 'DokployRegistryMoved';\n\t}\n}\n\n/** Refuse a deploy key that has moved, naming where it lives now. */\nexport function assertNoMovedDeployKeys(deploy: unknown): void {\n\tconst dokploy = (deploy as { dokploy?: unknown } | undefined)?.dokploy;\n\tif (dokploy && typeof dokploy === 'object' && 'registry' in dokploy) {\n\t\tthrow new DokployRegistryMoved((dokploy as { registry: unknown }).registry);\n\t}\n}\n\nexport function processConfig(\n\tconfig: GkmConfig | WorkspaceConfig,\n\tcwd: string,\n): LoadedConfig {\n\tassertNoMovedDeployKeys(config.deploy);\n\n\tif (isWorkspaceConfig(config)) {\n\t\tassertKnownSecretsStore(config.secrets?.store);\n\t\t// Validate workspace config\n\t\tconst result = safeValidateWorkspaceConfig(config);\n\t\tif (!result.success && result.error) {\n\t\t\tthrow new Error(formatValidationErrors(result.error));\n\t\t}\n\n\t\treturn {\n\t\t\ttype: 'workspace',\n\t\t\traw: config,\n\t\t\tworkspace: normalizeWorkspace(config, cwd),\n\t\t};\n\t}\n\n\t// Single-app config - wrap as workspace\n\treturn {\n\t\ttype: 'single',\n\t\traw: config,\n\t\tworkspace: wrapSingleAppAsWorkspace(config, cwd),\n\t};\n}\n\n/**\n * The workspace's own constructs globs, absolute.\n *\n * Infrastructure two apps share is a fact about the product, and declaring it\n * inside one of them makes moving that app a change to the other's database.\n */\nexport function workspaceConstructGlobs(\n\tworkspace: NormalizedWorkspace,\n): string[] {\n\treturn constructPatterns(workspace.constructs).map((pattern) =>\n\t\tisAbsolute(pattern) ? pattern : join(workspace.root, pattern),\n\t);\n}\n\n/**\n * Every constructs glob an app can see, absolute.\n *\n * The workspace's own first, then the app's. Absolute because the app builds\n * from its own directory while the workspace's constructs live above it —\n * a relative glob resolved from the app would look for the product's\n * infrastructure inside one of its consumers and find nothing.\n *\n * Additive, not overriding: an app that declares its own constructs still sees\n * the ones the workspace declared for everybody.\n */\nexport function appConstructGlobs(\n\tworkspace: NormalizedWorkspace,\n\tappName: string,\n): string[] {\n\tconst globs: string[] = workspaceConstructGlobs(workspace);\n\n\tconst app = workspace.apps[appName];\n\tif (app) {\n\t\tconst root = isAbsolute(app.path)\n\t\t\t? app.path\n\t\t\t: join(workspace.root, app.path);\n\t\tfor (const pattern of constructPatterns(app.constructs)) {\n\t\t\tglobs.push(isAbsolute(pattern) ? pattern : join(root, pattern));\n\t\t}\n\t}\n\n\treturn globs;\n}\n/**\n * Every constructs glob in the workspace: its own, plus each app's.\n *\n * One list, so what a deploy provisions and what an app's entry point\n * registers drivers for are read off the same globs.\n */\nexport function allConstructGlobs(workspace: NormalizedWorkspace): string[] {\n\tconst globs = [\n\t\t...workspaceConstructGlobs(workspace),\n\t\t...Object.keys(workspace.apps).flatMap((appName) =>\n\t\t\tappConstructGlobs(workspace, appName),\n\t\t),\n\t];\n\n\t// Every app's list starts with the workspace's, so they repeat.\n\treturn [...new Set(globs)];\n}\n\n/** A `Routes` in any of its accepted shapes, as a flat list of patterns. */\nexport function constructPatterns(routes: Routes | undefined): string[] {\n\tif (!routes) return [];\n\treturn typeof routes === 'string' ? [routes] : routes;\n}\n\n/**\n * Get the GkmConfig for a specific app in a workspace.\n * Useful for running existing single-app commands on a specific app.\n */\nexport function getAppGkmConfig(\n\tworkspace: NormalizedWorkspace,\n\tappName: string,\n): GkmConfig | undefined {\n\tconst app = workspace.apps[appName];\n\tif (!app || app.type !== 'backend') {\n\t\treturn undefined;\n\t}\n\n\treturn {\n\t\t// Workspace-level, and carried here deliberately. The deploy target is\n\t\t// what decides the backend a cache resolves to, which the *entry point*\n\t\t// reads when it decides which drivers to register — and the local target\n\t\t// reads from the workspace when it composes the URLs those drivers\n\t\t// receive. The two have to answer from the same target, or a project on\n\t\t// a server is handed a `postgres://` cache URL by an entry that\n\t\t// registered only the Upstash driver (`UnregisteredCacheScheme`).\n\t\t...(workspace.deploy ? { deploy: workspace.deploy } : {}),\n\t\tstages: workspace.stages,\n\t\t// One glob, every kind, and the root's covers every app's code as well\n\t\t// as the constructs. Which surface an endpoint belongs to is the\n\t\t// endpoint's to say, not its file's, so the build sorts them there.\n\t\tconstructs: appConstructGlobs(workspace, appName),\n\t\thooks: app.hooks,\n\t\ttelescope: app.telescope,\n\t\topenapi: app.openapi,\n\t\truntime: app.runtime,\n\t\tenv: app.env,\n\t};\n}\n\n/**\n * Get topologically sorted app names based on dependencies.\n * Apps with no dependencies come first, then apps that depend on them.\n */\nexport function getAppBuildOrder(workspace: NormalizedWorkspace): string[] {\n\tconst appNames = Object.keys(workspace.apps);\n\tconst visited = new Set<string>();\n\tconst result: string[] = [];\n\n\tfunction visit(name: string) {\n\t\tif (visited.has(name)) return;\n\t\tvisited.add(name);\n\n\t\tconst app = workspace.apps[name];\n\t\tif (app) {\n\t\t\tfor (const dep of app.dependencies) {\n\t\t\t\tvisit(dep);\n\t\t\t}\n\t\t}\n\n\t\tresult.push(name);\n\t}\n\n\tfor (const name of appNames) {\n\t\tvisit(name);\n\t}\n\n\treturn result;\n}\n\n/**\n * Resolve the Dokploy endpoint for a specific stage.\n *\n * Uses per-stage endpoint from `endpoints` if available,\n * otherwise falls back to the global `endpoint`.\n *\n * @param dokployConfig - Dokploy workspace configuration\n * @param stage - Deployment stage (e.g., 'development', 'production')\n * @returns The endpoint URL for the stage, or undefined if not configured\n *\n * @example\n * ```ts\n * // With per-stage endpoints\n * const config = {\n * endpoints: {\n * development: 'https://dev.dokploy.example.com:3000',\n * production: 'https://prod.dokploy.example.com:3000',\n * },\n * };\n * getEndpointForStage(config, 'production'); // => 'https://prod.dokploy.example.com:3000'\n *\n * // With single endpoint\n * const config = { endpoint: 'https://dokploy.example.com:3000' };\n * getEndpointForStage(config, 'production'); // => 'https://dokploy.example.com:3000'\n * ```\n */\nexport function getEndpointForStage(\n\tdokployConfig:\n\t\t| { endpoint?: string; endpoints?: Record<string, string> }\n\t\t| undefined,\n\tstage: string,\n): string | undefined {\n\tif (!dokployConfig) return undefined;\n\n\t// Check per-stage endpoint first\n\tif (dokployConfig.endpoints?.[stage]) {\n\t\treturn dokployConfig.endpoints[stage];\n\t}\n\n\t// Fall back to global endpoint\n\treturn dokployConfig.endpoint;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-QUTqYmVy.mjs","names":["z.union","z.string","z.array","z.object","z.boolean","z.number","z.enum","z.custom","z.tuple","z.unknown","z\n\t.object","z.url","z\n\t\t\t.record","z\n\t\t\t.object","z\n\t\t\t\t\t.string","z.literal","z.discriminatedUnion","z.record","z\n\t\t.union","z\n\t\t.record","z\n\t\t.string","z\n\t\t.object","z\n\t\t\t.union","z\n\t\t\t.array"],"sources":["../src/secrets/providers.ts","../src/compose/logsConfig.ts","../src/workspace/stages.ts","../src/workspace/schema.ts","../src/workspace/types.ts","../src/workspace/publicEnv.ts","../src/workspace/index.ts"],"sourcesContent":["/**\n * The secrets stores gkm ships, by name — with no imports, so the config\n * loader can check `secrets.store` without loading any store.\n */\n\n/** The stores this package ships, by the name `secrets.store` gives them. */\nexport const SECRETS_STORE_PROVIDERS = [\n\t'file',\n\t'ssm',\n\t'secrets-manager',\n] as const;\n\nexport type SecretsStoreProvider = (typeof SECRETS_STORE_PROVIDERS)[number];\n\n/**\n * `secrets.store` names a provider this package does not ship — a typo such\n * as `'secretsmanager'` or `'ssm-parameter'`. Refused rather than read as the\n * file, which would leave a deployed stage's secrets on one machine.\n */\nexport class UnknownSecretsStoreProvider extends Error {\n\tconstructor(readonly provider: unknown) {\n\t\tsuper(\n\t\t\t`secrets.store names the provider ${JSON.stringify(provider)}, which is not one gkm ships. ` +\n\t\t\t\t`Use 'file', { provider: 'ssm', region }, { provider: 'secrets-manager', region }, ` +\n\t\t\t\t'or { provider: <an object with name, read() and write()> } in gkm.config.ts.',\n\t\t);\n\t\tthis.name = 'UnknownSecretsStoreProvider';\n\t}\n}\n\n/**\n * Refuse a `secrets.store` naming a provider that is not one, before the\n * schema reduces it to a generic validation failure.\n */\nexport function assertKnownSecretsStore(store: unknown): void {\n\tif (store === undefined) return;\n\tconst provider =\n\t\ttypeof store === 'object' && store !== null && 'provider' in store\n\t\t\t? (store as { provider: unknown }).provider\n\t\t\t: store;\n\tif (\n\t\ttypeof provider === 'string' &&\n\t\t!(SECRETS_STORE_PROVIDERS as readonly string[]).includes(provider)\n\t) {\n\t\tthrow new UnknownSecretsStoreProvider(provider);\n\t}\n}\n","/**\n * `deploy.compose.logs`, read: whether the stack runs OpenObserve, and how it\n * is reached.\n *\n * Kept apart from the stack so the workspace schema can check a config with\n * the same rules — and the same named errors — the stack applies.\n */\n\nimport { isIP } from 'node:net';\nimport type { ComposeLogsConfig } from '../workspace/types.js';\n\n/** The port OpenObserve listens on, and the one it is published on by default. */\nexport const LOGS_PORT = 5080;\n\n/** How many days of data OpenObserve keeps unless told otherwise. */\nexport const LOGS_RETENTION_DAYS = 30;\n\n/** The fewest days OpenObserve accepts: below it, it refuses to start. */\nexport const LOGS_MIN_RETENTION_DAYS = 3;\n\n/** `deploy.compose.logs`, with its defaults applied. */\nexport interface ResolvedLogs {\n\t/** The loopback port it is published on. Not published when `public` is set. */\n\tport: number;\n\tretentionDays: number;\n\t/** Served on `logs.<stage domain>`, to these addresses only. */\n\tpublic?: { allow: string[] };\n}\n\n/** `public` with no address to allow: nobody could reach it. */\nexport class LogsAllowEmpty extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'deploy.compose.logs.public.allow is empty, so the logs site would ' +\n\t\t\t\t'answer every request with 403. List the addresses that may reach it ' +\n\t\t\t\t\"(public: { allow: ['203.0.113.7', '10.0.0.0/8'] }), or remove public \" +\n\t\t\t\t'and reach it through an SSH tunnel.',\n\t\t);\n\t\tthis.name = 'LogsAllowEmpty';\n\t}\n}\n\n/** An `allow` entry that is not an IP address or a CIDR range. */\nexport class LogsAllowEntryInvalid extends Error {\n\tconstructor(readonly entry: string) {\n\t\tsuper(\n\t\t\t`deploy.compose.logs.public.allow has '${entry}', which is not an IP ` +\n\t\t\t\t\"address or a CIDR range. Write one like '203.0.113.7', \" +\n\t\t\t\t\"'10.0.0.0/8' or '2001:db8::/32'.\",\n\t\t);\n\t\tthis.name = 'LogsAllowEntryInvalid';\n\t}\n}\n\n/** A retention OpenObserve would refuse to start with. */\nexport class LogsRetentionInvalid extends Error {\n\tconstructor(readonly retentionDays: unknown) {\n\t\tsuper(\n\t\t\t`deploy.compose.logs.retentionDays is ${String(retentionDays)}; it must ` +\n\t\t\t\t`be a whole number of days, at least ${LOGS_MIN_RETENTION_DAYS} — ` +\n\t\t\t\t'OpenObserve refuses to start with fewer. Leave it out for ' +\n\t\t\t\t`${LOGS_RETENTION_DAYS}.`,\n\t\t);\n\t\tthis.name = 'LogsRetentionInvalid';\n\t}\n}\n\n/** A port that is not one. */\nexport class LogsPortInvalid extends Error {\n\tconstructor(readonly port: unknown) {\n\t\tsuper(\n\t\t\t`deploy.compose.logs.port is ${String(port)}; set it to a number ` +\n\t\t\t\t`between 1 and 65535, or leave it out for ${LOGS_PORT}.`,\n\t\t);\n\t\tthis.name = 'LogsPortInvalid';\n\t}\n}\n\n/** Whether `entry` is an IP address, or an address and a prefix length. */\nfunction isAddressOrRange(entry: string): boolean {\n\tconst [address = '', prefix, ...rest] = entry.split('/');\n\tif (rest.length > 0) return false;\n\tconst family = isIP(address);\n\tif (family === 0) return false;\n\tif (prefix === undefined) return true;\n\tif (!/^\\d{1,3}$/.test(prefix)) return false;\n\treturn Number(prefix) <= (family === 4 ? 32 : 128);\n}\n\n/**\n * `deploy.compose.logs` with its defaults, or undefined when it is off.\n * Throws the named error for the first thing wrong with it.\n */\nexport function resolveLogs(\n\tlogs: boolean | ComposeLogsConfig | undefined,\n): ResolvedLogs | undefined {\n\tif (!logs) return undefined;\n\tconst config: ComposeLogsConfig = logs === true ? {} : logs;\n\n\tconst port = config.port ?? LOGS_PORT;\n\tif (!Number.isInteger(port) || port < 1 || port > 65_535) {\n\t\tthrow new LogsPortInvalid(config.port);\n\t}\n\n\tconst retentionDays = config.retentionDays ?? LOGS_RETENTION_DAYS;\n\tif (\n\t\t!Number.isInteger(retentionDays) ||\n\t\tretentionDays < LOGS_MIN_RETENTION_DAYS\n\t) {\n\t\tthrow new LogsRetentionInvalid(config.retentionDays);\n\t}\n\n\tif (config.public) {\n\t\tconst allow = config.public.allow ?? [];\n\t\tif (allow.length === 0) throw new LogsAllowEmpty();\n\t\tfor (const entry of allow) {\n\t\t\tif (!isAddressOrRange(entry)) throw new LogsAllowEntryInvalid(entry);\n\t\t}\n\t\treturn { port, retentionDays, public: { allow: [...allow] } };\n\t}\n\n\treturn { port, retentionDays };\n}\n","import type { StagesConfig } from './types.js';\n\n/**\n * What `gkm test` runs as. The CLI's own, so no project stage may take it:\n * its containers are torn down and rebuilt between runs.\n */\nexport const TEST_STAGE = 'test';\n\n/** A stage becomes part of every physical name, so it has to fit in one. */\nconst STAGE_NAME = /^[a-z][a-z0-9-]*$/;\n\n/** Everything wrong with one stage name, or nothing. */\nfunction nameProblems(name: string): string[] {\n\tconst problems: string[] = [];\n\tif (!STAGE_NAME.test(name)) {\n\t\tproblems.push(\n\t\t\t`stage \"${name}\" must be lowercase letters, digits and hyphens, starting with a letter`,\n\t\t);\n\t}\n\tif (name === TEST_STAGE) {\n\t\tproblems.push(`stage \"${TEST_STAGE}\" is reserved for gkm test`);\n\t}\n\treturn problems;\n}\n\n/** Everything wrong with a list of deployed stages on its own. */\nexport function deployedProblems(deployed: readonly string[]): string[] {\n\tconst problems = deployed.flatMap(nameProblems);\n\tconst seen = new Set<string>();\n\tfor (const name of deployed) {\n\t\tif (seen.has(name)) problems.push(`stage \"${name}\" is listed twice`);\n\t\tseen.add(name);\n\t}\n\treturn problems;\n}\n\n/** Everything wrong with a stages block, or nothing. */\nexport function stageProblems(stages: StagesConfig | undefined): string[] {\n\tif (!stages) {\n\t\treturn [\n\t\t\t\"`stages` is required, e.g. stages: { local: 'dev', deployed: ['prod'] }\",\n\t\t];\n\t}\n\n\tconst { local, deployed, protected: kept = [] } = stages;\n\tconst problems = [...nameProblems(local), ...deployedProblems(deployed)];\n\n\tif (deployed.includes(local)) {\n\t\tproblems.push(\n\t\t\t`\"${local}\" is both the local stage and a deployed one; they would share secrets`,\n\t\t);\n\t}\n\n\tfor (const name of kept) {\n\t\tif (!deployed.includes(name)) {\n\t\t\tproblems.push(`protected stage \"${name}\" is not a deployed stage`);\n\t\t}\n\t}\n\n\treturn problems;\n}\n\n/** The stages block, or an error naming everything wrong with it. */\nexport function validateStages(stages: StagesConfig | undefined): StagesConfig {\n\tconst problems = stageProblems(stages);\n\tif (problems.length) {\n\t\tthrow new InvalidStages(problems);\n\t}\n\treturn stages!;\n}\n\n/** Refuses a stage the project does not deploy to. */\nexport function assertDeployedStage(stages: StagesConfig, stage: string): void {\n\tif (!stages.deployed.includes(stage)) {\n\t\tthrow new UndeclaredStage(stage, stages.deployed);\n\t}\n}\n\n/** A stages block — in gkm.config.ts or from `gkm init`'s flags — that breaks a rule. */\nexport class InvalidStages extends Error {\n\tconstructor(readonly problems: readonly string[]) {\n\t\tsuper(`Invalid stages:\\n ${problems.join('\\n ')}`);\n\t\tthis.name = 'InvalidStages';\n\t}\n}\n\n/**\n * A stage the project does not deploy to. Refused before anything is\n * provisioned, so a typo does not become a second environment.\n */\nexport class UndeclaredStage extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly deployed: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`\"${stage}\" is not a deployed stage. gkm.config.ts deploys to: ${deployed.join(', ') || 'nothing yet'}.`,\n\t\t);\n\t\tthis.name = 'UndeclaredStage';\n\t}\n}\n","import { z } from 'zod/v4';\nimport { resolveLogs } from '../compose/logsConfig.js';\nimport {\n\tBUILTIN_TARGETS,\n\tbuiltinTarget,\n\tconfigurableBuiltins,\n} from '../target/builtins.js';\nimport { isDeployTarget } from '../target/define.js';\nimport { stageProblems } from './stages.js';\n\n/** Routes are a glob, or a list of them. */\nconst RoutesSchema = z.union([z.string(), z.array(z.string())]);\n\n/**\n * Telescope configuration schema.\n */\nconst TelescopeConfigSchema = z.object({\n\tenabled: z.boolean().optional(),\n\tport: z.number().optional(),\n\tpath: z.string().optional(),\n\tignore: z.array(z.string()).optional(),\n\trecordBody: z.boolean().optional(),\n\tmaxEntries: z.number().optional(),\n\twebsocket: z.boolean().optional(),\n});\n\n/**\n * OpenAPI configuration schema.\n */\nconst OpenApiConfigSchema = z.object({\n\tenabled: z.boolean().optional(),\n\ttitle: z.string().optional(),\n\tversion: z.string().optional(),\n\tdescription: z.string().optional(),\n});\n\n/**\n * Hooks configuration schema.\n */\nconst HooksConfigSchema = z.object({\n\tserver: z.string().optional(),\n});\n\n/**\n * Backend framework schema for non-gkm apps.\n */\nexport const BackendFrameworkSchema = z.enum([\n\t'hono',\n\t'better-auth',\n\t'express',\n\t'fastify',\n]);\n\n/**\n * Backend framework types for apps that don't use gkm routes.\n * Derived from {@link BackendFrameworkSchema}.\n *\n * Used with `entry` to specify the framework for proper Docker builds.\n *\n * @example\n * ```ts\n * // Better Auth server\n * {\n * entry: './src/index.ts',\n * framework: 'better-auth',\n * port: 3001,\n * }\n *\n * // Hono app without gkm routes\n * {\n * entry: './src/server.ts',\n * framework: 'hono',\n * port: 3000,\n * }\n * ```\n */\nexport type BackendFramework = z.infer<typeof BackendFrameworkSchema>;\n\n/**\n * Frontend framework schema.\n */\nexport const FrontendFrameworkSchema = z.enum([\n\t'nextjs',\n\t'remix',\n\t'vite',\n\t'tanstack-start',\n]);\n\n/**\n * Frontend framework types. Derived from {@link FrontendFrameworkSchema}.\n *\n * @example\n * ```ts\n * // Next.js app\n * {\n * type: 'web',\n * framework: 'nextjs',\n * port: 3000,\n * }\n *\n * // Vite SPA\n * {\n * type: 'web',\n * framework: 'vite',\n * port: 5173,\n * }\n *\n * // TanStack Start (Vite-based, full-stack)\n * {\n * type: 'web',\n * framework: 'tanstack-start',\n * port: 3000,\n * }\n * ```\n */\nexport type FrontendFramework = z.infer<typeof FrontendFrameworkSchema>;\n\n/**\n * Mobile framework schema.\n */\nexport const MobileFrameworkSchema = z.enum(['expo']);\n\n/**\n * Mobile framework types. Derived from {@link MobileFrameworkSchema}.\n *\n * Mobile apps deploy via their own toolchain (e.g. EAS Build for Expo)\n * rather than Docker/Dokploy. Only build-time public env vars\n * (e.g. `EXPO_PUBLIC_*`) reach the device — server secrets are dropped.\n *\n * @example\n * ```ts\n * {\n * type: 'mobile',\n * framework: 'expo',\n * path: 'apps/mobile',\n * port: 8081,\n * dependencies: ['api'],\n * }\n * ```\n */\nexport type MobileFramework = z.infer<typeof MobileFrameworkSchema>;\n\n/**\n * Combined framework schema (backend, frontend, or mobile).\n */\nexport const FrameworkSchema = z.union([\n\tBackendFrameworkSchema,\n\tFrontendFrameworkSchema,\n\tMobileFrameworkSchema,\n]);\n\n/**\n * Any framework value across backend, frontend, or mobile.\n * Derived from {@link FrameworkSchema}.\n */\nexport type Framework = z.infer<typeof FrameworkSchema>;\n\n/** Frontend framework values, kept in sync with FrontendFrameworkSchema. */\nconst FRONTEND_FRAMEWORKS = [\n\t'nextjs',\n\t'remix',\n\t'vite',\n\t'tanstack-start',\n] as const;\n\n/** Mobile framework values, kept in sync with MobileFrameworkSchema. */\nconst MOBILE_FRAMEWORKS = ['expo'] as const;\n\n/**\n * A deploy target's name. Which names exist is the workspace's to say — the\n * built-ins and its own `deploy.targets` — so it is checked against both\n * once the whole config is read.\n */\nconst DeployTargetSchema = z.string().min(1);\n\n/**\n * The built-in targets a config may name, both deployed by `gkm deploy`:\n * `dokploy`, and `sst`, which runs `sst deploy` on the manifest `gkm build`\n * writes.\n */\nconst SUPPORTED_DEPLOY_TARGETS = configurableBuiltins();\n\n/** Built-in names reserved for targets that do not exist yet. */\nconst PHASE_2_DEPLOY_TARGETS = Object.entries(BUILTIN_TARGETS)\n\t.filter(([, target]) => target.status === 'planned')\n\t.map(([name]) => name);\n\n/**\n * Whether a config may name `target`: a usable built-in, or one of its own\n * `deploy.targets`.\n */\nexport function isDeployTargetSupported(\n\ttarget: string,\n\ttargets: Record<string, unknown> = {},\n): boolean {\n\treturn (\n\t\tSUPPORTED_DEPLOY_TARGETS.includes(target) || Object.hasOwn(targets, target)\n\t);\n}\n\n/**\n * Check if a deploy target is planned for Phase 2.\n */\nexport function isPhase2DeployTarget(target: string): boolean {\n\treturn PHASE_2_DEPLOY_TARGETS.includes(target);\n}\n\n/**\n * Get error message for unsupported deploy targets.\n */\nexport function getDeployTargetError(target: string, appName?: string): string {\n\tif (isPhase2DeployTarget(target)) {\n\t\tconst context = appName ? ` for app \"${appName}\"` : '';\n\t\treturn `Deploy target \"${target}\"${context} is coming in Phase 2. Currently \"dokploy\" and \"sst\" are supported.`;\n\t}\n\treturn `Unknown deploy target: ${target}. Built in: ${SUPPORTED_DEPLOY_TARGETS.join(', ')}; any other is named in deploy.targets with the package that provides it. Coming in Phase 2: ${PHASE_2_DEPLOY_TARGETS.join(', ')}.`;\n}\n\n/**\n * One `deploy.targets` entry: a package name, a target object, or either\n * with options.\n */\nconst DeployTargetEntrySchema = z.union([\n\tz.string().min(1, 'A target package name cannot be empty'),\n\tz.custom((value) => isDeployTarget(value), {\n\t\tmessage:\n\t\t\t'Expected a package name, a target (defineTarget), or [package or target, options]',\n\t}),\n\tz.tuple([\n\t\tz.union([\n\t\t\tz.string().min(1),\n\t\t\tz.custom((value) => isDeployTarget(value), {\n\t\t\t\tmessage: 'Expected a package name or a target (defineTarget)',\n\t\t\t}),\n\t\t]),\n\t\tz.unknown(),\n\t]),\n]);\n\n/**\n * Dokploy workspace configuration schema.\n * Supports either a single endpoint or per-stage endpoints.\n */\nconst DokployWorkspaceConfigSchema = z\n\t.object({\n\t\t/** Single endpoint for all stages */\n\t\tendpoint: z.url('Dokploy endpoint must be a valid URL').optional(),\n\t\t/** Per-stage endpoints (stage name -> endpoint URL) */\n\t\tendpoints: z\n\t\t\t.record(z.string(), z.url('Endpoint must be a valid URL'))\n\t\t\t.optional(),\n\t\tregistryId: z.string().optional(),\n\t\tverify: z\n\t\t\t.object({\n\t\t\t\tdeploymentTimeoutMs: z.number().int().positive().optional(),\n\t\t\t\thealthCheckPath: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.startsWith('/', 'healthCheckPath must start with /')\n\t\t\t\t\t.optional(),\n\t\t\t\thealthyAfter: z.number().int().positive().optional(),\n\t\t\t\tintervalMs: z.number().int().positive().optional(),\n\t\t\t\thealthTimeoutMs: z.number().int().positive().optional(),\n\t\t\t})\n\t\t\t.optional(),\n\t})\n\t.refine((data) => data.endpoint || data.endpoints, {\n\t\tmessage: 'Either endpoint or endpoints must be provided',\n\t});\n\n// =============================================================================\n// AWS Regions (needed by DNS and State providers)\n// =============================================================================\n\n/**\n * Valid AWS regions.\n */\nconst AwsRegionSchema = z.enum([\n\t'us-east-1',\n\t'us-east-2',\n\t'us-west-1',\n\t'us-west-2',\n\t'af-south-1',\n\t'ap-east-1',\n\t'ap-south-1',\n\t'ap-south-2',\n\t'ap-southeast-1',\n\t'ap-southeast-2',\n\t'ap-southeast-3',\n\t'ap-southeast-4',\n\t'ap-northeast-1',\n\t'ap-northeast-2',\n\t'ap-northeast-3',\n\t'ca-central-1',\n\t'eu-central-1',\n\t'eu-central-2',\n\t'eu-west-1',\n\t'eu-west-2',\n\t'eu-west-3',\n\t'eu-south-1',\n\t'eu-south-2',\n\t'eu-north-1',\n\t'me-south-1',\n\t'me-central-1',\n\t'sa-east-1',\n]);\n\n// =============================================================================\n// DNS Record Types (used by DnsProvider interface)\n// =============================================================================\n\n/**\n * DNS record types supported across providers.\n */\nexport const DnsRecordTypeSchema = z.enum([\n\t'A',\n\t'AAAA',\n\t'CNAME',\n\t'MX',\n\t'TXT',\n\t'NS',\n\t'SRV',\n\t'CAA',\n]);\n\n/**\n * A DNS record as returned by the provider.\n */\nexport const DnsRecordSchema = z.object({\n\t/** Subdomain name (e.g., 'api' for api.example.com, '@' for root) */\n\tname: z.string(),\n\t/** Record type */\n\ttype: DnsRecordTypeSchema,\n\t/** TTL in seconds */\n\tttl: z.number().int().positive(),\n\t/** Record values */\n\tvalues: z.array(z.string()),\n});\n\n/**\n * A DNS record to create or update.\n */\nexport const UpsertDnsRecordSchema = z.object({\n\t/** Subdomain name (e.g., 'api' for api.example.com, '@' for root) */\n\tname: z.string(),\n\t/** Record type */\n\ttype: DnsRecordTypeSchema,\n\t/** TTL in seconds */\n\tttl: z.number().int().positive(),\n\t/** Record value (IP address, hostname, etc.) */\n\tvalue: z.string(),\n});\n\n/**\n * Result of an upsert operation.\n */\nexport const UpsertResultSchema = z.object({\n\t/** The record that was upserted */\n\trecord: UpsertDnsRecordSchema,\n\t/** Whether the record was created (true) or updated (false) */\n\tcreated: z.boolean(),\n\t/** Whether the record already existed with the same value */\n\tunchanged: z.boolean(),\n});\n\n// =============================================================================\n// DNS Provider Configuration\n// =============================================================================\n\n/**\n * Hostinger DNS provider config (without domain - domain is the record key).\n */\nexport const HostingerDnsProviderSchema = z.object({\n\tprovider: z.literal('hostinger'),\n\t/** TTL in seconds (default: 300) */\n\tttl: z.number().int().positive().optional(),\n});\n\n/**\n * Route53 DNS provider config (without domain - domain is the record key).\n */\nexport const Route53DnsProviderSchema = z.object({\n\tprovider: z.literal('route53'),\n\t/** AWS region (optional - uses AWS_REGION env var if not provided) */\n\tregion: AwsRegionSchema.optional(),\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile: z.string().optional(),\n\t/** Hosted zone ID (optional - auto-detected from domain if not provided) */\n\thostedZoneId: z.string().optional(),\n\t/** TTL in seconds (default: 300) */\n\tttl: z.number().int().positive().optional(),\n});\n\n/**\n * Cloudflare DNS provider config (placeholder for future).\n */\nexport const CloudflareDnsProviderSchema = z.object({\n\tprovider: z.literal('cloudflare'),\n\t/** TTL in seconds (default: 300) */\n\tttl: z.number().int().positive().optional(),\n});\n\n/**\n * Manual DNS configuration (user handles DNS themselves).\n */\nexport const ManualDnsProviderSchema = z.object({\n\tprovider: z.literal('manual'),\n});\n\n/**\n * Custom DNS provider config (user-provided implementation).\n */\nexport const CustomDnsProviderSchema = z.object({\n\t/** Custom DnsProvider implementation */\n\tprovider: z.custom<{\n\t\tname: string;\n\t\tgetRecords: Function;\n\t\tupsertRecords: Function;\n\t}>(\n\t\t(val) =>\n\t\t\ttypeof val === 'object' &&\n\t\t\tval !== null &&\n\t\t\ttypeof (val as any).name === 'string' &&\n\t\t\ttypeof (val as any).getRecords === 'function' &&\n\t\t\ttypeof (val as any).upsertRecords === 'function',\n\t\t{\n\t\t\tmessage:\n\t\t\t\t'Custom DNS provider must implement name, getRecords(), and upsertRecords() methods',\n\t\t},\n\t),\n\t/** TTL in seconds (default: 300) */\n\tttl: z.number().int().positive().optional(),\n});\n\n/**\n * Built-in DNS provider config (discriminated union).\n */\nexport const BuiltInDnsProviderSchema = z.discriminatedUnion('provider', [\n\tHostingerDnsProviderSchema,\n\tRoute53DnsProviderSchema,\n\tCloudflareDnsProviderSchema,\n\tManualDnsProviderSchema,\n]);\n\n/**\n * Single DNS provider config (for one domain).\n */\nexport const DnsProviderSchema = z.union([\n\tBuiltInDnsProviderSchema,\n\tCustomDnsProviderSchema,\n]);\n\nexport type DnsProvider = z.infer<typeof DnsProviderSchema>;\n\n/**\n * DNS configuration schema.\n *\n * Maps root domains to their DNS provider configuration.\n * Example:\n * ```\n * dns: {\n * 'geekmidas.dev': { provider: 'hostinger' },\n * 'geekmidas.com': { provider: 'route53' },\n * }\n * ```\n *\n * Supported providers:\n * - 'hostinger': Use Hostinger DNS API\n * - 'route53': Use AWS Route53\n * - 'cloudflare': Use Cloudflare DNS API (future)\n * - 'manual': Don't create records, just print required records\n * - Custom: Provide a DnsProvider implementation\n */\nexport const DnsConfigSchema = z.record(z.string(), DnsProviderSchema);\n\n// Legacy single-domain config schemas (for backwards compatibility)\nexport const HostingerDnsConfigSchema = HostingerDnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const Route53DnsConfigSchema = Route53DnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const CloudflareDnsConfigSchema = CloudflareDnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const ManualDnsConfigSchema = ManualDnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const CustomDnsConfigSchema = CustomDnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const BuiltInDnsConfigSchema = z.discriminatedUnion('provider', [\n\tHostingerDnsConfigSchema,\n\tRoute53DnsConfigSchema,\n\tCloudflareDnsConfigSchema,\n\tManualDnsConfigSchema,\n]);\nexport const LegacyDnsConfigSchema = z.union([\n\tBuiltInDnsConfigSchema,\n\tCustomDnsConfigSchema,\n]);\n\n/**\n * Combined DNS config that supports both new multi-domain and legacy single-domain formats.\n */\nexport const DnsConfigWithLegacySchema = z.union([\n\tDnsConfigSchema,\n\tLegacyDnsConfigSchema,\n]);\n\nexport type DnsConfig = z.infer<typeof DnsConfigWithLegacySchema>;\n\n/**\n * Backups configuration schema.\n *\n * Configures automatic backup destinations for database services.\n * On first deploy, creates S3 bucket with unique name and IAM credentials.\n */\nexport const BackupsConfigSchema = z.object({\n\t/** Backup storage type (currently only 's3' supported) */\n\ttype: z.literal('s3'),\n\t/** AWS profile name for creating bucket/IAM resources */\n\tprofile: z.string().optional(),\n\t/** AWS region for the backup bucket */\n\tregion: AwsRegionSchema,\n\t/** Cron schedule for backups (default: '0 2 * * *' = 2 AM daily) */\n\tschedule: z.string().optional(),\n\t/** Number of backups to retain (default: 30) */\n\tretention: z.number().optional(),\n});\n\nexport type BackupsConfig = z.infer<typeof BackupsConfigSchema>;\n\n/**\n * `deploy.compose` — what the compose target runs beside the apps. The rules\n * past the shape are `resolveLogs`'s, so the schema and the stack refuse the\n * same configs with the same words.\n */\nconst ComposeWorkspaceConfigSchema = z.object({\n\tlogs: z\n\t\t.union([\n\t\t\tz.boolean(),\n\t\t\tz.object({\n\t\t\t\tport: z.number().optional(),\n\t\t\t\tretentionDays: z.number().optional(),\n\t\t\t\tpublic: z.object({ allow: z.array(z.string()) }).optional(),\n\t\t\t}),\n\t\t])\n\t\t.superRefine((logs, ctx) => {\n\t\t\ttry {\n\t\t\t\tresolveLogs(logs);\n\t\t\t} catch (error) {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t}\n\t\t})\n\t\t.optional(),\n});\n\n/**\n * Deploy configuration schema.\n */\nconst DeployConfigSchema = z.object({\n\tdefault: DeployTargetSchema.optional(),\n\ttargets: z\n\t\t.record(z.string(), DeployTargetEntrySchema)\n\t\t.superRefine((targets, ctx) => {\n\t\t\tfor (const name of Object.keys(targets)) {\n\t\t\t\t// The built-ins resolve first, so an entry under one of their\n\t\t\t\t// names would never be used — say so rather than ignore it.\n\t\t\t\tif (builtinTarget(name)) {\n\t\t\t\t\tctx.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `\"${name}\" is a built-in target; give this one a name of its own`,\n\t\t\t\t\t\tpath: [name],\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\t.optional(),\n\t/** Whose deploy this is, on a target shared with other workspaces. */\n\tnamespace: z\n\t\t.string()\n\t\t.regex(\n\t\t\t/^[a-z0-9]+(?:-[a-z0-9]+)*$/,\n\t\t\t\"deploy.namespace must be lowercase letters and digits with single '-' between them\",\n\t\t)\n\t\t.optional(),\n\t/** Each deployed stage's base domain (stage name -> domain). */\n\tdomains: z.record(z.string(), z.string()).optional(),\n\t/** Where every target pushes and pulls the apps' images. */\n\tregistry: z.string().min(1).optional(),\n\tdokploy: DokployWorkspaceConfigSchema.optional(),\n\tcompose: ComposeWorkspaceConfigSchema.optional(),\n\tdns: DnsConfigWithLegacySchema.optional(),\n\tbackups: BackupsConfigSchema.optional(),\n});\n\n/**\n * Models configuration schema.\n */\nconst ModelsConfigSchema = z.object({\n\tpath: z.string().optional(),\n\tschema: z.enum(['zod']).optional(),\n});\n\n/**\n * Shared configuration schema.\n */\nconst SharedConfigSchema = z.object({\n\tpackages: z.array(z.string()).optional(),\n\tmodels: ModelsConfigSchema.optional(),\n});\n\n/**\n * Secrets configuration schema.\n */\nconst StagesConfigSchema = z\n\t.object({\n\t\tlocal: z.string(),\n\t\tdeployed: z.array(z.string()),\n\t\tprotected: z.array(z.string()).optional(),\n\t})\n\t.superRefine((stages, ctx) => {\n\t\tfor (const message of stageProblems(stages)) {\n\t\t\tctx.addIssue({ code: 'custom', message });\n\t\t}\n\t});\n\nconst SecretsStoreSchema = z.union([\n\tz.literal('file'),\n\tz.object({ provider: z.literal('ssm'), region: AwsRegionSchema }).strict(),\n\tz\n\t\t.object({\n\t\t\tprovider: z.literal('secrets-manager'),\n\t\t\tregion: AwsRegionSchema,\n\t\t\tkmsKeyId: z.string().min(1).optional(),\n\t\t})\n\t\t.strict(),\n\tz.object({\n\t\t/** Any backend: an object implementing SecretsStore */\n\t\tprovider: z.custom<{ name: string; read: Function; write: Function }>(\n\t\t\t(val) =>\n\t\t\t\ttypeof val === 'object' &&\n\t\t\t\tval !== null &&\n\t\t\t\ttypeof (val as any).name === 'string' &&\n\t\t\t\ttypeof (val as any).read === 'function' &&\n\t\t\t\ttypeof (val as any).write === 'function',\n\t\t\t{ message: 'a secrets store has a name, read() and write()' },\n\t\t),\n\t}),\n]);\n\nconst SecretsConfigSchema = z.object({\n\tenabled: z.boolean().optional(),\n\tstore: SecretsStoreSchema.optional(),\n\talgorithm: z.string().optional(),\n\tkdf: z.enum(['scrypt', 'pbkdf2']).optional(),\n});\n\n// =============================================================================\n// State Provider Configuration\n// =============================================================================\n\n/**\n * Local state provider config.\n */\nconst LocalStateConfigSchema = z.object({\n\tprovider: z.literal('local'),\n});\n\n/**\n * SSM state provider config (requires region).\n */\nconst SSMStateConfigSchema = z.object({\n\tprovider: z.literal('ssm'),\n\t/** AWS region (required for SSM provider) */\n\tregion: AwsRegionSchema,\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile: z.string().optional(),\n});\n\n/**\n * S3 state provider config: conditional writes and a lock object in a bucket.\n */\nconst S3StateConfigSchema = z.object({\n\tprovider: z.literal('s3'),\n\t/** Bucket the state lives in (must already exist) */\n\tbucket: z.string().min(1),\n\t/** AWS region of the bucket */\n\tregion: AwsRegionSchema,\n\t/** Key prefix inside the bucket (default: 'gkm') */\n\tprefix: z.string().optional(),\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile: z.string().optional(),\n});\n\n/**\n * Custom state provider config (user-provided implementation).\n */\nconst CustomStateConfigSchema = z.object({\n\t/** Custom StateProvider implementation */\n\tprovider: z.custom<{ read: Function; write: Function }>(\n\t\t(val) =>\n\t\t\ttypeof val === 'object' &&\n\t\t\tval !== null &&\n\t\t\ttypeof (val as any).read === 'function' &&\n\t\t\ttypeof (val as any).write === 'function',\n\t\t{ message: 'Custom provider must implement read() and write() methods' },\n\t),\n});\n\n/**\n * Built-in state provider config (discriminated union).\n */\nconst BuiltInStateConfigSchema = z.discriminatedUnion('provider', [\n\tLocalStateConfigSchema,\n\tSSMStateConfigSchema,\n\tS3StateConfigSchema,\n]);\n\n/**\n * State configuration schema.\n *\n * Configures how deployment state is stored.\n * - 'local': Store in .gkm/deploy-{stage}.json (default)\n * - 'ssm': Store in AWS SSM Parameter Store (requires region)\n * - 's3': Store in an S3 bucket with conditional writes (requires bucket, region)\n * - Custom: Provide a StateStore, or a StateProvider with read/write methods\n */\nconst StateConfigSchema = z.union([\n\tBuiltInStateConfigSchema,\n\tCustomStateConfigSchema,\n]);\n\n/**\n * App configuration schema.\n */\nconst AppConfigSchema = z\n\t.object({\n\t\t// Core properties\n\t\ttype: z.enum(['backend', 'web', 'mobile']).optional().default('backend'),\n\t\tpath: z.string().min(1, 'App path is required'),\n\t\tport: z.number().int().positive('Port must be a positive integer'),\n\t\tdependencies: z.array(z.string()).optional(),\n\t\tdeploy: DeployTargetSchema.optional(),\n\n\t\t// Backend-specific (from GkmConfig)\n\t\tconstructs: RoutesSchema.optional(),\n\t\troutes: RoutesSchema.optional(),\n\t\tfunctions: RoutesSchema.optional(),\n\t\tcrons: RoutesSchema.optional(),\n\t\tqueues: RoutesSchema.optional(),\n\t\ttopics: RoutesSchema.optional(),\n\t\tsubscribers: RoutesSchema.optional(),\n\t\tenvParser: z.string().optional(),\n\t\tlogger: z.string().optional(),\n\t\thooks: HooksConfigSchema.optional(),\n\t\ttelescope: z\n\t\t\t.union([z.string(), z.boolean(), TelescopeConfigSchema])\n\t\t\t.optional(),\n\t\topenapi: z.union([z.boolean(), OpenApiConfigSchema]).optional(),\n\t\truntime: z.enum(['node', 'bun']).optional(),\n\t\tenv: z.union([z.string(), z.array(z.string())]).optional(),\n\n\t\t// Entry point for non-gkm apps (used by dev and docker build)\n\t\tentry: z.string().optional(),\n\n\t\t// Framework (backend, web, or mobile)\n\t\tframework: FrameworkSchema.optional(),\n\t\t// Web/mobile: config file paths for env sniffing (calls .parse() at import)\n\t\tconfig: z\n\t\t\t.object({\n\t\t\t\tclient: z.string().optional(),\n\t\t\t\tserver: z.string().optional(),\n\t\t\t})\n\t\t\t.optional(),\n\t})\n\t// Note: routes is optional for backend apps - some backends like auth servers don't use routes\n\t.refine(\n\t\t(data) => {\n\t\t\t// Web apps must have a web framework\n\t\t\tif (data.type === 'web') {\n\t\t\t\tif (\n\t\t\t\t\t!data.framework ||\n\t\t\t\t\t!(FRONTEND_FRAMEWORKS as readonly string[]).includes(data.framework)\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\t\t{\n\t\t\tmessage: `Web apps must have a valid web framework (${FRONTEND_FRAMEWORKS.join(', ')})`,\n\t\t\tpath: ['framework'],\n\t\t},\n\t)\n\t.refine(\n\t\t(data) => {\n\t\t\t// Mobile apps must have a mobile framework\n\t\t\tif (data.type === 'mobile') {\n\t\t\t\tif (\n\t\t\t\t\t!data.framework ||\n\t\t\t\t\t!(MOBILE_FRAMEWORKS as readonly string[]).includes(data.framework)\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\t\t{\n\t\t\tmessage: `Mobile apps must have a valid mobile framework (${MOBILE_FRAMEWORKS.join(', ')})`,\n\t\t\tpath: ['framework'],\n\t\t},\n\t);\n\n/** What `gkm test` hands a feature test. */\nconst TestConfigSchema = z\n\t.object({\n\t\t/** The folder of per-database factories; `test/factories` by default. */\n\t\tfactories: z.string().optional(),\n\t})\n\t.strict();\n\n/** How `gkm dev` is reached from outside the apps it runs. */\nconst DevConfigSchema = z\n\t.object({\n\t\t/** Browser origins that may read the discovery endpoint. */\n\t\tallowedOrigins: z\n\t\t\t.array(\n\t\t\t\tz.url().refine(\n\t\t\t\t\t// Checked even when `z.url()` has already failed it.\n\t\t\t\t\t(value) => URL.canParse(value) && new URL(value).origin === value,\n\t\t\t\t\t{\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t'An origin is a scheme, host and port only, e.g. https://console.example.com — no path or trailing slash',\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t)\n\t\t\t.optional(),\n\t\t/** The discovery endpoint's loopback port; 4983 by default. */\n\t\tdiscoveryPort: z.number().int().min(0).max(65535).optional(),\n\t})\n\t.strict();\n\n/**\n * Workspace configuration schema.\n */\nexport const WorkspaceConfigSchema = z\n\t.object({\n\t\tname: z.string().optional(),\n\t\t/**\n\t\t * Where the workspace's own constructs live, relative to its root.\n\t\t *\n\t\t * Infrastructure is a fact about the product, not about the process that\n\t\t * happens to import it — so a database two apps share is declared once,\n\t\t * at the top, and both find it. An app-level glob still works and is\n\t\t * additive, for something only one app uses.\n\t\t */\n\t\tconstructs: RoutesSchema.optional(),\n\t\t/**\n\t\t * Apps that nothing declares.\n\t\t *\n\t\t * Optional, and normally absent: a `site` is an app and so is a\n\t\t * `rest-api` that named one, so the list is read off the graph. This is\n\t\t * the escape hatch for what no construct describes — a mobile app, a\n\t\t * process someone runs by hand — and for overriding one field of a\n\t\t * derived app without restating it.\n\t\t */\n\t\tapps: z.record(z.string(), AppConfigSchema).optional(),\n\t\tshared: SharedConfigSchema.optional(),\n\t\tdeploy: DeployConfigSchema.optional(),\n\t\tstages: StagesConfigSchema,\n\t\tsecrets: SecretsConfigSchema.optional(),\n\t\tstate: StateConfigSchema.optional(),\n\t\ttest: TestConfigSchema.optional(),\n\t\tdev: DevConfigSchema.optional(),\n\t})\n\t// Strict, so a key this schema no longer has fails instead of being\n\t// dropped: a leftover `services:` block is a question the config used to\n\t// answer, and silently ignoring it would hide that the answer moved.\n\t.strict()\n\t.refine(\n\t\t(data) => {\n\t\t\t// Validate dependencies reference existing apps\n\t\t\tconst appNames = Object.keys(data.apps ?? {});\n\t\t\tfor (const [appName, app] of Object.entries(data.apps ?? {})) {\n\t\t\t\tfor (const dep of app.dependencies ?? []) {\n\t\t\t\t\tif (!appNames.includes(dep)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t// Prevent self-dependency\n\t\t\t\t\tif (dep === appName) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\t\t{\n\t\t\tmessage:\n\t\t\t\t'App dependencies must reference existing apps and cannot be self-referential',\n\t\t},\n\t)\n\t.refine(\n\t\t(data) => {\n\t\t\t// Check for circular dependencies\n\t\t\tconst appNames = Object.keys(data.apps ?? {});\n\t\t\tconst visited = new Set<string>();\n\t\t\tconst recStack = new Set<string>();\n\n\t\t\tfunction hasCycle(app: string): boolean {\n\t\t\t\tif (recStack.has(app)) return true;\n\t\t\t\tif (visited.has(app)) return false;\n\n\t\t\t\tvisited.add(app);\n\t\t\t\trecStack.add(app);\n\n\t\t\t\tconst deps = data.apps?.[app]?.dependencies ?? [];\n\t\t\t\tfor (const dep of deps) {\n\t\t\t\t\tif (hasCycle(dep)) return true;\n\t\t\t\t}\n\n\t\t\t\trecStack.delete(app);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tfor (const app of appNames) {\n\t\t\t\tvisited.clear();\n\t\t\t\trecStack.clear();\n\t\t\t\tif (hasCycle(app)) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\t\t{\n\t\t\tmessage: 'Circular dependencies detected between apps',\n\t\t},\n\t)\n\t.superRefine((data, ctx) => {\n\t\t// Validate deploy targets are supported\n\t\tconst defaultTarget = data.deploy?.default;\n\t\tconst ownTargets = data.deploy?.targets ?? {};\n\t\tif (defaultTarget && !isDeployTargetSupported(defaultTarget, ownTargets)) {\n\t\t\tctx.addIssue({\n\t\t\t\tcode: 'custom',\n\t\t\t\tmessage: getDeployTargetError(defaultTarget),\n\t\t\t\tpath: ['deploy', 'default'],\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const [appName, app] of Object.entries(data.apps ?? {})) {\n\t\t\tif (app.deploy && !isDeployTargetSupported(app.deploy, ownTargets)) {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: getDeployTargetError(app.deploy, appName),\n\t\t\t\t\tpath: ['apps', appName, 'deploy'],\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Validate workspace name is required for SSM state provider\n\t\tif (data.state?.provider === 'ssm' && !data.name) {\n\t\t\tctx.addIssue({\n\t\t\t\tcode: 'custom',\n\t\t\t\tmessage:\n\t\t\t\t\t'Workspace name is required when using SSM state provider. Add \"name\" to your gkm.config.ts.',\n\t\t\t\tpath: ['name'],\n\t\t\t});\n\t\t}\n\t});\n\n/**\n * Validate workspace configuration.\n * Throws ZodError with detailed messages on validation failure.\n */\nexport function validateWorkspaceConfig(\n\tconfig: unknown,\n): z.infer<typeof WorkspaceConfigSchema> {\n\treturn WorkspaceConfigSchema.parse(config);\n}\n\n/**\n * Safe validation that returns result instead of throwing.\n */\nexport function safeValidateWorkspaceConfig(config: unknown): {\n\tsuccess: boolean;\n\tdata?: z.infer<typeof WorkspaceConfigSchema>;\n\terror?: z.ZodError;\n} {\n\tconst result = WorkspaceConfigSchema.safeParse(config);\n\tif (result.success) {\n\t\treturn { success: true, data: result.data };\n\t}\n\treturn { success: false, error: result.error };\n}\n\n/**\n * Format Zod errors into user-friendly messages.\n */\nexport function formatValidationErrors(error: z.ZodError): string {\n\tconst messages = error.issues.map((issue: z.core.$ZodIssue) => {\n\t\tconst path = issue.path.join('.');\n\t\treturn path ? ` - ${path}: ${issue.message}` : ` - ${issue.message}`;\n\t});\n\n\treturn `Workspace configuration validation failed:\\n${messages.join('\\n')}`;\n}\n\nexport type ValidatedWorkspaceConfig = z.infer<typeof WorkspaceConfigSchema>;\n\nexport { SUPPORTED_DEPLOY_TARGETS, PHASE_2_DEPLOY_TARGETS };\n","import type { ConstructManifest } from '@geekmidas/manifest';\nimport type { AwsRegion, StateConfig } from '../deploy/StateProvider.js';\nimport type { DeployTargetEntry } from '../target/types';\n\nexport type { AwsRegion };\n\nimport type {\n\tGkmConfig,\n\tHooksConfig,\n\tOpenApiConfig,\n\tRoutes,\n\tRuntime,\n\tTelescopeConfig,\n} from '../types.js';\nimport type {\n\tBackendFramework,\n\tBackupsConfig,\n\tDnsConfig,\n\tDnsProvider,\n\tFramework,\n\tFrontendFramework,\n\tMobileFramework,\n} from './schema.js';\n\nexport type {\n\tBackendFramework,\n\tBackupsConfig,\n\tDnsConfig,\n\tDnsProvider,\n\tFramework,\n\tFrontendFramework,\n\tMobileFramework,\n};\n\n/**\n * Where a stage — or one app — deploys, by target name: a built-in\n * (`dokploy`, `sst`) or a key of `deploy.targets`.\n *\n * @example\n * ```ts\n * deploy: 'dokploy'\n * deploy: 'acme' // deploy: { targets: { acme: '@acme/gkm-target' } }\n * ```\n */\nexport type DeployTargetName = 'dokploy' | 'compose' | 'sst' | (string & {});\n\n/**\n * Each deployed stage's base domain — `deploy.domains`.\n *\n * A fact about the deployment rather than the platform, so every target reads\n * it. The root site answers on the base domain; every other surface on\n * `{subdomain}.{domain}`, where the subdomain is the construct's own\n * `subdomain`, or its id kebab-cased.\n *\n * @example\n * ```ts\n * deploy: {\n * domains: { production: 'myapp.com', staging: 'staging.myapp.com' },\n * }\n *\n * // production:\n * // - new StaticSite('Web', { root: true }) → myapp.com\n * // - new RestApi('Api', { subdomain: 'api' }) → api.myapp.com\n * // - new RestApi('Webhooks', { … }) → webhooks.myapp.com\n * ```\n */\nexport type DomainsConfig = Record<string, string>;\n\n/**\n * Per-app domain override configuration.\n *\n * Can be a single domain string (used for all stages) or\n * stage-specific domains.\n *\n * @example\n * ```ts\n * // Single domain for all stages\n * domain: 'api.custom.com'\n *\n * // Stage-specific domains\n * domain: {\n * development: 'api.dev.custom.com',\n * staging: 'api.staging.custom.com',\n * production: 'api.custom.com',\n * }\n * ```\n */\nexport type AppDomainConfig = string | Record<string, string>;\n\n/**\n * Dokploy workspace deployment configuration.\n *\n * Configures how the workspace is deployed to a Dokploy server.\n * One workspace maps to one Dokploy project with stage-based environments.\n * Project IDs are stored in deploy state (not config) and created on first deploy.\n *\n * @example Single endpoint for all stages:\n * ```ts\n * deploy: {\n * default: 'dokploy',\n * registry: 'ghcr.io/myorg',\n * dokploy: {\n * endpoint: 'https://dokploy.myserver.com',\n * },\n * }\n * ```\n *\n * @example Per-stage endpoints (different Dokploy servers):\n * ```ts\n * deploy: {\n * default: 'dokploy',\n * registry: 'ghcr.io/myorg',\n * dokploy: {\n * endpoints: {\n * development: 'https://dev-dokploy.myserver.com',\n * production: 'https://dokploy.myserver.com',\n * },\n * },\n * }\n * ```\n */\nexport interface DokployWorkspaceConfig {\n\t/** Dokploy API endpoint for all stages */\n\tendpoint?: string;\n\t/** Per-stage Dokploy API endpoints (overrides endpoint) */\n\tendpoints?: Record<string, string>;\n\t/** Registry ID in Dokploy (auto-configured) */\n\tregistryId?: string;\n\t/** How a release is waited for and checked before it counts as live. */\n\tverify?: DokployVerifyConfig;\n}\n\n/**\n * How a Dokploy release is waited for and checked.\n *\n * A release counts once Dokploy's deployment has finished and the app has\n * answered its health route `healthyAfter` times in a row. One that does not\n * is rolled back.\n */\nexport interface DokployVerifyConfig {\n\t/** How long Dokploy may take to finish a deployment. Default 10 minutes. */\n\tdeploymentTimeoutMs?: number;\n\t/**\n\t * The path each backend is checked on, under its domain. Default\n\t * `/health`, which every production server build serves. A site is\n\t * checked at `/`.\n\t */\n\thealthCheckPath?: string;\n\t/** Consecutive 2xx answers that make an app healthy. Default 3. */\n\thealthyAfter?: number;\n\t/** Between health checks, and the first wait between polls. Default 2s. */\n\tintervalMs?: number;\n\t/** How long an app may take to become healthy. Default 5 minutes. */\n\thealthTimeoutMs?: number;\n}\n\n/**\n * The `compose` target's own settings — `deploy.compose`. Read by `gkm\n * compose` and `gkm deploy --target compose` only; the Dokploy target never\n * consults it.\n *\n * @example\n * ```ts\n * deploy: { compose: { logs: true } }\n * deploy: { compose: { logs: { port: 5081, retentionDays: 14 } } }\n * deploy: { compose: { logs: { public: { allow: ['203.0.113.7', '10.0.0.0/8'] } } } }\n * ```\n */\nexport interface ComposeWorkspaceConfig {\n\t/**\n\t * Run OpenObserve in the stack and send every backend's logs and traces\n\t * to it. `true` for the defaults.\n\t */\n\tlogs?: boolean | ComposeLogsConfig;\n}\n\n/** How the stack's OpenObserve is run and reached — `deploy.compose.logs`. */\nexport interface ComposeLogsConfig {\n\t/**\n\t * The port it is published on, on 127.0.0.1 only — reached from another\n\t * machine through an SSH tunnel. Default 5080.\n\t */\n\tport?: number;\n\t/**\n\t * How many days of data it keeps (OpenObserve's\n\t * `ZO_COMPACT_DATA_RETENTION_DAYS`). At least 3; default 30.\n\t */\n\tretentionDays?: number;\n\t/**\n\t * Serve it on `logs.<stage domain>` through the stack's Caddy, to these\n\t * addresses only (IPs or CIDRs), instead of on a loopback port. Every\n\t * other address is answered 403.\n\t */\n\tpublic?: { allow: string[] };\n}\n\n/**\n * DNS provider types for automatic DNS record creation.\n */\nexport type DnsProviderType = 'hostinger' | 'route53' | 'cloudflare' | 'manual';\n\n/**\n * Deployment configuration for the workspace.\n *\n * @example\n * ```ts\n * // Minimal - just set default target\n * deploy: {\n * default: 'dokploy',\n * }\n *\n * // Full configuration with DNS and backups\n * deploy: {\n * default: 'dokploy',\n * registry: 'ghcr.io/myorg',\n * dokploy: {\n * endpoint: 'https://dokploy.myserver.com',\n * projectId: 'proj_abc123',\n * domains: {\n * production: 'myapp.com',\n * },\n * },\n * dns: {\n * provider: 'hostinger',\n * domain: 'myapp.com',\n * },\n * backups: {\n * type: 's3',\n * region: 'us-east-1',\n * },\n * }\n * ```\n */\nexport interface DeployConfig {\n\t/** Default deploy target for all apps (default: 'dokploy') */\n\tdefault?: DeployTargetName;\n\t/**\n\t * Targets that do not ship with the CLI, by the name `default` (or an\n\t * app's `deploy`) uses: a package whose default export is the target, a\n\t * target object, or either with its options.\n\t *\n\t * ```ts\n\t * targets: {\n\t * acme: '@acme/gkm-target',\n\t * fly: ['@acme/gkm-fly', { org: 'acme' }],\n\t * local: defineTarget({ … }),\n\t * }\n\t * ```\n\t *\n\t * A package is never guessed from a name: it is installed and listed here.\n\t * A built-in's name cannot be taken.\n\t */\n\ttargets?: Record<string, DeployTargetEntry>;\n\t/**\n\t * Whose deploy this is, on a target shared with other workspaces — an\n\t * organisation or a team, lowercase `[a-z0-9-]`. Two workspaces with the\n\t * same name deploying to one Dokploy are kept apart by it: it is in the\n\t * project's ownership marker, the image path\n\t * (`<registry>/<namespace>/<project>-<app>`) and every resource name.\n\t *\n\t * Defaults to the kebab-cased workspace name, which adds nothing to the\n\t * names a workspace was already deployed under.\n\t */\n\tnamespace?: string;\n\t/** Each deployed stage's base domain — see {@link DomainsConfig}. */\n\tdomains?: DomainsConfig;\n\t/**\n\t * The container registry the apps' images are pushed to and pulled from,\n\t * whatever the target — `ghcr.io/myorg`. An image is\n\t * `<registry>/<namespace>/<project>-<app>:<tag>`.\n\t */\n\tregistry?: string;\n\t/** Dokploy-specific configuration */\n\tdokploy?: DokployWorkspaceConfig;\n\t/** What the `compose` target runs beside the apps — see {@link ComposeWorkspaceConfig}. */\n\tcompose?: ComposeWorkspaceConfig;\n\t/** DNS configuration for automatic record creation */\n\tdns?: DnsConfig;\n\t/** Backup destination configuration for database services */\n\tbackups?: BackupsConfig;\n}\n\n/**\n * Models package configuration for shared schemas.\n *\n * Configures a shared models package containing Zod schemas\n * that can be used across backend and frontend apps.\n *\n * @example\n * ```ts\n * shared: {\n * models: {\n * path: 'packages/models',\n * schema: 'zod',\n * },\n * }\n * ```\n */\nexport interface ModelsConfig {\n\t/** Path to models package relative to workspace root (default: 'packages/models') */\n\tpath?: string;\n\t/**\n\t * Schema library to use (default: 'zod').\n\t * Currently only 'zod' is supported.\n\t * Future: any StandardSchema-compatible library\n\t */\n\tschema?: 'zod';\n}\n\n/**\n * Shared packages configuration.\n *\n * Configures shared packages in the monorepo that are\n * used by multiple apps.\n *\n * @example\n * ```ts\n * shared: {\n * packages: ['packages/*', 'libs/*'],\n * models: {\n * path: 'packages/models',\n * schema: 'zod',\n * },\n * }\n * ```\n */\nexport interface SharedConfig {\n\t/** Glob patterns for shared packages (default: ['packages/*']) */\n\tpackages?: string[];\n\t/** Models package configuration */\n\tmodels?: ModelsConfig;\n}\n\n/**\n * The project's stages, named by the project.\n *\n * Stage names were literals scattered through the CLI — `gkm dev` ran as\n * `development` and looked for `dev` secrets first, a deploy script said\n * `production` — so a team that called its stages anything else was\n * working against the tool. They are declared once, here, and read.\n *\n * @example\n * ```ts\n * stages: { local: 'dev', deployed: ['staging', 'prod'], protected: ['prod'] }\n * ```\n */\nexport interface StagesConfig {\n\t/**\n\t * What `gkm dev`, `exec`, `setup` and `test` run as: the stage whose\n\t * secrets are read locally, and whose containers carry no suffix.\n\t *\n\t * Never also a deployed stage. Secrets are stored per stage name, so a\n\t * deployed stage sharing it would share its secrets with every laptop.\n\t */\n\tlocal: string;\n\t/** The stages `gkm deploy --stage` accepts. */\n\tdeployed: string[];\n\t/**\n\t * Deployed stages whose resources outlive the stack that made them —\n\t * retained on removal and protected, e.g. the stage users are on.\n\t */\n\tprotected?: string[];\n}\n\n/**\n * Secrets encryption configuration.\n *\n * Configures how secrets are encrypted for deployment.\n * Secrets are stored encrypted in `.gkm/secrets/{stage}.json`\n * with keys stored separately in `~/.gkm/keys/{namespace}/{project}/{stage}.key`.\n *\n * @example\n * ```ts\n * secrets: {\n * enabled: true,\n * algorithm: 'aes-256-gcm',\n * kdf: 'scrypt',\n * }\n * ```\n */\nexport interface SecretsConfig {\n\t/** Enable encrypted secrets (default: true) */\n\tenabled?: boolean;\n\t/**\n\t * Where deployed stages' secrets live, so a deploy can reach them from\n\t * anywhere: `'file'` (default — the encrypted `.gkm/secrets/<stage>.json`,\n\t * which cannot serve CI while `.gkm/` is gitignored), `{ provider: 'ssm',\n\t * region }` (each stage's secrets in an SSM parameter in its own AWS\n\t * account, up to 8 KB), `{ provider: 'secrets-manager', region, kmsKeyId? }`\n\t * (a Secrets Manager secret in its own account, up to 64 KB), or any object\n\t * implementing `SecretsStore`. The local stage always uses the file.\n\t */\n\tstore?: import('../secrets/store.js').SecretsStoreConfig;\n\t/** Encryption algorithm (default: 'aes-256-gcm') */\n\talgorithm?: string;\n\t/** Key derivation function (default: 'scrypt') */\n\tkdf?: 'scrypt' | 'pbkdf2';\n}\n\n/**\n * Base app configuration properties (shared between input and normalized).\n *\n * @example\n * ```ts\n * // Backend app with gkm routes\n * api: {\n * type: 'backend',\n * path: 'apps/api',\n * port: 3000,\n * routes: './src/endpoints/**\\/*.ts',\n * envParser: './src/config/env',\n * logger: './src/config/logger',\n * }\n *\n * // Backend app with entry point (e.g., Better Auth)\n * auth: {\n * type: 'backend',\n * path: 'apps/auth',\n * port: 3001,\n * entry: './src/index.ts',\n * framework: 'better-auth',\n * requiredEnv: ['DATABASE_URL', 'BETTER_AUTH_SECRET'],\n * }\n *\n * // Frontend app\n * web: {\n * type: 'web',\n * path: 'apps/web',\n * port: 3002,\n * framework: 'nextjs',\n * dependencies: ['api', 'auth'],\n * }\n * ```\n */\ninterface AppConfigBase {\n\t/**\n\t * App type.\n\t * - 'backend': Server-side app (API, auth service, etc.)\n\t * - 'web': Client-side web app (Next.js, Vite, TanStack Start, etc.)\n\t * - 'mobile': Native mobile app (Expo). Built/deployed via its own\n\t * toolchain (e.g. EAS Build) — Docker/Dokploy steps are skipped.\n\t * @default 'backend'\n\t */\n\ttype?: 'backend' | 'web' | 'mobile';\n\n\t/**\n\t * Path to the app relative to workspace root.\n\t * @example 'apps/api', 'apps/web', 'services/auth'\n\t */\n\tpath: string;\n\n\t/**\n\t * Development server port.\n\t * Must be unique across all apps in the workspace.\n\t * @example 3000, 3001, 3002\n\t */\n\tport: number;\n\n\t/**\n\t * Per-app deploy target override.\n\t * Overrides `deploy.default` for this specific app.\n\t * @example 'dokploy', 'vercel'\n\t */\n\tdeploy?: DeployTargetName;\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Backend-specific (gkm routes mode)\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Constructs glob pattern — one glob, every kind.\n\t *\n\t * A glob per kind cannot find a resource, because a declared `ObjectStorage`\n\t * has no kind to be listed under. This one is what reconcile reads to derive\n\t * the containers a stage needs, and it replaces the per-kind globs below.\n\t *\n\t * @example './src/**\\/*.ts'\n\t */\n\tconstructs?: Routes;\n\n\t/**\n\t * Routes glob pattern for gkm endpoints.\n\t * @example './src/endpoints/**\\/*.ts'\n\t */\n\troutes?: Routes;\n\n\t/**\n\t * Functions glob pattern for Lambda functions.\n\t * @example './src/functions/**\\/*.ts'\n\t */\n\tfunctions?: Routes;\n\n\t/**\n\t * Crons glob pattern for scheduled tasks.\n\t * @example './src/crons/**\\/*.ts'\n\t */\n\tcrons?: Routes;\n\n\t/**\n\t * Queues glob pattern for point-to-point workers.\n\t * @example './src/queues/**\\/*.ts'\n\t */\n\tqueues?: Routes;\n\n\t/**\n\t * Topics glob pattern for the topics subscribers bind to.\n\t * @example './src/topics/**\\/*.ts'\n\t */\n\ttopics?: Routes;\n\n\t/**\n\t * Subscribers glob pattern for event handlers.\n\t * @example './src/subscribers/**\\/*.ts'\n\t */\n\tsubscribers?: Routes;\n\n\t/**\n\t * Path to environment parser module.\n\t * @example './src/config/env'\n\t */\n\tenvParser?: string;\n\n\t/**\n\t * Path to logger module.\n\t * @example './src/config/logger'\n\t */\n\tlogger?: string;\n\n\t/**\n\t * Server lifecycle hooks.\n\t * @example { beforeSetup: './src/hooks/setup.ts' }\n\t */\n\thooks?: HooksConfig;\n\n\t/**\n\t * Telescope request recording configuration.\n\t * @example true, './src/config/telescope', { enabled: true, path: '/__telescope' }\n\t */\n\ttelescope?: string | boolean | TelescopeConfig;\n\n\t/**\n\t * OpenAPI documentation configuration.\n\t * @example true, { output: './src/openapi.ts' }\n\t */\n\topenapi?: boolean | OpenApiConfig;\n\n\t/**\n\t * Runtime environment.\n\t * @default 'node'\n\t */\n\truntime?: Runtime;\n\n\t/**\n\t * Environment file(s) to load during development.\n\t * @example '.env', ['.env', '.env.local']\n\t */\n\tenv?: string | string[];\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Entry point mode (non-gkm apps)\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Entry file path for apps that don't use gkm routes.\n\t *\n\t * When specified, the app is run directly with tsx in development\n\t * and bundled with esbuild for production Docker builds.\n\t *\n\t * Use this for:\n\t * - Better Auth servers\n\t * - Custom Hono/Express apps\n\t * - Any backend that doesn't use gkm's endpoint builder\n\t *\n\t * @example './src/index.ts', './src/server.ts'\n\t */\n\tentry?: string;\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Frontend-specific\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Framework for the app.\n\t *\n\t * Backend frameworks: 'hono', 'better-auth', 'express', 'fastify'\n\t * Frontend frameworks: 'nextjs', 'remix', 'vite', 'tanstack-start'\n\t * Mobile frameworks: 'expo'\n\t *\n\t * @example 'nextjs', 'better-auth', 'hono', 'expo'\n\t */\n\tframework?: Framework;\n\n\t/**\n\t * Config file paths for frontend environment sniffing.\n\t *\n\t * Points to file(s) that call EnvironmentParser.parse() at import time.\n\t * The sniffer imports these files and captures all env vars accessed.\n\t *\n\t * Dependencies are auto-generated as public-prefixed URL vars based on\n\t * the app's framework (e.g. `NEXT_PUBLIC_{DEP}_URL` for Next.js,\n\t * `VITE_{DEP}_URL` for Vite/TanStack Start, `EXPO_PUBLIC_{DEP}_URL`\n\t * for Expo).\n\t *\n\t * @example\n\t * ```ts\n\t * config: {\n\t * client: './src/config/client.ts', // public-prefixed vars for browser\n\t * server: './src/config/server.ts', // server-only vars for SSR\n\t * }\n\t * ```\n\t */\n\tconfig?: {\n\t\t/** Client-side config (public-prefixed vars, available in browser/device) */\n\t\tclient?: string;\n\t\t/** Server-side config (all env vars, for SSR/API routes) */\n\t\tserver?: string;\n\t};\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Deployment\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Override domain for this app.\n\t *\n\t * By default, apps get `{appName}.{baseDomain}` (or just `{baseDomain}`\n\t * for the main frontend). Use this to specify a custom domain.\n\t *\n\t * @example\n\t * ```ts\n\t * // Single domain for all stages\n\t * domain: 'api.custom.com'\n\t *\n\t * // Stage-specific domains\n\t * domain: {\n\t * production: 'api.custom.com',\n\t * staging: 'api.staging.custom.com',\n\t * }\n\t * ```\n\t */\n\tdomain?: AppDomainConfig;\n\n\t/**\n\t * Required environment variables for entry-based apps.\n\t *\n\t * Use this instead of envParser for apps that don't use gkm routes.\n\t * The deploy command uses this list to filter which secrets to embed\n\t * in the Docker image.\n\t *\n\t * @example ['DATABASE_URL', 'BETTER_AUTH_SECRET', 'REDIS_URL']\n\t */\n\trequiredEnv?: string[];\n}\n\n/**\n * App configuration input with type-safe dependencies.\n *\n * @template TAppNames - Union of valid app names in the workspace (auto-inferred)\n *\n * @example\n * ```ts\n * // Dependencies are type-checked against app names\n * apps: {\n * api: { path: 'apps/api', port: 3000 },\n * auth: { path: 'apps/auth', port: 3001 },\n * web: {\n * path: 'apps/web',\n * port: 3002,\n * type: 'web',\n * dependencies: ['api', 'auth'], // ✓ Valid\n * // dependencies: ['invalid'], // ✗ Type error\n * },\n * }\n * ```\n */\nexport interface AppConfigInput<TAppNames extends string = string>\n\textends AppConfigBase {\n\t/** Dependencies on other apps in the workspace (type-safe) */\n\tdependencies?: TAppNames[];\n}\n\n/**\n * App configuration (legacy, for backwards compatibility).\n * @deprecated Use AppConfigInput for new code\n */\nexport interface AppConfig extends AppConfigBase {\n\t/** Dependencies on other apps in the workspace */\n\tdependencies?: string[];\n}\n\n/**\n * Base app input type for type inference.\n */\nexport type AppInput = AppConfigBase & {\n\tdependencies?: readonly string[];\n};\n\n/**\n * Apps record type for workspace configuration.\n */\nexport type AppsRecord = Record<string, AppInput>;\n\n/**\n * Constrain apps so dependencies only reference valid app names.\n * Dependencies must be an array of valid app names from the workspace.\n */\nexport type ConstrainedApps<TApps extends AppsRecord> = {\n\t[K in keyof TApps]: Omit<TApps[K], 'dependencies'> & {\n\t\tdependencies?: readonly (keyof TApps & string)[];\n\t};\n};\n\n/**\n * Full workspace input type with constrained dependencies.\n *\n * @example\n * ```ts\n * import { defineWorkspace } from '@geekmidas/cli';\n *\n * export default defineWorkspace({\n * name: 'my-app',\n * apps: {\n * api: {\n * path: 'apps/api',\n * port: 3000,\n * routes: './src/endpoints/**\\/*.ts',\n * },\n * web: {\n * type: 'web',\n * path: 'apps/web',\n * port: 3001,\n * framework: 'nextjs',\n * dependencies: ['api'],\n * },\n * },\n * deploy: {\n * default: 'dokploy',\n * },\n * });\n * ```\n */\nexport type WorkspaceInput<TApps extends AppsRecord> = {\n\t/**\n\t * Where the workspace's own constructs live, relative to its root.\n\t *\n\t * The product's infrastructure, declared once for every app that consumes\n\t * it. Additive with an app's own glob rather than replacing it.\n\t */\n\tconstructs?: Routes;\n\t/** Workspace name (defaults to root package.json name) */\n\tname?: string;\n\t/**\n\t * Apps that nothing declares.\n\t *\n\t * Normally absent: a `site` is an app and so is a `rest-api` that named\n\t * one, so the list is read off the graph. This is the escape hatch for what\n\t * no construct describes.\n\t */\n\tapps?: ConstrainedApps<TApps>;\n\t/** Shared packages configuration */\n\tshared?: SharedConfig;\n\t/** Deployment configuration */\n\tdeploy?: DeployConfig;\n\t/** The project's stages: which one is local, which deploy */\n\tstages: StagesConfig;\n\t/** Encrypted secrets configuration */\n\tsecrets?: SecretsConfig;\n\t/** State provider configuration (local filesystem by default, or SSM for team collaboration) */\n\tstate?: StateConfig;\n\t/** How `gkm dev` is reached: the discovery endpoint's port and origins */\n\tdev?: DevConfig;\n};\n\n/**\n * How `gkm dev` is reached from outside the apps it runs.\n */\nexport interface DevConfig {\n\t/**\n\t * Browser origins allowed to read the discovery endpoint — and this\n\t * workspace in it — e.g. `['https://console.example.com']`.\n\t *\n\t * Empty by default: any page the developer visits can send requests to a\n\t * loopback port, so none is answered unless it is listed here. A request\n\t * with no `Origin` (curl, a local process) still needs the session token.\n\t */\n\tallowedOrigins?: string[];\n\t/**\n\t * The loopback port the discovery endpoint listens on. Defaults to 4983;\n\t * `GKM_DISCOVERY_PORT` overrides it.\n\t */\n\tdiscoveryPort?: number;\n}\n\n/**\n * How `gkm test` builds what a feature test is handed.\n */\nexport interface TestConfig {\n\t/**\n\t * The folder of test factories, relative to the root.\n\t *\n\t * Defaults to `test/factories`. A factory belongs to a database, not to an\n\t * app, so each file is named after one — `database.ts` for `Database`,\n\t * `auth-database.ts` for `AuthDatabase` — and exports `createFactory(db)`.\n\t * Every app's harness builds them on each test's transactions, and a test is\n\t * handed them as `factories`, keyed by service name.\n\t */\n\tfactories?: string;\n}\n\n/**\n * Extract app names from apps record.\n */\nexport type InferAppNames<TApps extends AppsRecord> = keyof TApps & string;\n\n/**\n * Inferred workspace config with proper app name types.\n */\nexport type InferredWorkspaceConfig<TApps extends AppsRecord> = {\n\t/**\n\t * Where the workspace's own constructs live, relative to its root.\n\t *\n\t * The product's infrastructure, declared once for every app that consumes\n\t * it. Additive with an app's own glob rather than replacing it.\n\t */\n\tconstructs?: Routes;\n\tname?: string;\n\tapps?: {\n\t\t[K in keyof TApps]: Omit<TApps[K], 'dependencies'> & {\n\t\t\tdependencies?: InferAppNames<TApps>[];\n\t\t};\n\t};\n\tshared?: SharedConfig;\n\tdeploy?: DeployConfig;\n\tstages: StagesConfig;\n\tsecrets?: SecretsConfig;\n\tstate?: StateConfig;\n\ttest?: TestConfig;\n\tdev?: DevConfig;\n};\n\n// Legacy types for backwards compatibility\n/** @deprecated Use WorkspaceInput */\nexport type RawWorkspaceInput = {\n\tname?: string;\n\tapps: AppsRecord;\n\tshared?: SharedConfig;\n\tdeploy?: DeployConfig;\n\tsecrets?: SecretsConfig;\n\tstate?: StateConfig;\n};\n\n/** @deprecated Use WorkspaceInput */\nexport type WorkspaceConfigInput<\n\tT extends RawWorkspaceInput = RawWorkspaceInput,\n> = WorkspaceInput<T['apps']>;\n\n/**\n * Workspace configuration for multi-app monorepos.\n *\n * Use `defineWorkspace()` helper for type-safe configuration with\n * auto-completion and dependency validation.\n *\n * @example\n * ```ts\n * // gkm.config.ts\n * import { defineWorkspace } from '@geekmidas/cli';\n *\n * export default defineWorkspace({\n * name: 'my-saas',\n *\n * // App definitions\n * apps: {\n * // Backend API with gkm routes\n * api: {\n * path: 'apps/api',\n * port: 3000,\n * routes: './src/endpoints/**\\/*.ts',\n * envParser: './src/config/env',\n * logger: './src/config/logger',\n * telescope: true,\n * },\n *\n * // Better Auth service\n * auth: {\n * path: 'apps/auth',\n * port: 3001,\n * entry: './src/index.ts',\n * framework: 'better-auth',\n * requiredEnv: ['DATABASE_URL', 'BETTER_AUTH_SECRET'],\n * },\n *\n * // Next.js frontend\n * web: {\n * type: 'web',\n * path: 'apps/web',\n * port: 3002,\n * framework: 'nextjs',\n * dependencies: ['api', 'auth'],\n * },\n * },\n *\n * // Deployment configuration\n * deploy: {\n * default: 'dokploy',\n * registry: 'ghcr.io/myorg',\n * domains: {\n * production: 'myapp.com',\n * staging: 'staging.myapp.com',\n * },\n * dokploy: {\n * endpoint: 'https://dokploy.myserver.com',\n * projectId: 'proj_abc123',\n * },\n * },\n *\n * // Shared packages\n * shared: {\n * packages: ['packages/*'],\n * models: { path: 'packages/models' },\n * },\n * });\n * ```\n *\n * @deprecated Use WorkspaceInput with defineWorkspace for type inference\n */\nexport interface WorkspaceConfig {\n\t/**\n\t * Where the workspace's own constructs live, relative to its root.\n\t *\n\t * The product's infrastructure, declared once for every app that consumes\n\t * it. Additive with an app's own glob rather than replacing it.\n\t */\n\tconstructs?: Routes;\n\t/** Workspace name (defaults to root package.json name) */\n\tname?: string;\n\n\t/** Apps that nothing declares. Normally absent — see `WorkspaceInput`. */\n\tapps?: Record<string, AppConfig>;\n\n\t/** Shared packages configuration */\n\tshared?: SharedConfig;\n\n\t/** Default deployment configuration */\n\tdeploy?: DeployConfig;\n\n\t/** The project's stages: which one is local, which deploy */\n\tstages: StagesConfig;\n\n\t/** Encrypted secrets configuration */\n\tsecrets?: SecretsConfig;\n\n\t/** State provider configuration (local filesystem by default, or SSM for team collaboration) */\n\tstate?: StateConfig;\n\n\t/** What `gkm test` hands a feature test: where the factories are. */\n\ttest?: TestConfig;\n\n\t/** How `gkm dev` is reached: the discovery endpoint's port and origins. */\n\tdev?: DevConfig;\n}\n\n/**\n * Normalized app configuration with resolved defaults.\n *\n * This is the internal representation after processing user input.\n * All optional fields have been resolved to their defaults.\n */\nexport interface NormalizedAppConfig extends Omit<AppConfigBase, 'type'> {\n\t/**\n\t * Whether the base domain points at this site.\n\t *\n\t * Carried from the `site` declaration rather than configured, because which\n\t * site is primary is structural — its *hostname* is what varies by stage,\n\t * and that is `domain`.\n\t */\n\troot?: boolean;\n\n\t/**\n\t * The label this app answers on under a stage's domain, carried from its\n\t * declaration — `api` for `api.myapp.com`. Absent, the app's own name.\n\t */\n\tsubdomain?: string;\n\n\t/** App type (always defined after normalization) */\n\ttype: 'backend' | 'web' | 'mobile';\n\t/** Path to the app */\n\tpath: string;\n\t/** Development server port */\n\tport: number;\n\t/** Resolved dependencies array (empty array if none) */\n\tdependencies: string[];\n\t/** Resolved deploy target (app.deploy > deploy.default > 'dokploy') */\n\tresolvedDeployTarget: DeployTargetName;\n\t/** Entry file path for non-gkm apps */\n\tentry?: string;\n\t/** Framework for the app */\n\tframework?: Framework;\n\t/** Override domain for this app */\n\tdomain?: AppDomainConfig;\n\t/** Required environment variables for entry-based apps */\n\trequiredEnv?: string[];\n}\n\n/**\n * Normalized workspace configuration with resolved defaults.\n *\n * This is the internal representation after processing user input.\n * All optional fields have been resolved to their defaults.\n */\nexport interface NormalizedWorkspace {\n\t/** Workspace name (resolved from package.json if not specified) */\n\tname: string;\n\t/** Absolute path to workspace root */\n\troot: string;\n\t/**\n\t * The workspace's own constructs glob, relative to `root`.\n\t *\n\t * Where the product's shared infrastructure is declared. Additive with each\n\t * app's glob, so an app can still declare something only it uses.\n\t */\n\tconstructs?: Routes;\n\t/** Normalized app configurations */\n\tapps: Record<string, NormalizedAppConfig>;\n\t/** Deploy configuration (empty object if not specified) */\n\tdeploy: DeployConfig;\n\t/** Shared packages configuration (empty object if not specified) */\n\tshared: SharedConfig;\n\t/** The project's stages, as declared */\n\tstages: StagesConfig;\n\t/** Secrets configuration (empty object if not specified) */\n\tsecrets: SecretsConfig;\n\t/** State provider configuration (undefined = local filesystem) */\n\tstate?: StateConfig;\n\t/** What `gkm test` hands a feature test (empty object if not specified) */\n\ttest: TestConfig;\n\t/** How `gkm dev` is reached, when configured */\n\tdev?: DevConfig;\n}\n\n/**\n * Result of loading and processing a configuration.\n */\nexport interface LoadedConfig {\n\t/** Whether this is a single-app or workspace config */\n\ttype: 'single' | 'workspace';\n\t/** The raw configuration as loaded */\n\traw: GkmConfig | WorkspaceConfig;\n\t/** Normalized workspace (always available) */\n\tworkspace: NormalizedWorkspace;\n\t/**\n\t * What the workspace's constructs declared, when it has any.\n\t *\n\t * Read once at load, because the apps above are derived from it and a\n\t * caller that re-discovers would be answering the same question twice with\n\t * two chances to disagree. Absent when the workspace declares no constructs.\n\t */\n\tmanifest?: ConstructManifest;\n\t/**\n\t * The files each Worker's crons, queues and subscribers are declared in,\n\t * read in the same discovery — what says which app a worker is built from.\n\t */\n\tbackground?: Record<string, string[]>;\n}\n\n/**\n * Type guard to check if a config is a WorkspaceConfig.\n *\n * @example\n * ```ts\n * const config = await loadConfig();\n * if (isWorkspaceConfig(config)) {\n * // config.apps is available\n * console.log(Object.keys(config.apps));\n * }\n * ```\n */\nexport function isWorkspaceConfig(\n\tconfig: GkmConfig | WorkspaceConfig,\n): config is WorkspaceConfig {\n\tif (typeof config !== 'object' || config === null) return false;\n\n\t// `apps` used to be the only tell, and it stopped being one when apps\n\t// became derived: a workspace that declares its apps through constructs has\n\t// none. `constructs` at the top level is the other tell — a single-app\n\t// `defineConfig` has no notion of a workspace-wide glob — and either alone\n\t// is enough.\n\tif ('apps' in config && typeof config.apps === 'object') return true;\n\n\t// Only when nothing else says it is one app. `openapi`, `telescope`,\n\t// `hooks` and the rest configure a single process, so a config that\n\t// sets any of them is that process's — the workspace schema is strict,\n\t// and reading one of these as a workspace would reject what it was never\n\t// meant to accept.\n\treturn (\n\t\t'constructs' in config &&\n\t\tObject.keys(config).every((key) => WORKSPACE_KEYS.has(key))\n\t);\n}\n\n/** Every top-level key a workspace config has — see `WorkspaceConfigSchema`. */\nconst WORKSPACE_KEYS: ReadonlySet<string> = new Set([\n\t'name',\n\t'constructs',\n\t'apps',\n\t'shared',\n\t'deploy',\n\t'stages',\n\t'secrets',\n\t'state',\n]);\n","import type { Framework } from './types.js';\n\n/**\n * All public env-var prefixes the toolbox understands.\n *\n * Used when resolving incoming var names back to a dependency\n * (e.g. `VITE_AUTH_URL` → `auth`). A var matching any of these\n * is considered safe to inline into a client bundle.\n */\nexport const PUBLIC_ENV_PREFIXES = [\n\t'NEXT_PUBLIC_',\n\t'VITE_',\n\t'EXPO_PUBLIC_',\n] as const;\n\n/**\n * Resolve the public env-var prefix that a frontend framework's bundler\n * inlines into client code at build time:\n *\n * - `nextjs` → `NEXT_PUBLIC_`\n * - `vite` → `VITE_`\n * - `tanstack-start` → `VITE_` (uses Vite under the hood)\n * - `expo` → `EXPO_PUBLIC_`\n * - `remix` → `''` (Remix exposes via loaders, no prefix convention)\n *\n * For backend frameworks or unspecified, falls back to `NEXT_PUBLIC_` to\n * preserve the historical default. Backend apps never read these vars\n * from a bundle, so the prefix only affects scaffolding/symmetry.\n */\nexport function getPublicEnvPrefix(framework?: Framework): string {\n\tswitch (framework) {\n\t\tcase 'vite':\n\t\tcase 'tanstack-start':\n\t\t\treturn 'VITE_';\n\t\tcase 'expo':\n\t\t\treturn 'EXPO_PUBLIC_';\n\t\tcase 'remix':\n\t\t\treturn '';\n\t\tcase 'nextjs':\n\t\t\treturn 'NEXT_PUBLIC_';\n\t\tdefault:\n\t\t\treturn 'NEXT_PUBLIC_';\n\t}\n}\n\n/**\n * Strip a known public prefix from a var name.\n * Returns the un-prefixed name, or `null` if no prefix matched.\n */\nexport function stripPublicPrefix(name: string): string | null {\n\tfor (const prefix of PUBLIC_ENV_PREFIXES) {\n\t\tif (prefix && name.startsWith(prefix)) {\n\t\t\treturn name.slice(prefix.length);\n\t\t}\n\t}\n\treturn null;\n}\n","import { basename, isAbsolute, join } from 'node:path';\nimport { assertKnownSecretsStore } from '../secrets/providers.js';\nimport type { GkmConfig, Routes } from '../types.js';\nimport {\n\tformatValidationErrors,\n\tsafeValidateWorkspaceConfig,\n} from './schema.js';\nimport { validateStages } from './stages.js';\nimport type {\n\tAppConfig,\n\tAppsRecord,\n\tDeployTargetName,\n\tInferredWorkspaceConfig,\n\tLoadedConfig,\n\tNormalizedAppConfig,\n\tNormalizedWorkspace,\n\tWorkspaceConfig,\n\tWorkspaceInput,\n} from './types.js';\nimport { isWorkspaceConfig } from './types.js';\n\nexport {\n\tgetPublicEnvPrefix,\n\tPUBLIC_ENV_PREFIXES,\n\tstripPublicPrefix,\n} from './publicEnv.js';\nexport {\n\tformatValidationErrors,\n\tgetDeployTargetError,\n\tisDeployTargetSupported,\n\tisPhase2DeployTarget,\n\tPHASE_2_DEPLOY_TARGETS,\n\tSUPPORTED_DEPLOY_TARGETS,\n\tsafeValidateWorkspaceConfig,\n\tvalidateWorkspaceConfig,\n\tWorkspaceConfigSchema,\n} from './schema.js';\n// Re-export types\nexport type {\n\tAppConfig,\n\tAppConfigInput,\n\tAppInput,\n\tAppsRecord,\n\tBackendFramework,\n\tComposeLogsConfig,\n\tComposeWorkspaceConfig,\n\tConstrainedApps,\n\tDeployConfig,\n\tDeployTargetName,\n\tDokployWorkspaceConfig,\n\tFrontendFramework,\n\tInferAppNames,\n\tInferredWorkspaceConfig,\n\tLoadedConfig,\n\tMobileFramework,\n\tModelsConfig,\n\tNormalizedAppConfig,\n\tNormalizedWorkspace,\n\tSecretsConfig,\n\tSharedConfig,\n\tWorkspaceConfig,\n\tWorkspaceInput,\n} from './types.js';\nexport { isWorkspaceConfig } from './types.js';\n\n/**\n * Validate that all dependencies reference existing apps.\n * Returns the config if valid, throws otherwise.\n */\nfunction validateDependencies<TApps extends AppsRecord>(apps: TApps): void {\n\tconst appNames = new Set(Object.keys(apps));\n\n\tfor (const [appName, app] of Object.entries(apps)) {\n\t\tfor (const dep of app.dependencies ?? []) {\n\t\t\tif (!appNames.has(dep)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid dependency: App \"${appName}\" depends on \"${dep}\" which does not exist. ` +\n\t\t\t\t\t\t`Valid apps are: ${[...appNames].join(', ')}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (dep === appName) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid dependency: App \"${appName}\" cannot depend on itself.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Define workspace configuration with full TypeScript support and type inference.\n *\n * Uses `const` type parameter to infer literal app names, providing:\n * - Autocomplete for app names in dependencies\n * - Type errors for invalid dependency references\n * - Full type inference for the returned config\n *\n * @example\n * ```ts\n * // gkm.config.ts\n * import { defineWorkspace } from '@geekmidas/cli';\n *\n * export default defineWorkspace({\n * name: 'my-saas',\n * apps: {\n * api: {\n * type: 'backend',\n * path: 'apps/api',\n * port: 3000,\n * routes: './src/endpoints/**\\/*.ts',\n * envParser: './src/config/env',\n * logger: './src/logger',\n * },\n * web: {\n * type: 'web',\n * framework: 'nextjs',\n * path: 'apps/web',\n * port: 3001,\n * dependencies: ['api'], // <- autocomplete shows 'api' | 'web'\n * },\n * },\n * });\n *\n * // config.apps.api <- full type inference\n * // config.apps.foo <- TypeScript error\n * ```\n */\nexport function defineWorkspace<const TApps extends AppsRecord>(\n\tconfig: WorkspaceInput<TApps>,\n): InferredWorkspaceConfig<TApps> {\n\tassertNoMovedDeployKeys(config.deploy);\n\tassertKnownSecretsStore(config.secrets?.store);\n\n\t// Validate dependencies at runtime\n\tif (config.apps) validateDependencies(config.apps as unknown as TApps);\n\n\t// Validate with Zod schema\n\tconst result = safeValidateWorkspaceConfig(config);\n\tif (!result.success && result.error) {\n\t\tthrow new Error(formatValidationErrors(result.error));\n\t}\n\n\treturn config as unknown as InferredWorkspaceConfig<TApps>;\n}\n\n/**\n * Get the package name from package.json in the given directory.\n */\nfunction getPackageName(cwd: string): string | undefined {\n\ttry {\n\t\t// Dynamic import would be async, so we use require for sync operation\n\t\t// eslint-disable-next-line @typescript-eslint/no-require-imports\n\t\tconst pkg = require(`${cwd}/package.json`);\n\t\treturn pkg.name?.replace(/^@[^/]+\\//, ''); // Remove scope\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Normalize a workspace configuration with resolved defaults.\n */\nexport function normalizeWorkspace(\n\tconfig: WorkspaceConfig,\n\tcwd: string,\n): NormalizedWorkspace {\n\tconst name = config.name ?? getPackageName(cwd) ?? basename(cwd);\n\tconst defaultTarget = config.deploy?.default ?? 'dokploy';\n\n\tconst normalizedApps: Record<string, NormalizedAppConfig> = {};\n\n\tfor (const [appName, app] of Object.entries(config.apps ?? {})) {\n\t\tnormalizedApps[appName] = normalizeAppConfig(app, defaultTarget);\n\t}\n\n\treturn {\n\t\tname,\n\t\troot: cwd,\n\t\t...(config.constructs ? { constructs: config.constructs } : {}),\n\t\tapps: normalizedApps,\n\t\tdeploy: config.deploy ?? { default: 'dokploy' },\n\t\tshared: config.shared ?? { packages: ['packages/*'] },\n\t\tstages: validateStages(config.stages),\n\t\tsecrets: config.secrets ?? {},\n\t\tstate: config.state,\n\t\ttest: config.test ?? {},\n\t\t...(config.dev ? { dev: config.dev } : {}),\n\t};\n}\n\n/**\n * Normalize an app configuration with resolved defaults.\n * @param app - App configuration\n * @param defaultTarget - Default deploy target from workspace config\n */\nfunction normalizeAppConfig(\n\tapp: AppConfig,\n\tdefaultTarget: DeployTargetName,\n): NormalizedAppConfig {\n\treturn {\n\t\t...app,\n\t\ttype: app.type ?? 'backend',\n\t\tport: app.port,\n\t\tpath: app.path,\n\t\tdependencies: app.dependencies ?? [],\n\t\tresolvedDeployTarget: app.deploy ?? defaultTarget,\n\t};\n}\n\n/**\n * Wrap a single-app GkmConfig as a workspace.\n * This allows existing single-app configs to work seamlessly.\n */\nexport function wrapSingleAppAsWorkspace(\n\tconfig: GkmConfig,\n\tcwd: string,\n): NormalizedWorkspace {\n\t// `config.name` first, the way `defineWorkspace` already reads it. It was\n\t// only honoured on the workspace path, so the same field decided the scope\n\t// in a monorepo and was ignored in a single-app project.\n\tconst name = config.name ?? getPackageName(cwd) ?? basename(cwd);\n\n\t// A projection, not a default-filled stand-in.\n\t//\n\t// Every hardcoded value here was a field the config could already state and\n\t// this quietly dropped: the port it serves on, and the target it deploys to.\n\t// `resolvedDeployTarget` was the worse of the two — a project deploying to\n\t// Vercel was normalised into one that deploys to Dokploy, and nothing said\n\t// so.\n\tconst app: NormalizedAppConfig = {\n\t\ttype: 'backend',\n\t\tpath: '.',\n\t\tport: 3000,\n\t\tdependencies: [],\n\t\tresolvedDeployTarget: config.deploy?.default ?? 'dokploy',\n\t\tconstructs: config.constructs,\n\t\thooks: config.hooks,\n\t\ttelescope: config.telescope,\n\t\topenapi: config.openapi,\n\t\truntime: config.runtime,\n\t\tenv: config.env,\n\t};\n\n\treturn {\n\t\tname,\n\t\troot: cwd,\n\t\t// Keyed `api` because that is what a single-app backend is, and because\n\t\t// the key is what names the application: `applicationName(stage, name,\n\t\t// 'api')` is `production-<name>-api`, beside the\n\t\t// `production-<name>-database` its constructs get. One model, so the\n\t\t// deploy asks the workspace what its apps are called rather than asking\n\t\t// the filesystem.\n\t\tapps: { api: app },\n\t\t// Carried rather than replaced. A single-app project configures its\n\t\t// deploy in the same shape a workspace does — endpoint, registry,\n\t\t// domains — and hardcoding the default here silently discarded all of it,\n\t\t// which surfaced as `resolveHost` refusing to name a host for a stage the\n\t\t// config had no way to describe.\n\t\tdeploy: { default: 'dokploy', ...config.deploy },\n\t\tshared: { packages: [] },\n\t\tstages: validateStages(config.stages),\n\t\tsecrets: config.secrets ?? {},\n\t\ttest: config.test ?? {},\n\t\t...(config.dev ? { dev: config.dev } : {}),\n\t};\n}\n\n/**\n * Process a loaded configuration (either single-app or workspace).\n * Returns a normalized workspace in both cases.\n */\n/**\n * `deploy.dokploy.registry`, which became `deploy.registry`: every target\n * pushes and pulls through the one registry, so it is not Dokploy's to hold.\n *\n * Refused by name rather than dropped: the schema strips keys it does not\n * know, and a registry silently gone would deploy images under no registry.\n */\nexport class DokployRegistryMoved extends Error {\n\tconstructor(readonly registry: unknown) {\n\t\tsuper(\n\t\t\t`deploy.dokploy.registry is now deploy.registry, read by every deploy target. ` +\n\t\t\t\t`In gkm.config.ts, move it up one level: deploy: { registry: ${JSON.stringify(registry)}, dokploy: { … } }.`,\n\t\t);\n\t\tthis.name = 'DokployRegistryMoved';\n\t}\n}\n\n/** Refuse a deploy key that has moved, naming where it lives now. */\nexport function assertNoMovedDeployKeys(deploy: unknown): void {\n\tconst dokploy = (deploy as { dokploy?: unknown } | undefined)?.dokploy;\n\tif (dokploy && typeof dokploy === 'object' && 'registry' in dokploy) {\n\t\tthrow new DokployRegistryMoved((dokploy as { registry: unknown }).registry);\n\t}\n}\n\nexport function processConfig(\n\tconfig: GkmConfig | WorkspaceConfig,\n\tcwd: string,\n): LoadedConfig {\n\tassertNoMovedDeployKeys(config.deploy);\n\n\tif (isWorkspaceConfig(config)) {\n\t\tassertKnownSecretsStore(config.secrets?.store);\n\t\t// Validate workspace config\n\t\tconst result = safeValidateWorkspaceConfig(config);\n\t\tif (!result.success && result.error) {\n\t\t\tthrow new Error(formatValidationErrors(result.error));\n\t\t}\n\n\t\treturn {\n\t\t\ttype: 'workspace',\n\t\t\traw: config,\n\t\t\tworkspace: normalizeWorkspace(config, cwd),\n\t\t};\n\t}\n\n\t// Single-app config - wrap as workspace\n\treturn {\n\t\ttype: 'single',\n\t\traw: config,\n\t\tworkspace: wrapSingleAppAsWorkspace(config, cwd),\n\t};\n}\n\n/**\n * The workspace's own constructs globs, absolute.\n *\n * Infrastructure two apps share is a fact about the product, and declaring it\n * inside one of them makes moving that app a change to the other's database.\n */\nexport function workspaceConstructGlobs(\n\tworkspace: NormalizedWorkspace,\n): string[] {\n\treturn constructPatterns(workspace.constructs).map((pattern) =>\n\t\tisAbsolute(pattern) ? pattern : join(workspace.root, pattern),\n\t);\n}\n\n/**\n * Every constructs glob an app can see, absolute.\n *\n * The workspace's own first, then the app's. Absolute because the app builds\n * from its own directory while the workspace's constructs live above it —\n * a relative glob resolved from the app would look for the product's\n * infrastructure inside one of its consumers and find nothing.\n *\n * Additive, not overriding: an app that declares its own constructs still sees\n * the ones the workspace declared for everybody.\n */\nexport function appConstructGlobs(\n\tworkspace: NormalizedWorkspace,\n\tappName: string,\n): string[] {\n\tconst globs: string[] = workspaceConstructGlobs(workspace);\n\n\tconst app = workspace.apps[appName];\n\tif (app) {\n\t\tconst root = isAbsolute(app.path)\n\t\t\t? app.path\n\t\t\t: join(workspace.root, app.path);\n\t\tfor (const pattern of constructPatterns(app.constructs)) {\n\t\t\tglobs.push(isAbsolute(pattern) ? pattern : join(root, pattern));\n\t\t}\n\t}\n\n\treturn globs;\n}\n/**\n * Every constructs glob in the workspace: its own, plus each app's.\n *\n * One list, so what a deploy provisions and what an app's entry point\n * registers drivers for are read off the same globs.\n */\nexport function allConstructGlobs(workspace: NormalizedWorkspace): string[] {\n\tconst globs = [\n\t\t...workspaceConstructGlobs(workspace),\n\t\t...Object.keys(workspace.apps).flatMap((appName) =>\n\t\t\tappConstructGlobs(workspace, appName),\n\t\t),\n\t];\n\n\t// Every app's list starts with the workspace's, so they repeat.\n\treturn [...new Set(globs)];\n}\n\n/** A `Routes` in any of its accepted shapes, as a flat list of patterns. */\nexport function constructPatterns(routes: Routes | undefined): string[] {\n\tif (!routes) return [];\n\treturn typeof routes === 'string' ? [routes] : routes;\n}\n\n/**\n * Get the GkmConfig for a specific app in a workspace.\n * Useful for running existing single-app commands on a specific app.\n */\nexport function getAppGkmConfig(\n\tworkspace: NormalizedWorkspace,\n\tappName: string,\n): GkmConfig | undefined {\n\tconst app = workspace.apps[appName];\n\tif (!app || app.type !== 'backend') {\n\t\treturn undefined;\n\t}\n\n\treturn {\n\t\t// Workspace-level, and carried here deliberately. The deploy target is\n\t\t// what decides the backend a cache resolves to, which the *entry point*\n\t\t// reads when it decides which drivers to register — and the local target\n\t\t// reads from the workspace when it composes the URLs those drivers\n\t\t// receive. The two have to answer from the same target, or a project on\n\t\t// a server is handed a `postgres://` cache URL by an entry that\n\t\t// registered only the Upstash driver (`UnregisteredCacheScheme`).\n\t\t...(workspace.deploy ? { deploy: workspace.deploy } : {}),\n\t\tstages: workspace.stages,\n\t\t// One glob, every kind, and the root's covers every app's code as well\n\t\t// as the constructs. Which surface an endpoint belongs to is the\n\t\t// endpoint's to say, not its file's, so the build sorts them there.\n\t\tconstructs: appConstructGlobs(workspace, appName),\n\t\thooks: app.hooks,\n\t\ttelescope: app.telescope,\n\t\topenapi: app.openapi,\n\t\truntime: app.runtime,\n\t\tenv: app.env,\n\t};\n}\n\n/**\n * Get topologically sorted app names based on dependencies.\n * Apps with no dependencies come first, then apps that depend on them.\n */\nexport function getAppBuildOrder(workspace: NormalizedWorkspace): string[] {\n\tconst appNames = Object.keys(workspace.apps);\n\tconst visited = new Set<string>();\n\tconst result: string[] = [];\n\n\tfunction visit(name: string) {\n\t\tif (visited.has(name)) return;\n\t\tvisited.add(name);\n\n\t\tconst app = workspace.apps[name];\n\t\tif (app) {\n\t\t\tfor (const dep of app.dependencies) {\n\t\t\t\tvisit(dep);\n\t\t\t}\n\t\t}\n\n\t\tresult.push(name);\n\t}\n\n\tfor (const name of appNames) {\n\t\tvisit(name);\n\t}\n\n\treturn result;\n}\n\n/**\n * Resolve the Dokploy endpoint for a specific stage.\n *\n * Uses per-stage endpoint from `endpoints` if available,\n * otherwise falls back to the global `endpoint`.\n *\n * @param dokployConfig - Dokploy workspace configuration\n * @param stage - Deployment stage (e.g., 'development', 'production')\n * @returns The endpoint URL for the stage, or undefined if not configured\n *\n * @example\n * ```ts\n * // With per-stage endpoints\n * const config = {\n * endpoints: {\n * development: 'https://dev.dokploy.example.com:3000',\n * production: 'https://prod.dokploy.example.com:3000',\n * },\n * };\n * getEndpointForStage(config, 'production'); // => 'https://prod.dokploy.example.com:3000'\n *\n * // With single endpoint\n * const config = { endpoint: 'https://dokploy.example.com:3000' };\n * getEndpointForStage(config, 'production'); // => 'https://dokploy.example.com:3000'\n * ```\n */\nexport function getEndpointForStage(\n\tdokployConfig:\n\t\t| { endpoint?: string; endpoints?: Record<string, string> }\n\t\t| undefined,\n\tstage: string,\n): string | undefined {\n\tif (!dokployConfig) return undefined;\n\n\t// Check per-stage endpoint first\n\tif (dokployConfig.endpoints?.[stage]) {\n\t\treturn dokployConfig.endpoints[stage];\n\t}\n\n\t// Fall back to global endpoint\n\treturn dokployConfig.endpoint;\n}\n"],"mappings":"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"}
|