@geekmidas/cli 10.0.0-alpha.5 → 10.0.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/{config-C21ocYNd.cjs → config-CNz5KaSR.cjs} +16 -45
  2. package/dist/config-CNz5KaSR.cjs.map +1 -0
  3. package/dist/{config-WRdsQYZK.mjs → config-CxP7P-qG.mjs} +17 -46
  4. package/dist/config-CxP7P-qG.mjs.map +1 -0
  5. package/dist/config.cjs +2 -2
  6. package/dist/config.d.cts +2 -2
  7. package/dist/config.d.mts +2 -2
  8. package/dist/config.mjs +2 -2
  9. package/dist/{credentials-BBIRJVp4.mjs → credentials-BK3Z93MX.mjs} +5 -5
  10. package/dist/credentials-BK3Z93MX.mjs.map +1 -0
  11. package/dist/{credentials-G1JwBoXH.cjs → credentials-DzsXZzkx.cjs} +5 -5
  12. package/dist/credentials-DzsXZzkx.cjs.map +1 -0
  13. package/dist/discover-CAlFgYec.mjs.map +1 -1
  14. package/dist/discover-DTmFeloL.cjs.map +1 -1
  15. package/dist/{index-BThUzEIp.d.mts → index-BGGdjueF.d.cts} +2 -2
  16. package/dist/index-BGGdjueF.d.cts.map +1 -0
  17. package/dist/{index-CHB4tSzX.d.cts → index-DEbaleny.d.mts} +2 -2
  18. package/dist/index-DEbaleny.d.mts.map +1 -0
  19. package/dist/index.cjs +307 -121
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.mjs +307 -121
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/{openapi-kkQbsRVj.mjs → openapi-Bw8DWFWz.mjs} +104 -50
  24. package/dist/openapi-Bw8DWFWz.mjs.map +1 -0
  25. package/dist/{openapi-CA0yJUTN.cjs → openapi-D-dx9Akj.cjs} +122 -50
  26. package/dist/openapi-D-dx9Akj.cjs.map +1 -0
  27. package/dist/openapi.cjs +5 -3
  28. package/dist/openapi.d.cts +46 -10
  29. package/dist/openapi.d.cts.map +1 -1
  30. package/dist/openapi.d.mts +46 -10
  31. package/dist/openapi.d.mts.map +1 -1
  32. package/dist/openapi.mjs +4 -4
  33. package/dist/reconcile/public.d.cts +1 -1
  34. package/dist/reconcile/public.d.mts +1 -1
  35. package/dist/setup-8IWPsRIN.cjs +11 -0
  36. package/dist/{setup-DXvRLTZN.mjs → setup-BWMhbW-p.mjs} +3 -3
  37. package/dist/{setup-DXvRLTZN.mjs.map → setup-BWMhbW-p.mjs.map} +1 -1
  38. package/dist/{setup-Cm2UnAdh.cjs → setup-D7L4BgBT.cjs} +3 -3
  39. package/dist/{setup-Cm2UnAdh.cjs.map → setup-D7L4BgBT.cjs.map} +1 -1
  40. package/dist/{setup-ZijS4qeC.mjs → setup-DGIPd2fh.mjs} +4 -4
  41. package/dist/{types-C-arnQ1r.d.mts → types-BLSNdrk0.d.cts} +5 -13
  42. package/dist/{types-C-arnQ1r.d.mts.map → types-BLSNdrk0.d.cts.map} +1 -1
  43. package/dist/{types-abJQuk7m.d.cts → types-C3POXhw3.d.mts} +2 -10
  44. package/dist/{types-abJQuk7m.d.cts.map → types-C3POXhw3.d.mts.map} +1 -1
  45. package/dist/workspace/index.cjs +1 -1
  46. package/dist/workspace/index.d.cts +2 -2
  47. package/dist/workspace/index.d.mts +2 -2
  48. package/dist/workspace/index.mjs +1 -1
  49. package/dist/{workspace-DdJss8I6.cjs → workspace-CGaUAw5A.cjs} +3 -3
  50. package/dist/workspace-CnTqIlB3.mjs +8 -0
  51. package/dist/{workspace-BFMrAKqI.mjs → workspace-DIuZx-cc.mjs} +1 -17
  52. package/dist/{workspace-BFMrAKqI.mjs.map → workspace-DIuZx-cc.mjs.map} +1 -1
  53. package/dist/{workspace-BcucjwvA.cjs → workspace-LKiNyaDy.cjs} +1 -17
  54. package/dist/{workspace-BcucjwvA.cjs.map → workspace-LKiNyaDy.cjs.map} +1 -1
  55. package/package.json +11 -11
  56. package/dist/config-C21ocYNd.cjs.map +0 -1
  57. package/dist/config-WRdsQYZK.mjs.map +0 -1
  58. package/dist/credentials-BBIRJVp4.mjs.map +0 -1
  59. package/dist/credentials-G1JwBoXH.cjs.map +0 -1
  60. package/dist/index-BThUzEIp.d.mts.map +0 -1
  61. package/dist/index-CHB4tSzX.d.cts.map +0 -1
  62. package/dist/openapi-CA0yJUTN.cjs.map +0 -1
  63. package/dist/openapi-kkQbsRVj.mjs.map +0 -1
  64. package/dist/setup-B5_oHHUj.cjs +0 -11
  65. package/dist/workspace-k4l3rsFh.mjs +0 -8
@@ -1 +1 @@
1
- {"version":3,"file":"discover-CAlFgYec.mjs","names":["routes: Routes | undefined","DEFAULT_CACHE: Record<MainProvider, CacheBackend>","DEFAULT_STORAGE: Record<MainProvider, StorageBackend>","DEFAULT_EMAIL: EmailBackend","context: BuildContext","outputDir: string","generator: ConstructGenerator<T, R>","patterns?: Routes","options?: GeneratorOptions","logger","globPatterns: string[]","partitionFn: ((filepath: string) => string) | undefined","constructs: GeneratedConstruct<T>[]","value: unknown","options: DiscoverOptions","manifest: Record<string, Declaration>","sources: Record<string, string>","globs: readonly string[]","matched: number","declared: number","id: string","sources: readonly string[]"],"sources":["../src/types.ts","../src/generators/Generator.ts","../src/reconcile/discover.ts"],"sourcesContent":["import type { ServicesConfig } from './workspace/types.js';\n\nexport type MainProvider = 'aws' | 'server';\nexport type LegacyProvider =\n\t| 'server'\n\t| 'aws-apigatewayv1'\n\t| 'aws-apigatewayv2'\n\t| 'aws-lambda';\n\nexport interface PartitionedRoutes {\n\tpaths: string | string[];\n\tpartition: (filepath: string) => string;\n}\n\nexport type Routes = string | string[] | PartitionedRoutes;\n\nexport function isPartitionedRoutes(\n\troutes: Routes | undefined,\n): routes is PartitionedRoutes {\n\treturn (\n\t\ttypeof routes === 'object' &&\n\t\troutes !== null &&\n\t\t!Array.isArray(routes) &&\n\t\t'paths' in routes\n\t);\n}\n\nexport interface ProviderConfig {\n\tenabled?: boolean;\n\toutputDir?: string;\n}\n\nexport interface AWSApiGatewayConfig extends ProviderConfig {\n\t// Additional AWS API Gateway specific options\n}\n\nexport interface AWSLambdaConfig extends ProviderConfig {\n\t// Additional AWS Lambda specific options\n}\n\nexport interface ProductionConfig {\n\t/** Enable production mode (default: false) */\n\tenabled?: boolean;\n\t/** Bundle server into single file (default: true) */\n\tbundle?: boolean;\n\t/** Minify bundled output (default: true) */\n\tminify?: boolean;\n\t/** Health check endpoint path (default: '/health') */\n\thealthCheck?: string;\n\t/** Enable graceful shutdown handling (default: true) */\n\tgracefulShutdown?: boolean;\n\t/** Packages to exclude from bundling (default: []) */\n\texternal?: string[];\n\t/** Include subscribers in production build (default: 'exclude' for serverless) */\n\tsubscribers?: 'include' | 'exclude';\n\t/** Include OpenAPI spec in production (default: false) */\n\topenapi?: boolean;\n\t/**\n\t * Enable build-time optimized handler generation (default: true)\n\t * Generates specialized handlers based on endpoint tier:\n\t * - minimal: Near-raw-Hono performance for simple endpoints\n\t * - standard: Optimized handlers for auth/services\n\t * - full: Uses HonoEndpoint.addRoutes for complex endpoints\n\t */\n\toptimizedHandlers?: boolean;\n}\n\n/** Service-specific configuration for docker-compose */\nexport interface ServiceConfig {\n\t/**\n\t * Full Docker image reference (e.g., 'postgis/postgis:16-3.4-alpine').\n\t * When specified, overrides the default image entirely.\n\t */\n\timage?: string;\n\t/**\n\t * Docker image version/tag (e.g., '15-alpine' for postgres).\n\t * Only used when `image` is not specified.\n\t */\n\tversion?: string;\n}\n\n/**\n * Supported event backend types.\n *\n * - `pgboss`: Reuses PostgreSQL with a dedicated user/schema. Auto-enables db service.\n * - `sns`: Adds LocalStack container with SNS+SQS services.\n * - `rabbitmq`: Adds RabbitMQ container.\n *\n * All backends generate EVENT_PUBLISHER_CONNECTION_STRING and EVENT_SUBSCRIBER_CONNECTION_STRING.\n */\nexport type EventsBackend = 'pgboss' | 'sns' | 'rabbitmq';\n\n/**\n * Where a declared cache actually lives.\n *\n * A deployment choice, not an application one — the same code caches into any\n * of these — so it is config here beside `events` rather than a field on the\n * construct. Each backend is consistent between local and deployed, which is\n * the property worth having: a cache that behaves differently in the two places\n * is worse than a slower one.\n *\n * - `upstash` (default) — HTTP with a token. Reachable from a Lambda with no\n * VPC and no connection pool, which is why it is the default; locally the\n * `serverless-redis-http` proxy in front of Redis speaks the same protocol.\n * - `elasticache` — the Redis wire protocol, inside a VPC. Locally, plain Redis.\n * - `db` — a table in the database the app already declared. No infrastructure\n * at all, and the same relationship pg-boss has to Postgres: it creates its\n * own table on first use and needs a declared database to live in.\n */\nexport type CacheBackend = 'upstash' | 'elasticache' | 'db';\n\n/**\n * Where a declared bucket actually lives.\n *\n * The fourth of these selectors and the last to arrive, which is why\n * `UPLOADS_URL` had no answer on a container target: there was nothing to\n * choose with. Like the others it changes no application code — the `s3://`\n * scheme picks the driver, and every one of these speaks it.\n *\n * - `s3` — a real bucket. Nothing is created on the host; the URL is composed\n * against credentials you already hold, the same way `resend` is an API key\n * rather than infrastructure.\n * - `r2` — Cloudflare's, reached through the same S3 API and differing only in\n * its endpoint.\n * - `minio` — a container. Everything the app declares lives on the box, which\n * is what makes a single-host deploy self-contained, and what makes it a\n * single disk with no replication. Locally this is what every backend is.\n */\nexport type StorageBackend = 'minio' | 's3' | 'r2';\n\n/**\n * The backend a project gets when it says nothing, by where it deploys.\n *\n * Flat defaults could not be right for both: `upstash` on a single box means a\n * SaaS round-trip for a cache the database could have held, and `minio` on AWS\n * means running a bucket beside S3. So the default is a function of the target,\n * and an app that says nothing gets the obvious answer in both places — which\n * is the whole of what \"zero config\" can mean once there is more than one\n * target.\n *\n * `server` covers Dokploy and a bare host alike: the question these answer is\n * whether the app is next to a managed cloud or next to its own containers, and\n * on that question the two are the same.\n *\n * A per-construct, per-stage override is the layer above this — see\n * `constructs-outstanding.md` — so two buckets in one app can differ. This is\n * only what happens when nobody has said anything at all.\n */\nexport const DEFAULT_CACHE: Record<MainProvider, CacheBackend> = {\n\t// Reachable from a Lambda with no VPC and no connection pool.\n\taws: 'upstash',\n\t// The database is already there and already has a connection pool open to\n\t// it. Anything else is a second service to run for a table.\n\tserver: 'db',\n};\n\n/** Where a bucket lives when nobody said — see {@link DEFAULT_CACHE}. */\nexport const DEFAULT_STORAGE: Record<MainProvider, StorageBackend> = {\n\taws: 's3',\n\tserver: 'minio',\n};\n\n/**\n * Who delivers a declared app's mail.\n *\n * Unlike a cache, this changes *nothing* about the client. Every backend speaks\n * SMTP, so the declaration's `smtp://` URL is true of all of them and only the\n * provisioner differs — which is the same reason the declaration has no\n * `provider` field and there is no `ses://` scheme.\n *\n * - `ses` (default) — the only backend that can be a *chain* of resources: an\n * identity, a user, an access key, and an SMTP password derived from it,\n * because SES issues IAM credentials rather than SMTP ones. Hand it a URL and\n * it uses that instead, which is what you want when the credentials already\n * exist — provisioning a second set of sending credentials for an identity\n * that already has them is not a thing to do by default.\n * - `resend` — an API key you hold, composed into an SMTP URL. Nothing to\n * provision: a SaaS account is not infrastructure.\n * - `smtp` — a URL supplied whole, for a relay somebody else runs.\n *\n * Locally every one of them is Mailpit, because locally none of it matters.\n */\nexport type EmailBackend = 'resend' | 'ses' | 'smtp';\n\n/**\n * Who delivers mail when nobody said.\n *\n * Flat, unlike the others, and deliberately: mail is a SaaS on every target.\n * There is no self-hosted preset to default a container deploy to — Mailpit is\n * a development inbox, not a sender — so `server` has no answer of its own to\n * give and would only be `ses` written twice.\n */\nexport const DEFAULT_EMAIL: EmailBackend = 'ses';\n\n/** Supported docker-compose service names */\nexport type ComposeServiceName =\n\t| 'postgres'\n\t| 'redis'\n\t| 'rabbitmq'\n\t| 'minio'\n\t| 'mailpit'\n\t| 'localstack';\n\n/** Services configuration - can be boolean (use defaults) or object with version */\nexport type ComposeServicesConfig = {\n\t[K in ComposeServiceName]?: boolean | ServiceConfig;\n};\n\nexport interface DockerConfig {\n\t/** Container registry URL (e.g., 'ghcr.io/myorg') */\n\tregistry?: string;\n\t/** Docker image name (default: derived from package.json name) */\n\timageName?: string;\n\t/** Base Docker image (default: 'node:22-alpine') */\n\tbaseImage?: string;\n\t/** Container port (default: 3000) */\n\tport?: number;\n\t/** docker-compose services to include */\n\tcompose?: {\n\t\t/**\n\t\t * Services to include in docker-compose.\n\t\t * Can be an object with service configs or an array of service names (legacy).\n\t\t *\n\t\t * @example Object format (recommended)\n\t\t * services: {\n\t\t * postgres: { version: '15-alpine' },\n\t\t * redis: true, // use default version\n\t\t * }\n\t\t *\n\t\t * @example Array format (legacy, uses default versions)\n\t\t * services: ['postgres', 'redis']\n\t\t */\n\t\tservices?: ComposeServicesConfig | ComposeServiceName[];\n\t};\n}\n\nexport interface ServerConfig extends ProviderConfig {\n\tenableOpenApi?: boolean;\n\tport?: number;\n\t/** Production build configuration */\n\tproduction?: ProductionConfig;\n}\n\nexport type Runtime = 'node' | 'bun';\n\nexport interface TelescopeConfig {\n\t/** Enable/disable telescope (default: true in development) */\n\tenabled?: boolean;\n\t/** Port for telescope to use (defaults to server port) */\n\tport?: number;\n\t/** Path prefix for telescope UI (default: /__telescope) */\n\tpath?: string;\n\t/** Ignore patterns for telescope (e.g., ['/health', '/metrics']) */\n\tignore?: string[];\n\t/** Record request/response bodies (default: true) */\n\trecordBody?: boolean;\n\t/** Maximum entries to keep in memory (default: 1000) */\n\tmaxEntries?: number;\n\t/** Enable WebSocket for real-time updates (default: true, requires @hono/node-ws for Node.js) */\n\twebsocket?: boolean;\n}\n\nexport interface StudioConfig {\n\t/** Enable/disable studio (default: true in development) */\n\tenabled?: boolean;\n\t/** Path prefix for studio UI (default: /__studio) */\n\tpath?: string;\n\t/** Schema to introspect (default: 'public') */\n\tschema?: string;\n}\n\nexport interface OpenApiConfig {\n\t/** Enable OpenAPI generation (default: true) */\n\tenabled?: boolean;\n\t/** API title */\n\ttitle?: string;\n\t/** API version */\n\tversion?: string;\n\t/** API description */\n\tdescription?: string;\n}\n\nexport interface HooksConfig {\n\t/**\n\t * Path to a module exporting server lifecycle hooks.\n\t * The module should export `beforeSetup` and/or `afterSetup` functions.\n\t *\n\t * @example\n\t * ```typescript\n\t * // src/config/hooks.ts\n\t * import type { Hono } from 'hono';\n\t * import type { Logger } from '@geekmidas/logger';\n\t * import type { EnvironmentParser } from '@geekmidas/envkit';\n\t *\n\t * // Called BEFORE gkm endpoints are registered\n\t * export function beforeSetup(app: Hono, ctx: { envParser: EnvironmentParser; logger: Logger }) {\n\t * app.use('*', cors());\n\t * app.get('/custom/health', (c) => c.json({ status: 'ok' }));\n\t * }\n\t *\n\t * // Called AFTER gkm endpoints are registered\n\t * export function afterSetup(app: Hono, ctx: { envParser: EnvironmentParser; logger: Logger }) {\n\t * app.notFound((c) => c.json({ error: 'Not found' }, 404));\n\t * app.onError((err, c) => c.json({ error: err.message }, 500));\n\t * }\n\t * ```\n\t */\n\tserver?: string;\n}\n\n/** Dokploy deployment configuration */\nexport interface DokployProviderConfig {\n\t/** Dokploy API endpoint (e.g., 'https://dokploy.example.com') */\n\tendpoint: string;\n\t/** Project ID in Dokploy */\n\tprojectId: string;\n\t/** Application ID in Dokploy */\n\tapplicationId: string;\n\t/** Container registry (overrides docker.registry if set) */\n\tregistry?: string;\n\t/** Registry ID in Dokploy (recommended for private registries) */\n\tregistryId?: string;\n}\n\nexport interface ProvidersConfig {\n\taws?: {\n\t\tapiGateway?: {\n\t\t\tv1?: boolean | AWSApiGatewayConfig;\n\t\t\tv2?: boolean | AWSApiGatewayConfig;\n\t\t};\n\t\tlambda?: {\n\t\t\tfunctions?: boolean | AWSLambdaConfig;\n\t\t\tcrons?: boolean | AWSLambdaConfig;\n\t\t};\n\t};\n\tserver?: boolean | ServerConfig;\n\t/** Dokploy deployment configuration */\n\tdokploy?: boolean | DokployProviderConfig;\n}\n\nexport interface GkmConfig {\n\t/**\n\t * What this app is called, and therefore how everything it declares is named.\n\t *\n\t * The same field `sst.config.ts` carries, doing the same job: every physical\n\t * name is `{stage}-{name}-{construct}`, so `Database` in the `production`\n\t * stage of `kitchen-sink` is `production-kitchen-sink-database` on AWS and on\n\t * Dokploy alike. One statement, in the config, rather than a rule each\n\t * provider restates.\n\t *\n\t * `defineWorkspace` has always had this. A single-app config did not, so its\n\t * names fell back to whatever the directory happened to be called — the\n\t * monorepo's package name for the project and the app's for the application,\n\t * two accidents of layout standing in for a decision.\n\t *\n\t * Defaults to the package.json name with any scope stripped, then the\n\t * directory name.\n\t */\n\tname?: string;\n\t/**\n\t * Where the things no construct implies live.\n\t *\n\t * The same block a workspace config carries, and the same split: `db` and\n\t * `storage` are derived from the declared `KyselyDatabase` and\n\t * `ObjectStorage` and are ignored here, while `cache`, `mail`, and `events`\n\t * name a *backend* — where the cache lives, who delivers the mail, which\n\t * broker carries events. A single-app project had no way to say any of that\n\t * before, so its event backend could only be guessed.\n\t */\n\tservices?: ServicesConfig;\n\t/**\n\t * Constructs glob pattern — one glob, every kind.\n\t *\n\t * What reconcile reads to derive the containers a stage needs: a database\n\t * implies Postgres, objects imply MinIO, mail implies Mailpit. A glob per\n\t * kind cannot find a resource, because a declared `ObjectStorage` has no\n\t * kind to be listed under.\n\t *\n\t * @example './src/**\\/*.ts'\n\t */\n\tconstructs?: Routes;\n\troutes: Routes;\n\tfunctions?: Routes;\n\tcrons?: Routes;\n\tsubscribers?: Routes;\n\tqueues?: Routes;\n\ttopics?: Routes;\n\tenvParser: string;\n\tlogger: string;\n\tproviders?: ProvidersConfig;\n\t/**\n\t * Server lifecycle hooks for customizing the Hono app.\n\t * Allows adding custom routes, middleware, error handlers, etc.\n\t *\n\t * @example\n\t * hooks: {\n\t * server: './src/config/hooks'\n\t * }\n\t */\n\thooks?: HooksConfig;\n\t/**\n\t * Telescope configuration for debugging/monitoring.\n\t * Can be:\n\t * - A string path to a module that exports a Telescope instance (recommended)\n\t * - A boolean to enable/disable with defaults\n\t * - A TelescopeConfig object for inline configuration\n\t */\n\ttelescope?: string | boolean | TelescopeConfig;\n\t/**\n\t * Studio configuration for database browsing.\n\t * Can be:\n\t * - A string path to a module that exports a Studio instance (recommended)\n\t * - A boolean to enable/disable with defaults\n\t * - A StudioConfig object for inline configuration\n\t *\n\t * Requires a database connection configured via services.\n\t */\n\tstudio?: string | boolean | StudioConfig;\n\t/**\n\t * OpenAPI generation configuration.\n\t * Can be:\n\t * - A boolean to enable/disable with defaults (output: ./src/api/openapi.ts)\n\t * - An OpenApiConfig object for customization\n\t *\n\t * When enabled, OpenAPI spec is generated on startup and regenerated on route changes.\n\t *\n\t * @example\n\t * openapi: true\n\t *\n\t * @example\n\t * openapi: {\n\t * output: './src/api/openapi.ts',\n\t * title: 'My API',\n\t * version: '1.0.0',\n\t * }\n\t */\n\topenapi?: boolean | OpenApiConfig;\n\t/** Runtime to use for dev server (default: 'node') */\n\truntime?: Runtime;\n\t/**\n\t * Environment file(s) to load for development.\n\t * Can be:\n\t * - A string path to a single env file (e.g., '.env.local')\n\t * - An array of paths to load in order (later files override earlier)\n\t * - Defaults to '.env' if not specified\n\t *\n\t * @example\n\t * env: '.env.local'\n\t *\n\t * @example\n\t * env: ['.env', '.env.local']\n\t */\n\tenv?: string | string[];\n\t/**\n\t * Docker deployment configuration.\n\t * Used by `gkm docker` and `gkm prepack` commands.\n\t *\n\t * @example\n\t * docker: {\n\t * registry: 'ghcr.io/myorg',\n\t * imageName: 'my-api',\n\t * compose: {\n\t * services: ['postgres', 'redis']\n\t * }\n\t * }\n\t */\n\tdocker?: DockerConfig;\n\t/**\n\t * Where this app deploys, and what the target needs to know.\n\t *\n\t * Absent until now, which meant a single-app project could not configure a\n\t * Dokploy deploy at all: the wrap that turns one into a workspace hardcoded\n\t * `{ default: 'dokploy' }` and dropped everything else, so there was no\n\t * endpoint, no registry and no domain — and `resolveHost` failed with \"no\n\t * domain configured for stage\" on a config that had nowhere to put one.\n\t *\n\t * The same shape a workspace config uses, so moving from one to the other is\n\t * a rename rather than a rewrite.\n\t */\n\tdeploy?: import('./workspace/types').DeployConfig;\n}\n\nexport interface BuildOptions {\n\tprovider?: MainProvider;\n\t// Legacy support - will be deprecated\n\tproviders?: LegacyProvider[];\n\tenableOpenApi?: boolean;\n\t/** Build for production (no dev tools, bundled output) */\n\tproduction?: boolean;\n\t/** Skip bundling step in production build */\n\tskipBundle?: boolean;\n\t/** Stage for secrets injection (e.g., 'production', 'staging') */\n\tstage?: string;\n\t/**\n\t * When true, optional environment variables (those with `.optional()` or\n\t * `.default()` in the envParser) are suffixed with `?` within each\n\t * construct's `environment` array in the manifest (e.g. `PORT?`).\n\t * Defaults to false.\n\t */\n\tmarkOptional?: boolean;\n}\n\n/** Result from build command when secrets are injected */\nexport interface BuildResult {\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\n// The deployment manifest types are a shared, dependency-free data contract\n// in `@geekmidas/manifest`, re-exported here for back-compat with existing\n// `@geekmidas/cli` imports.\nexport type {\n\tCronInfo,\n\tFunctionInfo,\n\tManifest,\n\tQueueInfo,\n\tRouteInfo,\n\tSubscriberInfo,\n\tTopicInfo,\n} from '@geekmidas/manifest';\n","import { relative } from 'node:path';\nimport type { Construct } from '@geekmidas/constructs';\nimport fg from 'fast-glob';\nimport kebabCase from 'lodash.kebabcase';\nimport type { BuildContext } from '../build/types';\nimport {\n\tisPartitionedRoutes,\n\ttype LegacyProvider,\n\ttype Routes,\n} from '../types';\n\n/**\n * Zod v4 maintains a process-wide registry of schemas registered via\n * `.meta({ id })`. When we cache-bust user module imports (by appending a\n * `?t=timestamp` query), those modules re-execute and try to register the\n * same ids again, which throws `ID X already exists in the registry`.\n *\n * Clearing the registry in the CLI process is safe: the user's running\n * server lives in a subprocess and has its own isolated registry.\n *\n * Exported for tests.\n */\nexport function clearZodGlobalRegistry(): void {\n\tconst registry = (\n\t\tglobalThis as { __zod_globalRegistry?: { clear?: () => void } }\n\t).__zod_globalRegistry;\n\tif (registry && typeof registry.clear === 'function') {\n\t\tregistry.clear();\n\t}\n}\n\nexport interface GeneratorOptions {\n\tprovider?: LegacyProvider;\n\t[key: string]: any;\n}\n\nexport abstract class ConstructGenerator<T extends Construct, R = void> {\n\tabstract isConstruct(value: any): value is T;\n\n\tstatic async build<T extends Construct, R = void>(\n\t\tcontext: BuildContext,\n\t\toutputDir: string,\n\t\tgenerator: ConstructGenerator<T, R>,\n\t\tpatterns?: Routes,\n\t\toptions?: GeneratorOptions,\n\t): Promise<R> {\n\t\tconst constructs = await generator.load(patterns);\n\t\treturn generator.build(context, constructs, outputDir, options);\n\t}\n\n\tabstract build(\n\t\tcontext: BuildContext,\n\t\tconstructs: GeneratedConstruct<T>[],\n\t\toutputDir: string,\n\t\toptions?: GeneratorOptions,\n\t): Promise<R>;\n\n\tasync load(\n\t\tpatterns?: Routes,\n\t\tcwd = process.cwd(),\n\t\tbustCache = false,\n\t): Promise<GeneratedConstruct<T>[]> {\n\t\tconst logger = console;\n\n\t\t// Extract glob patterns and optional partition function\n\t\tlet globPatterns: string[];\n\t\tlet partitionFn: ((filepath: string) => string) | undefined;\n\n\t\tif (isPartitionedRoutes(patterns)) {\n\t\t\tglobPatterns = Array.isArray(patterns.paths)\n\t\t\t\t? patterns.paths\n\t\t\t\t: [patterns.paths];\n\t\t\tpartitionFn = patterns.partition;\n\t\t} else {\n\t\t\tglobPatterns = Array.isArray(patterns)\n\t\t\t\t? patterns\n\t\t\t\t: patterns\n\t\t\t\t\t? [patterns]\n\t\t\t\t\t: [];\n\t\t}\n\n\t\t// Find all files\n\t\tconst files = fg.stream(globPatterns, {\n\t\t\tcwd,\n\t\t\tabsolute: true,\n\t\t});\n\n\t\t// Cache-busted re-imports re-execute user modules, which would try to\n\t\t// re-register Zod schemas with the same `.meta({ id })`. Clear the\n\t\t// global registry once per load to prevent duplicate-id errors.\n\t\tif (bustCache) {\n\t\t\tclearZodGlobalRegistry();\n\t\t}\n\n\t\t// Load constructs\n\t\tconst constructs: GeneratedConstruct<T>[] = [];\n\n\t\tfor await (const f of files) {\n\t\t\ttry {\n\t\t\t\tconst file = f.toString();\n\t\t\t\t// Append cache-busting query param to force re-import of changed modules\n\t\t\t\tconst importPath = bustCache ? `${file}?t=${Date.now()}` : file;\n\t\t\t\tconst module = await import(importPath);\n\n\t\t\t\t// Compute partition name for this file (if partition function provided)\n\t\t\t\tconst partition = partitionFn ? partitionFn(file) : undefined;\n\n\t\t\t\t// Check all exports for constructs\n\t\t\t\tfor (const [key, construct] of Object.entries(module)) {\n\t\t\t\t\tif (this.isConstruct(construct)) {\n\t\t\t\t\t\tconstructs.push({\n\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\tname: kebabCase(key),\n\t\t\t\t\t\t\tconstruct,\n\t\t\t\t\t\t\tpath: {\n\t\t\t\t\t\t\t\tabsolute: file,\n\t\t\t\t\t\t\t\trelative: relative(process.cwd(), file),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tpartition,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error as Error;\n\t\t\t\tlogger.error(`Failed to load ${f}: ${err.message}`);\n\t\t\t\tif (err.stack) {\n\t\t\t\t\tlogger.error(err.stack);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\treturn constructs;\n\t}\n}\n\nexport interface GeneratedConstruct<T extends Construct> {\n\tkey: string;\n\tname: string;\n\tconstruct: T;\n\tpath: {\n\t\tabsolute: string;\n\t\trelative: string;\n\t};\n\t/** Partition name assigned by the partition function, if configured. */\n\tpartition?: string;\n}\n","/**\n * Discovery — one glob, one structural test.\n *\n * A glob per kind is the same specialness the construct model removes, and\n * resources have no kind to be listed under: a declared `ObjectStorage` would\n * never be found by `routes:` or `crons:`. So there is one `constructs` glob,\n * every export of every matching module is inspected, and the per-kind\n * `isConstruct` checks collapse into a single question — does it have an id and\n * can it declare?\n *\n * Structural rather than `instanceof` on purpose. A construct from a different\n * copy of `@geekmidas/constructs` — a linked workspace, two versions in a\n * lockfile — is still a construct, and `instanceof` is exactly the check that\n * says otherwise.\n */\n\nimport { relative } from 'node:path';\nimport {\n\tassertDerivations,\n\ttype ConstructManifest,\n\tcanonicalId,\n\ttype Declaration,\n} from '@geekmidas/manifest';\nimport fg from 'fast-glob';\nimport { clearZodGlobalRegistry } from '../generators/Generator';\n\n/** Matches the rest of the CLI, which logs through `console` directly. */\nconst logger = console;\n\n/** The construct face discovery needs: an id, and the ability to declare. */\ninterface Declarable {\n\tid: string;\n\tdeclare(): Declaration[];\n}\n\n/**\n * Whether a module export is a construct.\n *\n * Deliberately loose about what else it carries — a construct that also has a\n * `service` is still a construct, and requiring one would exclude the kinds that\n * own no address.\n */\nexport function isDeclarable(value: unknown): value is Declarable {\n\tif (typeof value !== 'object' || value === null) return false;\n\n\tconst candidate = value as Partial<Declarable>;\n\n\treturn (\n\t\ttypeof candidate.id === 'string' &&\n\t\tcandidate.id.length > 0 &&\n\t\ttypeof candidate.declare === 'function'\n\t);\n}\n\nexport interface DiscoverOptions {\n\t/** The glob(s) to search. One pattern, not one per kind. */\n\tpatterns: string | readonly string[];\n\tcwd?: string;\n\t/**\n\t * Re-import changed modules, for the dev watcher.\n\t *\n\t * A single glob makes discovery more central, not less: a new construct file\n\t * has to be picked up on `add`, not only on `change`.\n\t */\n\tbustCache?: boolean;\n\t/**\n\t * Filled in with where each construct was found, when provided.\n\t *\n\t * Discovery is the only thing that knows: it globbed the files and imported\n\t * them. Everything downstream that needs to *write* an import — the build,\n\t * generating a server entry that reads a surface's logger — would otherwise\n\t * have to be told the path in config, which is the string this removes.\n\t *\n\t * An out-parameter rather than a second return value so that every existing\n\t * caller, which wants the manifest and nothing else, is unchanged.\n\t */\n\tsources?: Record<string, ConstructSource>;\n}\n\n/** Where a construct was declared, and under what name. */\nexport interface ConstructSource {\n\t/** Absolute path to the module that exported it. */\n\tfile: string;\n\t/** The name it was exported as — what an import statement has to say. */\n\texportName: string;\n}\n\n/**\n * Build the construct manifest for a project.\n *\n * Runs the manifest's own validation before returning, so an app is wrong here\n * — where the error names a file — rather than at deploy.\n *\n * @throws {DuplicateConstruct} when two exports claim one id.\n */\nexport async function discover(\n\toptions: DiscoverOptions,\n): Promise<ConstructManifest> {\n\tconst {\n\t\tpatterns,\n\t\tcwd = process.cwd(),\n\t\tbustCache = false,\n\t\tsources: out,\n\t} = options;\n\n\t// Re-importing user modules re-executes them, and Zod v4 throws on a\n\t// re-registered `.meta({ id })`. Same reason the generators clear it.\n\tif (bustCache) clearZodGlobalRegistry();\n\n\tconst manifest: Record<string, Declaration> = {};\n\t/** Which file declared each id, for the error when two of them do. */\n\tconst sources: Record<string, string> = {};\n\t/**\n\t * Constructs already seen, by identity.\n\t *\n\t * A re-export is the *same object* reached through a second file — ESM\n\t * bindings are live, so `export * from './database.js'` hands back the\n\t * binding rather than a copy. Without this, a barrel file made every\n\t * construct in it appear declared twice, and `constructs/index.ts` is the\n\t * first thing anyone writes in a shared folder.\n\t *\n\t * Identity rather than file is also the more honest rule: what may not\n\t * happen twice is two *different* constructs claiming one id, and that is\n\t * still an error below.\n\t */\n\tconst seen = new WeakSet<object>();\n\n\tconst globs = Array.isArray(patterns) ? [...patterns] : [patterns as string];\n\tconst files = fg.stream(globs, { cwd, absolute: true });\n\n\t/** Matched files, for the diagnostic below. */\n\tlet matched = 0;\n\n\tfor await (const found of files) {\n\t\tmatched++;\n\t\tconst file = found.toString();\n\t\tconst module = await import(bustCache ? `${file}?t=${Date.now()}` : file);\n\n\t\tfor (const [exportName, exported] of Object.entries(module)) {\n\t\t\tif (!isDeclarable(exported)) continue;\n\n\t\t\t// The same construct, reached again through a re-export. Skipped\n\t\t\t// rather than re-declared: it has already claimed its id, from the\n\t\t\t// file that defined it.\n\t\t\tif (seen.has(exported as object)) continue;\n\t\t\tseen.add(exported as object);\n\n\t\t\tfor (const declaration of exported.declare()) {\n\t\t\t\t// Canonicalise here too: a construct built by hand rather than through\n\t\t\t\t// a constructor is still subject to the same identity rule.\n\t\t\t\tconst id = canonicalId(declaration.id);\n\t\t\t\tconst source = relative(cwd, file);\n\n\t\t\t\tconst claimed = sources[id];\n\t\t\t\tif (claimed !== undefined && claimed !== source) {\n\t\t\t\t\tthrow new DuplicateConstruct(id, [claimed, source]);\n\t\t\t\t}\n\n\t\t\t\tmanifest[id] = { ...declaration, id };\n\t\t\t\tsources[id] = source;\n\t\t\t\tif (out) out[id] = { file, exportName };\n\t\t\t}\n\t\t}\n\t}\n\n\t// Reference integrity before anyone reads it — a schema tenant naming a\n\t// database that was deleted is a manifest error, not a reconcile failure.\n\tassertDerivations(manifest);\n\n\twarnIfNothingFound(globs, matched, Object.keys(manifest).length);\n\n\treturn manifest;\n}\n\n/**\n * Say so when a glob was configured and found nothing.\n *\n * The glob is not a filter over declarations already known — it is *how they\n * are found*, by importing what it matches. So a file it misses is a\n * declaration that does not exist, and every symptom of that appears somewhere\n * other than the cause: no container starts, no env key is written, and the\n * application fails on first use against a resource it can see in its own\n * source.\n *\n * Nothing reported it before, because an unmatched file is indistinguishable\n * from a file nobody wrote. These two cases are the ones that are *not*\n * ambiguous — a glob was configured, so something was expected.\n *\n * Silent when no glob is configured at all: that is a project which has not\n * adopted constructs, and has nothing to be missing.\n */\nfunction warnIfNothingFound(\n\tglobs: readonly string[],\n\tmatched: number,\n\tdeclared: number,\n): void {\n\tif (globs.length === 0) return;\n\n\tif (matched === 0) {\n\t\tlogger.warn(\n\t\t\t`\\n⚠️ The constructs glob matched no files, so nothing was declared.\\n` +\n\t\t\t\t` Patterns: ${globs.join(', ')}\\n` +\n\t\t\t\t` Nothing will be provisioned — no containers, no env keys, no ` +\n\t\t\t\t`resources.\\n` +\n\t\t\t\t` A common cause is depth: '*.ts' matches one level, '**/*.ts' ` +\n\t\t\t\t`matches any.`,\n\t\t);\n\t\treturn;\n\t}\n\n\tif (declared === 0) {\n\t\tlogger.warn(\n\t\t\t`\\n⚠️ The constructs glob matched ${matched} file(s) but none declared ` +\n\t\t\t\t`anything.\\n` +\n\t\t\t\t` Patterns: ${globs.join(', ')}\\n` +\n\t\t\t\t` Discovery keeps exports with an 'id' that can 'declare()'. A ` +\n\t\t\t\t`construct that is\\n` +\n\t\t\t\t` built but never exported is invisible to it.`,\n\t\t);\n\t}\n}\n\n/** Two constructs claiming one id. */\nexport class DuplicateConstruct extends Error {\n\t/** The id claimed twice. */\n\treadonly id: string;\n\t/** The files that claimed it. */\n\treadonly sources: readonly string[];\n\n\tconstructor(id: string, sources: readonly string[]) {\n\t\tsuper('Two constructs declare the same id');\n\t\tthis.name = 'DuplicateConstruct';\n\t\tthis.id = id;\n\t\tthis.sources = sources;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"discover-CAlFgYec.mjs","names":["routes: Routes | undefined","DEFAULT_CACHE: Record<MainProvider, CacheBackend>","DEFAULT_STORAGE: Record<MainProvider, StorageBackend>","DEFAULT_EMAIL: EmailBackend","context: BuildContext","outputDir: string","generator: ConstructGenerator<T, R>","patterns?: Routes","options?: GeneratorOptions","logger","globPatterns: string[]","partitionFn: ((filepath: string) => string) | undefined","constructs: GeneratedConstruct<T>[]","value: unknown","options: DiscoverOptions","manifest: Record<string, Declaration>","sources: Record<string, string>","globs: readonly string[]","matched: number","declared: number","id: string","sources: readonly string[]"],"sources":["../src/types.ts","../src/generators/Generator.ts","../src/reconcile/discover.ts"],"sourcesContent":["import type { ServicesConfig } from './workspace/types.js';\n\nexport type MainProvider = 'aws' | 'server';\nexport type LegacyProvider =\n\t| 'server'\n\t| 'aws-apigatewayv1'\n\t| 'aws-apigatewayv2'\n\t| 'aws-lambda';\n\nexport interface PartitionedRoutes {\n\tpaths: string | string[];\n\tpartition: (filepath: string) => string;\n}\n\nexport type Routes = string | string[] | PartitionedRoutes;\n\nexport function isPartitionedRoutes(\n\troutes: Routes | undefined,\n): routes is PartitionedRoutes {\n\treturn (\n\t\ttypeof routes === 'object' &&\n\t\troutes !== null &&\n\t\t!Array.isArray(routes) &&\n\t\t'paths' in routes\n\t);\n}\n\nexport interface ProviderConfig {\n\tenabled?: boolean;\n\toutputDir?: string;\n}\n\nexport interface AWSApiGatewayConfig extends ProviderConfig {\n\t// Additional AWS API Gateway specific options\n}\n\nexport interface AWSLambdaConfig extends ProviderConfig {\n\t// Additional AWS Lambda specific options\n}\n\nexport interface ProductionConfig {\n\t/** Enable production mode (default: false) */\n\tenabled?: boolean;\n\t/** Bundle server into single file (default: true) */\n\tbundle?: boolean;\n\t/** Minify bundled output (default: true) */\n\tminify?: boolean;\n\t/** Health check endpoint path (default: '/health') */\n\thealthCheck?: string;\n\t/** Enable graceful shutdown handling (default: true) */\n\tgracefulShutdown?: boolean;\n\t/** Packages to exclude from bundling (default: []) */\n\texternal?: string[];\n\t/** Include subscribers in production build (default: 'exclude' for serverless) */\n\tsubscribers?: 'include' | 'exclude';\n\t/** Include OpenAPI spec in production (default: false) */\n\topenapi?: boolean;\n\t/**\n\t * Enable build-time optimized handler generation (default: true)\n\t * Generates specialized handlers based on endpoint tier:\n\t * - minimal: Near-raw-Hono performance for simple endpoints\n\t * - standard: Optimized handlers for auth/services\n\t * - full: Uses HonoEndpoint.addRoutes for complex endpoints\n\t */\n\toptimizedHandlers?: boolean;\n}\n\n/** Service-specific configuration for docker-compose */\nexport interface ServiceConfig {\n\t/**\n\t * Full Docker image reference (e.g., 'postgis/postgis:16-3.4-alpine').\n\t * When specified, overrides the default image entirely.\n\t */\n\timage?: string;\n\t/**\n\t * Docker image version/tag (e.g., '15-alpine' for postgres).\n\t * Only used when `image` is not specified.\n\t */\n\tversion?: string;\n}\n\n/**\n * Supported event backend types.\n *\n * - `pgboss`: Reuses PostgreSQL with a dedicated user/schema. Auto-enables db service.\n * - `sns`: Adds LocalStack container with SNS+SQS services.\n * - `rabbitmq`: Adds RabbitMQ container.\n *\n * All backends generate EVENT_PUBLISHER_CONNECTION_STRING and EVENT_SUBSCRIBER_CONNECTION_STRING.\n */\nexport type EventsBackend = 'pgboss' | 'sns' | 'rabbitmq';\n\n/**\n * Where a declared cache actually lives.\n *\n * A deployment choice, not an application one — the same code caches into any\n * of these — so it is config here beside `events` rather than a field on the\n * construct. Each backend is consistent between local and deployed, which is\n * the property worth having: a cache that behaves differently in the two places\n * is worse than a slower one.\n *\n * - `upstash` (default) — HTTP with a token. Reachable from a Lambda with no\n * VPC and no connection pool, which is why it is the default; locally the\n * `serverless-redis-http` proxy in front of Redis speaks the same protocol.\n * - `elasticache` — the Redis wire protocol, inside a VPC. Locally, plain Redis.\n * - `db` — a table in the database the app already declared. No infrastructure\n * at all, and the same relationship pg-boss has to Postgres: it creates its\n * own table on first use and needs a declared database to live in.\n */\nexport type CacheBackend = 'upstash' | 'elasticache' | 'db';\n\n/**\n * Where a declared bucket actually lives.\n *\n * The fourth of these selectors and the last to arrive, which is why\n * `UPLOADS_URL` had no answer on a container target: there was nothing to\n * choose with. Like the others it changes no application code — the `s3://`\n * scheme picks the driver, and every one of these speaks it.\n *\n * - `s3` — a real bucket. Nothing is created on the host; the URL is composed\n * against credentials you already hold, the same way `resend` is an API key\n * rather than infrastructure.\n * - `r2` — Cloudflare's, reached through the same S3 API and differing only in\n * its endpoint.\n * - `minio` — a container. Everything the app declares lives on the box, which\n * is what makes a single-host deploy self-contained, and what makes it a\n * single disk with no replication. Locally this is what every backend is.\n */\nexport type StorageBackend = 'minio' | 's3' | 'r2';\n\n/**\n * The backend a project gets when it says nothing, by where it deploys.\n *\n * Flat defaults could not be right for both: `upstash` on a single box means a\n * SaaS round-trip for a cache the database could have held, and `minio` on AWS\n * means running a bucket beside S3. So the default is a function of the target,\n * and an app that says nothing gets the obvious answer in both places — which\n * is the whole of what \"zero config\" can mean once there is more than one\n * target.\n *\n * `server` covers Dokploy and a bare host alike: the question these answer is\n * whether the app is next to a managed cloud or next to its own containers, and\n * on that question the two are the same.\n *\n * A per-construct, per-stage override is the layer above this — see\n * `constructs-outstanding.md` — so two buckets in one app can differ. This is\n * only what happens when nobody has said anything at all.\n */\nexport const DEFAULT_CACHE: Record<MainProvider, CacheBackend> = {\n\t// Reachable from a Lambda with no VPC and no connection pool.\n\taws: 'upstash',\n\t// The database is already there and already has a connection pool open to\n\t// it. Anything else is a second service to run for a table.\n\tserver: 'db',\n};\n\n/** Where a bucket lives when nobody said — see {@link DEFAULT_CACHE}. */\nexport const DEFAULT_STORAGE: Record<MainProvider, StorageBackend> = {\n\taws: 's3',\n\tserver: 'minio',\n};\n\n/**\n * Who delivers a declared app's mail.\n *\n * Unlike a cache, this changes *nothing* about the client. Every backend speaks\n * SMTP, so the declaration's `smtp://` URL is true of all of them and only the\n * provisioner differs — which is the same reason the declaration has no\n * `provider` field and there is no `ses://` scheme.\n *\n * - `ses` (default) — the only backend that can be a *chain* of resources: an\n * identity, a user, an access key, and an SMTP password derived from it,\n * because SES issues IAM credentials rather than SMTP ones. Hand it a URL and\n * it uses that instead, which is what you want when the credentials already\n * exist — provisioning a second set of sending credentials for an identity\n * that already has them is not a thing to do by default.\n * - `resend` — an API key you hold, composed into an SMTP URL. Nothing to\n * provision: a SaaS account is not infrastructure.\n * - `smtp` — a URL supplied whole, for a relay somebody else runs.\n *\n * Locally every one of them is Mailpit, because locally none of it matters.\n */\nexport type EmailBackend = 'resend' | 'ses' | 'smtp';\n\n/**\n * Who delivers mail when nobody said.\n *\n * Flat, unlike the others, and deliberately: mail is a SaaS on every target.\n * There is no self-hosted preset to default a container deploy to — Mailpit is\n * a development inbox, not a sender — so `server` has no answer of its own to\n * give and would only be `ses` written twice.\n */\nexport const DEFAULT_EMAIL: EmailBackend = 'ses';\n\n/** Supported docker-compose service names */\nexport type ComposeServiceName =\n\t| 'postgres'\n\t| 'redis'\n\t| 'rabbitmq'\n\t| 'minio'\n\t| 'mailpit'\n\t| 'localstack';\n\n/** Services configuration - can be boolean (use defaults) or object with version */\nexport type ComposeServicesConfig = {\n\t[K in ComposeServiceName]?: boolean | ServiceConfig;\n};\n\nexport interface DockerConfig {\n\t/** Container registry URL (e.g., 'ghcr.io/myorg') */\n\tregistry?: string;\n\t/** Docker image name (default: derived from package.json name) */\n\timageName?: string;\n\t/** Base Docker image (default: 'node:22-alpine') */\n\tbaseImage?: string;\n\t/** Container port (default: 3000) */\n\tport?: number;\n\t/** docker-compose services to include */\n\tcompose?: {\n\t\t/**\n\t\t * Services to include in docker-compose.\n\t\t * Can be an object with service configs or an array of service names (legacy).\n\t\t *\n\t\t * @example Object format (recommended)\n\t\t * services: {\n\t\t * postgres: { version: '15-alpine' },\n\t\t * redis: true, // use default version\n\t\t * }\n\t\t *\n\t\t * @example Array format (legacy, uses default versions)\n\t\t * services: ['postgres', 'redis']\n\t\t */\n\t\tservices?: ComposeServicesConfig | ComposeServiceName[];\n\t};\n}\n\nexport interface ServerConfig extends ProviderConfig {\n\tenableOpenApi?: boolean;\n\tport?: number;\n\t/** Production build configuration */\n\tproduction?: ProductionConfig;\n}\n\nexport type Runtime = 'node' | 'bun';\n\nexport interface TelescopeConfig {\n\t/** Enable/disable telescope (default: true in development) */\n\tenabled?: boolean;\n\t/** Port for telescope to use (defaults to server port) */\n\tport?: number;\n\t/** Path prefix for telescope UI (default: /__telescope) */\n\tpath?: string;\n\t/** Ignore patterns for telescope (e.g., ['/health', '/metrics']) */\n\tignore?: string[];\n\t/** Record request/response bodies (default: true) */\n\trecordBody?: boolean;\n\t/** Maximum entries to keep in memory (default: 1000) */\n\tmaxEntries?: number;\n\t/** Enable WebSocket for real-time updates (default: true, requires @hono/node-ws for Node.js) */\n\twebsocket?: boolean;\n}\n\nexport interface StudioConfig {\n\t/** Enable/disable studio (default: true in development) */\n\tenabled?: boolean;\n\t/** Path prefix for studio UI (default: /__studio) */\n\tpath?: string;\n\t/** Schema to introspect (default: 'public') */\n\tschema?: string;\n}\n\nexport interface OpenApiConfig {\n\t/** Enable OpenAPI generation (default: true) */\n\tenabled?: boolean;\n\t/** API title */\n\ttitle?: string;\n\t/** API version */\n\tversion?: string;\n\t/** API description */\n\tdescription?: string;\n}\n\nexport interface HooksConfig {\n\t/**\n\t * Path to a module exporting server lifecycle hooks.\n\t * The module should export `beforeSetup` and/or `afterSetup` functions.\n\t *\n\t * @example\n\t * ```typescript\n\t * // src/config/hooks.ts\n\t * import type { Hono } from 'hono';\n\t * import type { Logger } from '@geekmidas/logger';\n\t * import type { EnvironmentParser } from '@geekmidas/envkit';\n\t *\n\t * // Called BEFORE gkm endpoints are registered\n\t * export function beforeSetup(app: Hono, ctx: { envParser: EnvironmentParser; logger: Logger }) {\n\t * app.use('*', cors());\n\t * app.get('/custom/health', (c) => c.json({ status: 'ok' }));\n\t * }\n\t *\n\t * // Called AFTER gkm endpoints are registered\n\t * export function afterSetup(app: Hono, ctx: { envParser: EnvironmentParser; logger: Logger }) {\n\t * app.notFound((c) => c.json({ error: 'Not found' }, 404));\n\t * app.onError((err, c) => c.json({ error: err.message }, 500));\n\t * }\n\t * ```\n\t */\n\tserver?: string;\n}\n\n/** Dokploy deployment configuration */\nexport interface DokployProviderConfig {\n\t/** Dokploy API endpoint (e.g., 'https://dokploy.example.com') */\n\tendpoint: string;\n\t/** Project ID in Dokploy */\n\tprojectId: string;\n\t/** Application ID in Dokploy */\n\tapplicationId: string;\n\t/** Container registry (overrides docker.registry if set) */\n\tregistry?: string;\n\t/** Registry ID in Dokploy (recommended for private registries) */\n\tregistryId?: string;\n}\n\nexport interface ProvidersConfig {\n\taws?: {\n\t\tapiGateway?: {\n\t\t\tv1?: boolean | AWSApiGatewayConfig;\n\t\t\tv2?: boolean | AWSApiGatewayConfig;\n\t\t};\n\t\tlambda?: {\n\t\t\tfunctions?: boolean | AWSLambdaConfig;\n\t\t\tcrons?: boolean | AWSLambdaConfig;\n\t\t};\n\t};\n\tserver?: boolean | ServerConfig;\n\t/** Dokploy deployment configuration */\n\tdokploy?: boolean | DokployProviderConfig;\n}\n\nexport interface GkmConfig {\n\t/**\n\t * What this app is called, and therefore how everything it declares is named.\n\t *\n\t * The same field `sst.config.ts` carries, doing the same job: every physical\n\t * name is `{stage}-{name}-{construct}`, so `Database` in the `production`\n\t * stage of `kitchen-sink` is `production-kitchen-sink-database` on AWS and on\n\t * Dokploy alike. One statement, in the config, rather than a rule each\n\t * provider restates.\n\t *\n\t * `defineWorkspace` has always had this. A single-app config did not, so its\n\t * names fell back to whatever the directory happened to be called — the\n\t * monorepo's package name for the project and the app's for the application,\n\t * two accidents of layout standing in for a decision.\n\t *\n\t * Defaults to the package.json name with any scope stripped, then the\n\t * directory name.\n\t */\n\tname?: string;\n\t/**\n\t * Where the things no construct implies live.\n\t *\n\t * The same block a workspace config carries, and the same split: `db` and\n\t * `storage` are derived from the declared `KyselyDatabase` and\n\t * `ObjectStorage` and are ignored here, while `cache`, `mail`, and `events`\n\t * name a *backend* — where the cache lives, who delivers the mail, which\n\t * broker carries events. A single-app project had no way to say any of that\n\t * before, so its event backend could only be guessed.\n\t */\n\tservices?: ServicesConfig;\n\t/**\n\t * Constructs glob pattern — one glob, every kind.\n\t *\n\t * What reconcile reads to derive the containers a stage needs: a database\n\t * implies Postgres, objects imply MinIO, mail implies Mailpit. A glob per\n\t * kind cannot find a resource, because a declared `ObjectStorage` has no\n\t * kind to be listed under.\n\t *\n\t * @example './src/**\\/*.ts'\n\t */\n\tconstructs: Routes;\n\tproviders?: ProvidersConfig;\n\t/**\n\t * Server lifecycle hooks for customizing the Hono app.\n\t * Allows adding custom routes, middleware, error handlers, etc.\n\t *\n\t * @example\n\t * hooks: {\n\t * server: './src/config/hooks'\n\t * }\n\t */\n\thooks?: HooksConfig;\n\t/**\n\t * Telescope configuration for debugging/monitoring.\n\t * Can be:\n\t * - A string path to a module that exports a Telescope instance (recommended)\n\t * - A boolean to enable/disable with defaults\n\t * - A TelescopeConfig object for inline configuration\n\t */\n\ttelescope?: string | boolean | TelescopeConfig;\n\t/**\n\t * Studio configuration for database browsing.\n\t * Can be:\n\t * - A string path to a module that exports a Studio instance (recommended)\n\t * - A boolean to enable/disable with defaults\n\t * - A StudioConfig object for inline configuration\n\t *\n\t * Requires a database connection configured via services.\n\t */\n\tstudio?: string | boolean | StudioConfig;\n\t/**\n\t * OpenAPI generation configuration.\n\t * Can be:\n\t * - A boolean to enable/disable with defaults (output: ./src/api/openapi.ts)\n\t * - An OpenApiConfig object for customization\n\t *\n\t * When enabled, OpenAPI spec is generated on startup and regenerated on route changes.\n\t *\n\t * @example\n\t * openapi: true\n\t *\n\t * @example\n\t * openapi: {\n\t * output: './src/api/openapi.ts',\n\t * title: 'My API',\n\t * version: '1.0.0',\n\t * }\n\t */\n\topenapi?: boolean | OpenApiConfig;\n\t/** Runtime to use for dev server (default: 'node') */\n\truntime?: Runtime;\n\t/**\n\t * Environment file(s) to load for development.\n\t * Can be:\n\t * - A string path to a single env file (e.g., '.env.local')\n\t * - An array of paths to load in order (later files override earlier)\n\t * - Defaults to '.env' if not specified\n\t *\n\t * @example\n\t * env: '.env.local'\n\t *\n\t * @example\n\t * env: ['.env', '.env.local']\n\t */\n\tenv?: string | string[];\n\t/**\n\t * Docker deployment configuration.\n\t * Used by `gkm docker` and `gkm prepack` commands.\n\t *\n\t * @example\n\t * docker: {\n\t * registry: 'ghcr.io/myorg',\n\t * imageName: 'my-api',\n\t * compose: {\n\t * services: ['postgres', 'redis']\n\t * }\n\t * }\n\t */\n\tdocker?: DockerConfig;\n\t/**\n\t * Where this app deploys, and what the target needs to know.\n\t *\n\t * Absent until now, which meant a single-app project could not configure a\n\t * Dokploy deploy at all: the wrap that turns one into a workspace hardcoded\n\t * `{ default: 'dokploy' }` and dropped everything else, so there was no\n\t * endpoint, no registry and no domain — and `resolveHost` failed with \"no\n\t * domain configured for stage\" on a config that had nowhere to put one.\n\t *\n\t * The same shape a workspace config uses, so moving from one to the other is\n\t * a rename rather than a rewrite.\n\t */\n\tdeploy?: import('./workspace/types').DeployConfig;\n}\n\nexport interface BuildOptions {\n\tprovider?: MainProvider;\n\t// Legacy support - will be deprecated\n\tproviders?: LegacyProvider[];\n\tenableOpenApi?: boolean;\n\t/** Build for production (no dev tools, bundled output) */\n\tproduction?: boolean;\n\t/** Skip bundling step in production build */\n\tskipBundle?: boolean;\n\t/** Stage for secrets injection (e.g., 'production', 'staging') */\n\tstage?: string;\n\t/**\n\t * When true, optional environment variables (those with `.optional()` or\n\t * `.default()` in the envParser) are suffixed with `?` within each\n\t * construct's `environment` array in the manifest (e.g. `PORT?`).\n\t * Defaults to false.\n\t */\n\tmarkOptional?: boolean;\n}\n\n/** Result from build command when secrets are injected */\nexport interface BuildResult {\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\n// The deployment manifest types are a shared, dependency-free data contract\n// in `@geekmidas/manifest`, re-exported here for back-compat with existing\n// `@geekmidas/cli` imports.\nexport type {\n\tCronInfo,\n\tFunctionInfo,\n\tManifest,\n\tQueueInfo,\n\tRouteInfo,\n\tSubscriberInfo,\n\tTopicInfo,\n} from '@geekmidas/manifest';\n","import { relative } from 'node:path';\nimport type { Construct } from '@geekmidas/constructs';\nimport fg from 'fast-glob';\nimport kebabCase from 'lodash.kebabcase';\nimport type { BuildContext } from '../build/types';\nimport {\n\tisPartitionedRoutes,\n\ttype LegacyProvider,\n\ttype Routes,\n} from '../types';\n\n/**\n * Zod v4 maintains a process-wide registry of schemas registered via\n * `.meta({ id })`. When we cache-bust user module imports (by appending a\n * `?t=timestamp` query), those modules re-execute and try to register the\n * same ids again, which throws `ID X already exists in the registry`.\n *\n * Clearing the registry in the CLI process is safe: the user's running\n * server lives in a subprocess and has its own isolated registry.\n *\n * Exported for tests.\n */\nexport function clearZodGlobalRegistry(): void {\n\tconst registry = (\n\t\tglobalThis as { __zod_globalRegistry?: { clear?: () => void } }\n\t).__zod_globalRegistry;\n\tif (registry && typeof registry.clear === 'function') {\n\t\tregistry.clear();\n\t}\n}\n\nexport interface GeneratorOptions {\n\tprovider?: LegacyProvider;\n\t[key: string]: any;\n}\n\nexport abstract class ConstructGenerator<T extends Construct, R = void> {\n\tabstract isConstruct(value: any): value is T;\n\n\tstatic async build<T extends Construct, R = void>(\n\t\tcontext: BuildContext,\n\t\toutputDir: string,\n\t\tgenerator: ConstructGenerator<T, R>,\n\t\tpatterns?: Routes,\n\t\toptions?: GeneratorOptions,\n\t): Promise<R> {\n\t\tconst constructs = await generator.load(patterns);\n\t\treturn generator.build(context, constructs, outputDir, options);\n\t}\n\n\tabstract build(\n\t\tcontext: BuildContext,\n\t\tconstructs: GeneratedConstruct<T>[],\n\t\toutputDir: string,\n\t\toptions?: GeneratorOptions,\n\t): Promise<R>;\n\n\tasync load(\n\t\tpatterns?: Routes,\n\t\tcwd = process.cwd(),\n\t\tbustCache = false,\n\t): Promise<GeneratedConstruct<T>[]> {\n\t\tconst logger = console;\n\n\t\t// Extract glob patterns and optional partition function\n\t\tlet globPatterns: string[];\n\t\tlet partitionFn: ((filepath: string) => string) | undefined;\n\n\t\tif (isPartitionedRoutes(patterns)) {\n\t\t\tglobPatterns = Array.isArray(patterns.paths)\n\t\t\t\t? patterns.paths\n\t\t\t\t: [patterns.paths];\n\t\t\tpartitionFn = patterns.partition;\n\t\t} else {\n\t\t\tglobPatterns = Array.isArray(patterns)\n\t\t\t\t? patterns\n\t\t\t\t: patterns\n\t\t\t\t\t? [patterns]\n\t\t\t\t\t: [];\n\t\t}\n\n\t\t// Find all files\n\t\tconst files = fg.stream(globPatterns, {\n\t\t\tcwd,\n\t\t\tabsolute: true,\n\t\t});\n\n\t\t// Cache-busted re-imports re-execute user modules, which would try to\n\t\t// re-register Zod schemas with the same `.meta({ id })`. Clear the\n\t\t// global registry once per load to prevent duplicate-id errors.\n\t\tif (bustCache) {\n\t\t\tclearZodGlobalRegistry();\n\t\t}\n\n\t\t// Load constructs\n\t\tconst constructs: GeneratedConstruct<T>[] = [];\n\n\t\tfor await (const f of files) {\n\t\t\ttry {\n\t\t\t\tconst file = f.toString();\n\t\t\t\t// Append cache-busting query param to force re-import of changed modules\n\t\t\t\tconst importPath = bustCache ? `${file}?t=${Date.now()}` : file;\n\t\t\t\tconst module = await import(importPath);\n\n\t\t\t\t// Compute partition name for this file (if partition function provided)\n\t\t\t\tconst partition = partitionFn ? partitionFn(file) : undefined;\n\n\t\t\t\t// Check all exports for constructs\n\t\t\t\tfor (const [key, construct] of Object.entries(module)) {\n\t\t\t\t\tif (this.isConstruct(construct)) {\n\t\t\t\t\t\tconstructs.push({\n\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\tname: kebabCase(key),\n\t\t\t\t\t\t\tconstruct,\n\t\t\t\t\t\t\tpath: {\n\t\t\t\t\t\t\t\tabsolute: file,\n\t\t\t\t\t\t\t\trelative: relative(process.cwd(), file),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tpartition,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error as Error;\n\t\t\t\tlogger.error(`Failed to load ${f}: ${err.message}`);\n\t\t\t\tif (err.stack) {\n\t\t\t\t\tlogger.error(err.stack);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\treturn constructs;\n\t}\n}\n\nexport interface GeneratedConstruct<T extends Construct> {\n\tkey: string;\n\tname: string;\n\tconstruct: T;\n\tpath: {\n\t\tabsolute: string;\n\t\trelative: string;\n\t};\n\t/** Partition name assigned by the partition function, if configured. */\n\tpartition?: string;\n}\n","/**\n * Discovery — one glob, one structural test.\n *\n * A glob per kind is the same specialness the construct model removes, and\n * resources have no kind to be listed under: a declared `ObjectStorage` would\n * never be found by `routes:` or `crons:`. So there is one `constructs` glob,\n * every export of every matching module is inspected, and the per-kind\n * `isConstruct` checks collapse into a single question — does it have an id and\n * can it declare?\n *\n * Structural rather than `instanceof` on purpose. A construct from a different\n * copy of `@geekmidas/constructs` — a linked workspace, two versions in a\n * lockfile — is still a construct, and `instanceof` is exactly the check that\n * says otherwise.\n */\n\nimport { relative } from 'node:path';\nimport {\n\tassertDerivations,\n\ttype ConstructManifest,\n\tcanonicalId,\n\ttype Declaration,\n} from '@geekmidas/manifest';\nimport fg from 'fast-glob';\nimport { clearZodGlobalRegistry } from '../generators/Generator';\n\n/** Matches the rest of the CLI, which logs through `console` directly. */\nconst logger = console;\n\n/** The construct face discovery needs: an id, and the ability to declare. */\ninterface Declarable {\n\tid: string;\n\tdeclare(): Declaration[];\n}\n\n/**\n * Whether a module export is a construct.\n *\n * Deliberately loose about what else it carries — a construct that also has a\n * `service` is still a construct, and requiring one would exclude the kinds that\n * own no address.\n */\nexport function isDeclarable(value: unknown): value is Declarable {\n\tif (typeof value !== 'object' || value === null) return false;\n\n\tconst candidate = value as Partial<Declarable>;\n\n\treturn (\n\t\ttypeof candidate.id === 'string' &&\n\t\tcandidate.id.length > 0 &&\n\t\ttypeof candidate.declare === 'function'\n\t);\n}\n\nexport interface DiscoverOptions {\n\t/** The glob(s) to search. One pattern, not one per kind. */\n\tpatterns: string | readonly string[];\n\tcwd?: string;\n\t/**\n\t * Re-import changed modules, for the dev watcher.\n\t *\n\t * A single glob makes discovery more central, not less: a new construct file\n\t * has to be picked up on `add`, not only on `change`.\n\t */\n\tbustCache?: boolean;\n\t/**\n\t * Filled in with where each construct was found, when provided.\n\t *\n\t * Discovery is the only thing that knows: it globbed the files and imported\n\t * them. Everything downstream that needs to *write* an import — the build,\n\t * generating a server entry that reads a surface's logger — would otherwise\n\t * have to be told the path in config, which is the string this removes.\n\t *\n\t * An out-parameter rather than a second return value so that every existing\n\t * caller, which wants the manifest and nothing else, is unchanged.\n\t */\n\tsources?: Record<string, ConstructSource>;\n}\n\n/** Where a construct was declared, and under what name. */\nexport interface ConstructSource {\n\t/** Absolute path to the module that exported it. */\n\tfile: string;\n\t/** The name it was exported as — what an import statement has to say. */\n\texportName: string;\n}\n\n/**\n * Build the construct manifest for a project.\n *\n * Runs the manifest's own validation before returning, so an app is wrong here\n * — where the error names a file — rather than at deploy.\n *\n * @throws {DuplicateConstruct} when two exports claim one id.\n */\nexport async function discover(\n\toptions: DiscoverOptions,\n): Promise<ConstructManifest> {\n\tconst {\n\t\tpatterns,\n\t\tcwd = process.cwd(),\n\t\tbustCache = false,\n\t\tsources: out,\n\t} = options;\n\n\t// Re-importing user modules re-executes them, and Zod v4 throws on a\n\t// re-registered `.meta({ id })`. Same reason the generators clear it.\n\tif (bustCache) clearZodGlobalRegistry();\n\n\tconst manifest: Record<string, Declaration> = {};\n\t/** Which file declared each id, for the error when two of them do. */\n\tconst sources: Record<string, string> = {};\n\t/**\n\t * Constructs already seen, by identity.\n\t *\n\t * A re-export is the *same object* reached through a second file — ESM\n\t * bindings are live, so `export * from './database.js'` hands back the\n\t * binding rather than a copy. Without this, a barrel file made every\n\t * construct in it appear declared twice, and `constructs/index.ts` is the\n\t * first thing anyone writes in a shared folder.\n\t *\n\t * Identity rather than file is also the more honest rule: what may not\n\t * happen twice is two *different* constructs claiming one id, and that is\n\t * still an error below.\n\t */\n\tconst seen = new WeakSet<object>();\n\n\tconst globs = Array.isArray(patterns) ? [...patterns] : [patterns as string];\n\tconst files = fg.stream(globs, { cwd, absolute: true });\n\n\t/** Matched files, for the diagnostic below. */\n\tlet matched = 0;\n\n\tfor await (const found of files) {\n\t\tmatched++;\n\t\tconst file = found.toString();\n\t\tconst module = await import(bustCache ? `${file}?t=${Date.now()}` : file);\n\n\t\tfor (const [exportName, exported] of Object.entries(module)) {\n\t\t\tif (!isDeclarable(exported)) continue;\n\n\t\t\t// The same construct, reached again through a re-export. Skipped\n\t\t\t// rather than re-declared: it has already claimed its id, from the\n\t\t\t// file that defined it.\n\t\t\tif (seen.has(exported as object)) continue;\n\t\t\tseen.add(exported as object);\n\n\t\t\tfor (const declaration of exported.declare()) {\n\t\t\t\t// Canonicalise here too: a construct built by hand rather than through\n\t\t\t\t// a constructor is still subject to the same identity rule.\n\t\t\t\tconst id = canonicalId(declaration.id);\n\t\t\t\tconst source = relative(cwd, file);\n\n\t\t\t\tconst claimed = sources[id];\n\t\t\t\tif (claimed !== undefined && claimed !== source) {\n\t\t\t\t\tthrow new DuplicateConstruct(id, [claimed, source]);\n\t\t\t\t}\n\n\t\t\t\tmanifest[id] = { ...declaration, id };\n\t\t\t\tsources[id] = source;\n\t\t\t\tif (out) out[id] = { file, exportName };\n\t\t\t}\n\t\t}\n\t}\n\n\t// Reference integrity before anyone reads it — a schema tenant naming a\n\t// database that was deleted is a manifest error, not a reconcile failure.\n\tassertDerivations(manifest);\n\n\twarnIfNothingFound(globs, matched, Object.keys(manifest).length);\n\n\treturn manifest;\n}\n\n/**\n * Say so when a glob was configured and found nothing.\n *\n * The glob is not a filter over declarations already known — it is *how they\n * are found*, by importing what it matches. So a file it misses is a\n * declaration that does not exist, and every symptom of that appears somewhere\n * other than the cause: no container starts, no env key is written, and the\n * application fails on first use against a resource it can see in its own\n * source.\n *\n * Nothing reported it before, because an unmatched file is indistinguishable\n * from a file nobody wrote. These two cases are the ones that are *not*\n * ambiguous — a glob was configured, so something was expected.\n *\n * Silent when no glob is configured at all: that is a project which has not\n * adopted constructs, and has nothing to be missing.\n */\nfunction warnIfNothingFound(\n\tglobs: readonly string[],\n\tmatched: number,\n\tdeclared: number,\n): void {\n\tif (globs.length === 0) return;\n\n\tif (matched === 0) {\n\t\tlogger.warn(\n\t\t\t`\\n⚠️ The constructs glob matched no files, so nothing was declared.\\n` +\n\t\t\t\t` Patterns: ${globs.join(', ')}\\n` +\n\t\t\t\t` Nothing will be provisioned — no containers, no env keys, no ` +\n\t\t\t\t`resources.\\n` +\n\t\t\t\t` A common cause is depth: '*.ts' matches one level, '**/*.ts' ` +\n\t\t\t\t`matches any.`,\n\t\t);\n\t\treturn;\n\t}\n\n\tif (declared === 0) {\n\t\tlogger.warn(\n\t\t\t`\\n⚠️ The constructs glob matched ${matched} file(s) but none declared ` +\n\t\t\t\t`anything.\\n` +\n\t\t\t\t` Patterns: ${globs.join(', ')}\\n` +\n\t\t\t\t` Discovery keeps exports with an 'id' that can 'declare()'. A ` +\n\t\t\t\t`construct that is\\n` +\n\t\t\t\t` built but never exported is invisible to it.`,\n\t\t);\n\t}\n}\n\n/** Two constructs claiming one id. */\nexport class DuplicateConstruct extends Error {\n\t/** The id claimed twice. */\n\treadonly id: string;\n\t/** The files that claimed it. */\n\treadonly sources: readonly string[];\n\n\tconstructor(id: string, sources: readonly string[]) {\n\t\tsuper('Two constructs declare the same id');\n\t\tthis.name = 'DuplicateConstruct';\n\t\tthis.id = id;\n\t\tthis.sources = sources;\n\t}\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"discover-DTmFeloL.cjs","names":["routes: Routes | undefined","DEFAULT_CACHE: Record<MainProvider, CacheBackend>","DEFAULT_STORAGE: Record<MainProvider, StorageBackend>","DEFAULT_EMAIL: EmailBackend","context: BuildContext","outputDir: string","generator: ConstructGenerator<T, R>","patterns?: Routes","options?: GeneratorOptions","logger","globPatterns: string[]","partitionFn: ((filepath: string) => string) | undefined","constructs: GeneratedConstruct<T>[]","module","value: unknown","options: DiscoverOptions","manifest: Record<string, Declaration>","sources: Record<string, string>","module","globs: readonly string[]","matched: number","declared: number","id: string","sources: readonly string[]"],"sources":["../src/types.ts","../src/generators/Generator.ts","../src/reconcile/discover.ts"],"sourcesContent":["import type { ServicesConfig } from './workspace/types.js';\n\nexport type MainProvider = 'aws' | 'server';\nexport type LegacyProvider =\n\t| 'server'\n\t| 'aws-apigatewayv1'\n\t| 'aws-apigatewayv2'\n\t| 'aws-lambda';\n\nexport interface PartitionedRoutes {\n\tpaths: string | string[];\n\tpartition: (filepath: string) => string;\n}\n\nexport type Routes = string | string[] | PartitionedRoutes;\n\nexport function isPartitionedRoutes(\n\troutes: Routes | undefined,\n): routes is PartitionedRoutes {\n\treturn (\n\t\ttypeof routes === 'object' &&\n\t\troutes !== null &&\n\t\t!Array.isArray(routes) &&\n\t\t'paths' in routes\n\t);\n}\n\nexport interface ProviderConfig {\n\tenabled?: boolean;\n\toutputDir?: string;\n}\n\nexport interface AWSApiGatewayConfig extends ProviderConfig {\n\t// Additional AWS API Gateway specific options\n}\n\nexport interface AWSLambdaConfig extends ProviderConfig {\n\t// Additional AWS Lambda specific options\n}\n\nexport interface ProductionConfig {\n\t/** Enable production mode (default: false) */\n\tenabled?: boolean;\n\t/** Bundle server into single file (default: true) */\n\tbundle?: boolean;\n\t/** Minify bundled output (default: true) */\n\tminify?: boolean;\n\t/** Health check endpoint path (default: '/health') */\n\thealthCheck?: string;\n\t/** Enable graceful shutdown handling (default: true) */\n\tgracefulShutdown?: boolean;\n\t/** Packages to exclude from bundling (default: []) */\n\texternal?: string[];\n\t/** Include subscribers in production build (default: 'exclude' for serverless) */\n\tsubscribers?: 'include' | 'exclude';\n\t/** Include OpenAPI spec in production (default: false) */\n\topenapi?: boolean;\n\t/**\n\t * Enable build-time optimized handler generation (default: true)\n\t * Generates specialized handlers based on endpoint tier:\n\t * - minimal: Near-raw-Hono performance for simple endpoints\n\t * - standard: Optimized handlers for auth/services\n\t * - full: Uses HonoEndpoint.addRoutes for complex endpoints\n\t */\n\toptimizedHandlers?: boolean;\n}\n\n/** Service-specific configuration for docker-compose */\nexport interface ServiceConfig {\n\t/**\n\t * Full Docker image reference (e.g., 'postgis/postgis:16-3.4-alpine').\n\t * When specified, overrides the default image entirely.\n\t */\n\timage?: string;\n\t/**\n\t * Docker image version/tag (e.g., '15-alpine' for postgres).\n\t * Only used when `image` is not specified.\n\t */\n\tversion?: string;\n}\n\n/**\n * Supported event backend types.\n *\n * - `pgboss`: Reuses PostgreSQL with a dedicated user/schema. Auto-enables db service.\n * - `sns`: Adds LocalStack container with SNS+SQS services.\n * - `rabbitmq`: Adds RabbitMQ container.\n *\n * All backends generate EVENT_PUBLISHER_CONNECTION_STRING and EVENT_SUBSCRIBER_CONNECTION_STRING.\n */\nexport type EventsBackend = 'pgboss' | 'sns' | 'rabbitmq';\n\n/**\n * Where a declared cache actually lives.\n *\n * A deployment choice, not an application one — the same code caches into any\n * of these — so it is config here beside `events` rather than a field on the\n * construct. Each backend is consistent between local and deployed, which is\n * the property worth having: a cache that behaves differently in the two places\n * is worse than a slower one.\n *\n * - `upstash` (default) — HTTP with a token. Reachable from a Lambda with no\n * VPC and no connection pool, which is why it is the default; locally the\n * `serverless-redis-http` proxy in front of Redis speaks the same protocol.\n * - `elasticache` — the Redis wire protocol, inside a VPC. Locally, plain Redis.\n * - `db` — a table in the database the app already declared. No infrastructure\n * at all, and the same relationship pg-boss has to Postgres: it creates its\n * own table on first use and needs a declared database to live in.\n */\nexport type CacheBackend = 'upstash' | 'elasticache' | 'db';\n\n/**\n * Where a declared bucket actually lives.\n *\n * The fourth of these selectors and the last to arrive, which is why\n * `UPLOADS_URL` had no answer on a container target: there was nothing to\n * choose with. Like the others it changes no application code — the `s3://`\n * scheme picks the driver, and every one of these speaks it.\n *\n * - `s3` — a real bucket. Nothing is created on the host; the URL is composed\n * against credentials you already hold, the same way `resend` is an API key\n * rather than infrastructure.\n * - `r2` — Cloudflare's, reached through the same S3 API and differing only in\n * its endpoint.\n * - `minio` — a container. Everything the app declares lives on the box, which\n * is what makes a single-host deploy self-contained, and what makes it a\n * single disk with no replication. Locally this is what every backend is.\n */\nexport type StorageBackend = 'minio' | 's3' | 'r2';\n\n/**\n * The backend a project gets when it says nothing, by where it deploys.\n *\n * Flat defaults could not be right for both: `upstash` on a single box means a\n * SaaS round-trip for a cache the database could have held, and `minio` on AWS\n * means running a bucket beside S3. So the default is a function of the target,\n * and an app that says nothing gets the obvious answer in both places — which\n * is the whole of what \"zero config\" can mean once there is more than one\n * target.\n *\n * `server` covers Dokploy and a bare host alike: the question these answer is\n * whether the app is next to a managed cloud or next to its own containers, and\n * on that question the two are the same.\n *\n * A per-construct, per-stage override is the layer above this — see\n * `constructs-outstanding.md` — so two buckets in one app can differ. This is\n * only what happens when nobody has said anything at all.\n */\nexport const DEFAULT_CACHE: Record<MainProvider, CacheBackend> = {\n\t// Reachable from a Lambda with no VPC and no connection pool.\n\taws: 'upstash',\n\t// The database is already there and already has a connection pool open to\n\t// it. Anything else is a second service to run for a table.\n\tserver: 'db',\n};\n\n/** Where a bucket lives when nobody said — see {@link DEFAULT_CACHE}. */\nexport const DEFAULT_STORAGE: Record<MainProvider, StorageBackend> = {\n\taws: 's3',\n\tserver: 'minio',\n};\n\n/**\n * Who delivers a declared app's mail.\n *\n * Unlike a cache, this changes *nothing* about the client. Every backend speaks\n * SMTP, so the declaration's `smtp://` URL is true of all of them and only the\n * provisioner differs — which is the same reason the declaration has no\n * `provider` field and there is no `ses://` scheme.\n *\n * - `ses` (default) — the only backend that can be a *chain* of resources: an\n * identity, a user, an access key, and an SMTP password derived from it,\n * because SES issues IAM credentials rather than SMTP ones. Hand it a URL and\n * it uses that instead, which is what you want when the credentials already\n * exist — provisioning a second set of sending credentials for an identity\n * that already has them is not a thing to do by default.\n * - `resend` — an API key you hold, composed into an SMTP URL. Nothing to\n * provision: a SaaS account is not infrastructure.\n * - `smtp` — a URL supplied whole, for a relay somebody else runs.\n *\n * Locally every one of them is Mailpit, because locally none of it matters.\n */\nexport type EmailBackend = 'resend' | 'ses' | 'smtp';\n\n/**\n * Who delivers mail when nobody said.\n *\n * Flat, unlike the others, and deliberately: mail is a SaaS on every target.\n * There is no self-hosted preset to default a container deploy to — Mailpit is\n * a development inbox, not a sender — so `server` has no answer of its own to\n * give and would only be `ses` written twice.\n */\nexport const DEFAULT_EMAIL: EmailBackend = 'ses';\n\n/** Supported docker-compose service names */\nexport type ComposeServiceName =\n\t| 'postgres'\n\t| 'redis'\n\t| 'rabbitmq'\n\t| 'minio'\n\t| 'mailpit'\n\t| 'localstack';\n\n/** Services configuration - can be boolean (use defaults) or object with version */\nexport type ComposeServicesConfig = {\n\t[K in ComposeServiceName]?: boolean | ServiceConfig;\n};\n\nexport interface DockerConfig {\n\t/** Container registry URL (e.g., 'ghcr.io/myorg') */\n\tregistry?: string;\n\t/** Docker image name (default: derived from package.json name) */\n\timageName?: string;\n\t/** Base Docker image (default: 'node:22-alpine') */\n\tbaseImage?: string;\n\t/** Container port (default: 3000) */\n\tport?: number;\n\t/** docker-compose services to include */\n\tcompose?: {\n\t\t/**\n\t\t * Services to include in docker-compose.\n\t\t * Can be an object with service configs or an array of service names (legacy).\n\t\t *\n\t\t * @example Object format (recommended)\n\t\t * services: {\n\t\t * postgres: { version: '15-alpine' },\n\t\t * redis: true, // use default version\n\t\t * }\n\t\t *\n\t\t * @example Array format (legacy, uses default versions)\n\t\t * services: ['postgres', 'redis']\n\t\t */\n\t\tservices?: ComposeServicesConfig | ComposeServiceName[];\n\t};\n}\n\nexport interface ServerConfig extends ProviderConfig {\n\tenableOpenApi?: boolean;\n\tport?: number;\n\t/** Production build configuration */\n\tproduction?: ProductionConfig;\n}\n\nexport type Runtime = 'node' | 'bun';\n\nexport interface TelescopeConfig {\n\t/** Enable/disable telescope (default: true in development) */\n\tenabled?: boolean;\n\t/** Port for telescope to use (defaults to server port) */\n\tport?: number;\n\t/** Path prefix for telescope UI (default: /__telescope) */\n\tpath?: string;\n\t/** Ignore patterns for telescope (e.g., ['/health', '/metrics']) */\n\tignore?: string[];\n\t/** Record request/response bodies (default: true) */\n\trecordBody?: boolean;\n\t/** Maximum entries to keep in memory (default: 1000) */\n\tmaxEntries?: number;\n\t/** Enable WebSocket for real-time updates (default: true, requires @hono/node-ws for Node.js) */\n\twebsocket?: boolean;\n}\n\nexport interface StudioConfig {\n\t/** Enable/disable studio (default: true in development) */\n\tenabled?: boolean;\n\t/** Path prefix for studio UI (default: /__studio) */\n\tpath?: string;\n\t/** Schema to introspect (default: 'public') */\n\tschema?: string;\n}\n\nexport interface OpenApiConfig {\n\t/** Enable OpenAPI generation (default: true) */\n\tenabled?: boolean;\n\t/** API title */\n\ttitle?: string;\n\t/** API version */\n\tversion?: string;\n\t/** API description */\n\tdescription?: string;\n}\n\nexport interface HooksConfig {\n\t/**\n\t * Path to a module exporting server lifecycle hooks.\n\t * The module should export `beforeSetup` and/or `afterSetup` functions.\n\t *\n\t * @example\n\t * ```typescript\n\t * // src/config/hooks.ts\n\t * import type { Hono } from 'hono';\n\t * import type { Logger } from '@geekmidas/logger';\n\t * import type { EnvironmentParser } from '@geekmidas/envkit';\n\t *\n\t * // Called BEFORE gkm endpoints are registered\n\t * export function beforeSetup(app: Hono, ctx: { envParser: EnvironmentParser; logger: Logger }) {\n\t * app.use('*', cors());\n\t * app.get('/custom/health', (c) => c.json({ status: 'ok' }));\n\t * }\n\t *\n\t * // Called AFTER gkm endpoints are registered\n\t * export function afterSetup(app: Hono, ctx: { envParser: EnvironmentParser; logger: Logger }) {\n\t * app.notFound((c) => c.json({ error: 'Not found' }, 404));\n\t * app.onError((err, c) => c.json({ error: err.message }, 500));\n\t * }\n\t * ```\n\t */\n\tserver?: string;\n}\n\n/** Dokploy deployment configuration */\nexport interface DokployProviderConfig {\n\t/** Dokploy API endpoint (e.g., 'https://dokploy.example.com') */\n\tendpoint: string;\n\t/** Project ID in Dokploy */\n\tprojectId: string;\n\t/** Application ID in Dokploy */\n\tapplicationId: string;\n\t/** Container registry (overrides docker.registry if set) */\n\tregistry?: string;\n\t/** Registry ID in Dokploy (recommended for private registries) */\n\tregistryId?: string;\n}\n\nexport interface ProvidersConfig {\n\taws?: {\n\t\tapiGateway?: {\n\t\t\tv1?: boolean | AWSApiGatewayConfig;\n\t\t\tv2?: boolean | AWSApiGatewayConfig;\n\t\t};\n\t\tlambda?: {\n\t\t\tfunctions?: boolean | AWSLambdaConfig;\n\t\t\tcrons?: boolean | AWSLambdaConfig;\n\t\t};\n\t};\n\tserver?: boolean | ServerConfig;\n\t/** Dokploy deployment configuration */\n\tdokploy?: boolean | DokployProviderConfig;\n}\n\nexport interface GkmConfig {\n\t/**\n\t * What this app is called, and therefore how everything it declares is named.\n\t *\n\t * The same field `sst.config.ts` carries, doing the same job: every physical\n\t * name is `{stage}-{name}-{construct}`, so `Database` in the `production`\n\t * stage of `kitchen-sink` is `production-kitchen-sink-database` on AWS and on\n\t * Dokploy alike. One statement, in the config, rather than a rule each\n\t * provider restates.\n\t *\n\t * `defineWorkspace` has always had this. A single-app config did not, so its\n\t * names fell back to whatever the directory happened to be called — the\n\t * monorepo's package name for the project and the app's for the application,\n\t * two accidents of layout standing in for a decision.\n\t *\n\t * Defaults to the package.json name with any scope stripped, then the\n\t * directory name.\n\t */\n\tname?: string;\n\t/**\n\t * Where the things no construct implies live.\n\t *\n\t * The same block a workspace config carries, and the same split: `db` and\n\t * `storage` are derived from the declared `KyselyDatabase` and\n\t * `ObjectStorage` and are ignored here, while `cache`, `mail`, and `events`\n\t * name a *backend* — where the cache lives, who delivers the mail, which\n\t * broker carries events. A single-app project had no way to say any of that\n\t * before, so its event backend could only be guessed.\n\t */\n\tservices?: ServicesConfig;\n\t/**\n\t * Constructs glob pattern — one glob, every kind.\n\t *\n\t * What reconcile reads to derive the containers a stage needs: a database\n\t * implies Postgres, objects imply MinIO, mail implies Mailpit. A glob per\n\t * kind cannot find a resource, because a declared `ObjectStorage` has no\n\t * kind to be listed under.\n\t *\n\t * @example './src/**\\/*.ts'\n\t */\n\tconstructs?: Routes;\n\troutes: Routes;\n\tfunctions?: Routes;\n\tcrons?: Routes;\n\tsubscribers?: Routes;\n\tqueues?: Routes;\n\ttopics?: Routes;\n\tenvParser: string;\n\tlogger: string;\n\tproviders?: ProvidersConfig;\n\t/**\n\t * Server lifecycle hooks for customizing the Hono app.\n\t * Allows adding custom routes, middleware, error handlers, etc.\n\t *\n\t * @example\n\t * hooks: {\n\t * server: './src/config/hooks'\n\t * }\n\t */\n\thooks?: HooksConfig;\n\t/**\n\t * Telescope configuration for debugging/monitoring.\n\t * Can be:\n\t * - A string path to a module that exports a Telescope instance (recommended)\n\t * - A boolean to enable/disable with defaults\n\t * - A TelescopeConfig object for inline configuration\n\t */\n\ttelescope?: string | boolean | TelescopeConfig;\n\t/**\n\t * Studio configuration for database browsing.\n\t * Can be:\n\t * - A string path to a module that exports a Studio instance (recommended)\n\t * - A boolean to enable/disable with defaults\n\t * - A StudioConfig object for inline configuration\n\t *\n\t * Requires a database connection configured via services.\n\t */\n\tstudio?: string | boolean | StudioConfig;\n\t/**\n\t * OpenAPI generation configuration.\n\t * Can be:\n\t * - A boolean to enable/disable with defaults (output: ./src/api/openapi.ts)\n\t * - An OpenApiConfig object for customization\n\t *\n\t * When enabled, OpenAPI spec is generated on startup and regenerated on route changes.\n\t *\n\t * @example\n\t * openapi: true\n\t *\n\t * @example\n\t * openapi: {\n\t * output: './src/api/openapi.ts',\n\t * title: 'My API',\n\t * version: '1.0.0',\n\t * }\n\t */\n\topenapi?: boolean | OpenApiConfig;\n\t/** Runtime to use for dev server (default: 'node') */\n\truntime?: Runtime;\n\t/**\n\t * Environment file(s) to load for development.\n\t * Can be:\n\t * - A string path to a single env file (e.g., '.env.local')\n\t * - An array of paths to load in order (later files override earlier)\n\t * - Defaults to '.env' if not specified\n\t *\n\t * @example\n\t * env: '.env.local'\n\t *\n\t * @example\n\t * env: ['.env', '.env.local']\n\t */\n\tenv?: string | string[];\n\t/**\n\t * Docker deployment configuration.\n\t * Used by `gkm docker` and `gkm prepack` commands.\n\t *\n\t * @example\n\t * docker: {\n\t * registry: 'ghcr.io/myorg',\n\t * imageName: 'my-api',\n\t * compose: {\n\t * services: ['postgres', 'redis']\n\t * }\n\t * }\n\t */\n\tdocker?: DockerConfig;\n\t/**\n\t * Where this app deploys, and what the target needs to know.\n\t *\n\t * Absent until now, which meant a single-app project could not configure a\n\t * Dokploy deploy at all: the wrap that turns one into a workspace hardcoded\n\t * `{ default: 'dokploy' }` and dropped everything else, so there was no\n\t * endpoint, no registry and no domain — and `resolveHost` failed with \"no\n\t * domain configured for stage\" on a config that had nowhere to put one.\n\t *\n\t * The same shape a workspace config uses, so moving from one to the other is\n\t * a rename rather than a rewrite.\n\t */\n\tdeploy?: import('./workspace/types').DeployConfig;\n}\n\nexport interface BuildOptions {\n\tprovider?: MainProvider;\n\t// Legacy support - will be deprecated\n\tproviders?: LegacyProvider[];\n\tenableOpenApi?: boolean;\n\t/** Build for production (no dev tools, bundled output) */\n\tproduction?: boolean;\n\t/** Skip bundling step in production build */\n\tskipBundle?: boolean;\n\t/** Stage for secrets injection (e.g., 'production', 'staging') */\n\tstage?: string;\n\t/**\n\t * When true, optional environment variables (those with `.optional()` or\n\t * `.default()` in the envParser) are suffixed with `?` within each\n\t * construct's `environment` array in the manifest (e.g. `PORT?`).\n\t * Defaults to false.\n\t */\n\tmarkOptional?: boolean;\n}\n\n/** Result from build command when secrets are injected */\nexport interface BuildResult {\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\n// The deployment manifest types are a shared, dependency-free data contract\n// in `@geekmidas/manifest`, re-exported here for back-compat with existing\n// `@geekmidas/cli` imports.\nexport type {\n\tCronInfo,\n\tFunctionInfo,\n\tManifest,\n\tQueueInfo,\n\tRouteInfo,\n\tSubscriberInfo,\n\tTopicInfo,\n} from '@geekmidas/manifest';\n","import { relative } from 'node:path';\nimport type { Construct } from '@geekmidas/constructs';\nimport fg from 'fast-glob';\nimport kebabCase from 'lodash.kebabcase';\nimport type { BuildContext } from '../build/types';\nimport {\n\tisPartitionedRoutes,\n\ttype LegacyProvider,\n\ttype Routes,\n} from '../types';\n\n/**\n * Zod v4 maintains a process-wide registry of schemas registered via\n * `.meta({ id })`. When we cache-bust user module imports (by appending a\n * `?t=timestamp` query), those modules re-execute and try to register the\n * same ids again, which throws `ID X already exists in the registry`.\n *\n * Clearing the registry in the CLI process is safe: the user's running\n * server lives in a subprocess and has its own isolated registry.\n *\n * Exported for tests.\n */\nexport function clearZodGlobalRegistry(): void {\n\tconst registry = (\n\t\tglobalThis as { __zod_globalRegistry?: { clear?: () => void } }\n\t).__zod_globalRegistry;\n\tif (registry && typeof registry.clear === 'function') {\n\t\tregistry.clear();\n\t}\n}\n\nexport interface GeneratorOptions {\n\tprovider?: LegacyProvider;\n\t[key: string]: any;\n}\n\nexport abstract class ConstructGenerator<T extends Construct, R = void> {\n\tabstract isConstruct(value: any): value is T;\n\n\tstatic async build<T extends Construct, R = void>(\n\t\tcontext: BuildContext,\n\t\toutputDir: string,\n\t\tgenerator: ConstructGenerator<T, R>,\n\t\tpatterns?: Routes,\n\t\toptions?: GeneratorOptions,\n\t): Promise<R> {\n\t\tconst constructs = await generator.load(patterns);\n\t\treturn generator.build(context, constructs, outputDir, options);\n\t}\n\n\tabstract build(\n\t\tcontext: BuildContext,\n\t\tconstructs: GeneratedConstruct<T>[],\n\t\toutputDir: string,\n\t\toptions?: GeneratorOptions,\n\t): Promise<R>;\n\n\tasync load(\n\t\tpatterns?: Routes,\n\t\tcwd = process.cwd(),\n\t\tbustCache = false,\n\t): Promise<GeneratedConstruct<T>[]> {\n\t\tconst logger = console;\n\n\t\t// Extract glob patterns and optional partition function\n\t\tlet globPatterns: string[];\n\t\tlet partitionFn: ((filepath: string) => string) | undefined;\n\n\t\tif (isPartitionedRoutes(patterns)) {\n\t\t\tglobPatterns = Array.isArray(patterns.paths)\n\t\t\t\t? patterns.paths\n\t\t\t\t: [patterns.paths];\n\t\t\tpartitionFn = patterns.partition;\n\t\t} else {\n\t\t\tglobPatterns = Array.isArray(patterns)\n\t\t\t\t? patterns\n\t\t\t\t: patterns\n\t\t\t\t\t? [patterns]\n\t\t\t\t\t: [];\n\t\t}\n\n\t\t// Find all files\n\t\tconst files = fg.stream(globPatterns, {\n\t\t\tcwd,\n\t\t\tabsolute: true,\n\t\t});\n\n\t\t// Cache-busted re-imports re-execute user modules, which would try to\n\t\t// re-register Zod schemas with the same `.meta({ id })`. Clear the\n\t\t// global registry once per load to prevent duplicate-id errors.\n\t\tif (bustCache) {\n\t\t\tclearZodGlobalRegistry();\n\t\t}\n\n\t\t// Load constructs\n\t\tconst constructs: GeneratedConstruct<T>[] = [];\n\n\t\tfor await (const f of files) {\n\t\t\ttry {\n\t\t\t\tconst file = f.toString();\n\t\t\t\t// Append cache-busting query param to force re-import of changed modules\n\t\t\t\tconst importPath = bustCache ? `${file}?t=${Date.now()}` : file;\n\t\t\t\tconst module = await import(importPath);\n\n\t\t\t\t// Compute partition name for this file (if partition function provided)\n\t\t\t\tconst partition = partitionFn ? partitionFn(file) : undefined;\n\n\t\t\t\t// Check all exports for constructs\n\t\t\t\tfor (const [key, construct] of Object.entries(module)) {\n\t\t\t\t\tif (this.isConstruct(construct)) {\n\t\t\t\t\t\tconstructs.push({\n\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\tname: kebabCase(key),\n\t\t\t\t\t\t\tconstruct,\n\t\t\t\t\t\t\tpath: {\n\t\t\t\t\t\t\t\tabsolute: file,\n\t\t\t\t\t\t\t\trelative: relative(process.cwd(), file),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tpartition,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error as Error;\n\t\t\t\tlogger.error(`Failed to load ${f}: ${err.message}`);\n\t\t\t\tif (err.stack) {\n\t\t\t\t\tlogger.error(err.stack);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\treturn constructs;\n\t}\n}\n\nexport interface GeneratedConstruct<T extends Construct> {\n\tkey: string;\n\tname: string;\n\tconstruct: T;\n\tpath: {\n\t\tabsolute: string;\n\t\trelative: string;\n\t};\n\t/** Partition name assigned by the partition function, if configured. */\n\tpartition?: string;\n}\n","/**\n * Discovery — one glob, one structural test.\n *\n * A glob per kind is the same specialness the construct model removes, and\n * resources have no kind to be listed under: a declared `ObjectStorage` would\n * never be found by `routes:` or `crons:`. So there is one `constructs` glob,\n * every export of every matching module is inspected, and the per-kind\n * `isConstruct` checks collapse into a single question — does it have an id and\n * can it declare?\n *\n * Structural rather than `instanceof` on purpose. A construct from a different\n * copy of `@geekmidas/constructs` — a linked workspace, two versions in a\n * lockfile — is still a construct, and `instanceof` is exactly the check that\n * says otherwise.\n */\n\nimport { relative } from 'node:path';\nimport {\n\tassertDerivations,\n\ttype ConstructManifest,\n\tcanonicalId,\n\ttype Declaration,\n} from '@geekmidas/manifest';\nimport fg from 'fast-glob';\nimport { clearZodGlobalRegistry } from '../generators/Generator';\n\n/** Matches the rest of the CLI, which logs through `console` directly. */\nconst logger = console;\n\n/** The construct face discovery needs: an id, and the ability to declare. */\ninterface Declarable {\n\tid: string;\n\tdeclare(): Declaration[];\n}\n\n/**\n * Whether a module export is a construct.\n *\n * Deliberately loose about what else it carries — a construct that also has a\n * `service` is still a construct, and requiring one would exclude the kinds that\n * own no address.\n */\nexport function isDeclarable(value: unknown): value is Declarable {\n\tif (typeof value !== 'object' || value === null) return false;\n\n\tconst candidate = value as Partial<Declarable>;\n\n\treturn (\n\t\ttypeof candidate.id === 'string' &&\n\t\tcandidate.id.length > 0 &&\n\t\ttypeof candidate.declare === 'function'\n\t);\n}\n\nexport interface DiscoverOptions {\n\t/** The glob(s) to search. One pattern, not one per kind. */\n\tpatterns: string | readonly string[];\n\tcwd?: string;\n\t/**\n\t * Re-import changed modules, for the dev watcher.\n\t *\n\t * A single glob makes discovery more central, not less: a new construct file\n\t * has to be picked up on `add`, not only on `change`.\n\t */\n\tbustCache?: boolean;\n\t/**\n\t * Filled in with where each construct was found, when provided.\n\t *\n\t * Discovery is the only thing that knows: it globbed the files and imported\n\t * them. Everything downstream that needs to *write* an import — the build,\n\t * generating a server entry that reads a surface's logger — would otherwise\n\t * have to be told the path in config, which is the string this removes.\n\t *\n\t * An out-parameter rather than a second return value so that every existing\n\t * caller, which wants the manifest and nothing else, is unchanged.\n\t */\n\tsources?: Record<string, ConstructSource>;\n}\n\n/** Where a construct was declared, and under what name. */\nexport interface ConstructSource {\n\t/** Absolute path to the module that exported it. */\n\tfile: string;\n\t/** The name it was exported as — what an import statement has to say. */\n\texportName: string;\n}\n\n/**\n * Build the construct manifest for a project.\n *\n * Runs the manifest's own validation before returning, so an app is wrong here\n * — where the error names a file — rather than at deploy.\n *\n * @throws {DuplicateConstruct} when two exports claim one id.\n */\nexport async function discover(\n\toptions: DiscoverOptions,\n): Promise<ConstructManifest> {\n\tconst {\n\t\tpatterns,\n\t\tcwd = process.cwd(),\n\t\tbustCache = false,\n\t\tsources: out,\n\t} = options;\n\n\t// Re-importing user modules re-executes them, and Zod v4 throws on a\n\t// re-registered `.meta({ id })`. Same reason the generators clear it.\n\tif (bustCache) clearZodGlobalRegistry();\n\n\tconst manifest: Record<string, Declaration> = {};\n\t/** Which file declared each id, for the error when two of them do. */\n\tconst sources: Record<string, string> = {};\n\t/**\n\t * Constructs already seen, by identity.\n\t *\n\t * A re-export is the *same object* reached through a second file — ESM\n\t * bindings are live, so `export * from './database.js'` hands back the\n\t * binding rather than a copy. Without this, a barrel file made every\n\t * construct in it appear declared twice, and `constructs/index.ts` is the\n\t * first thing anyone writes in a shared folder.\n\t *\n\t * Identity rather than file is also the more honest rule: what may not\n\t * happen twice is two *different* constructs claiming one id, and that is\n\t * still an error below.\n\t */\n\tconst seen = new WeakSet<object>();\n\n\tconst globs = Array.isArray(patterns) ? [...patterns] : [patterns as string];\n\tconst files = fg.stream(globs, { cwd, absolute: true });\n\n\t/** Matched files, for the diagnostic below. */\n\tlet matched = 0;\n\n\tfor await (const found of files) {\n\t\tmatched++;\n\t\tconst file = found.toString();\n\t\tconst module = await import(bustCache ? `${file}?t=${Date.now()}` : file);\n\n\t\tfor (const [exportName, exported] of Object.entries(module)) {\n\t\t\tif (!isDeclarable(exported)) continue;\n\n\t\t\t// The same construct, reached again through a re-export. Skipped\n\t\t\t// rather than re-declared: it has already claimed its id, from the\n\t\t\t// file that defined it.\n\t\t\tif (seen.has(exported as object)) continue;\n\t\t\tseen.add(exported as object);\n\n\t\t\tfor (const declaration of exported.declare()) {\n\t\t\t\t// Canonicalise here too: a construct built by hand rather than through\n\t\t\t\t// a constructor is still subject to the same identity rule.\n\t\t\t\tconst id = canonicalId(declaration.id);\n\t\t\t\tconst source = relative(cwd, file);\n\n\t\t\t\tconst claimed = sources[id];\n\t\t\t\tif (claimed !== undefined && claimed !== source) {\n\t\t\t\t\tthrow new DuplicateConstruct(id, [claimed, source]);\n\t\t\t\t}\n\n\t\t\t\tmanifest[id] = { ...declaration, id };\n\t\t\t\tsources[id] = source;\n\t\t\t\tif (out) out[id] = { file, exportName };\n\t\t\t}\n\t\t}\n\t}\n\n\t// Reference integrity before anyone reads it — a schema tenant naming a\n\t// database that was deleted is a manifest error, not a reconcile failure.\n\tassertDerivations(manifest);\n\n\twarnIfNothingFound(globs, matched, Object.keys(manifest).length);\n\n\treturn manifest;\n}\n\n/**\n * Say so when a glob was configured and found nothing.\n *\n * The glob is not a filter over declarations already known — it is *how they\n * are found*, by importing what it matches. So a file it misses is a\n * declaration that does not exist, and every symptom of that appears somewhere\n * other than the cause: no container starts, no env key is written, and the\n * application fails on first use against a resource it can see in its own\n * source.\n *\n * Nothing reported it before, because an unmatched file is indistinguishable\n * from a file nobody wrote. These two cases are the ones that are *not*\n * ambiguous — a glob was configured, so something was expected.\n *\n * Silent when no glob is configured at all: that is a project which has not\n * adopted constructs, and has nothing to be missing.\n */\nfunction warnIfNothingFound(\n\tglobs: readonly string[],\n\tmatched: number,\n\tdeclared: number,\n): void {\n\tif (globs.length === 0) return;\n\n\tif (matched === 0) {\n\t\tlogger.warn(\n\t\t\t`\\n⚠️ The constructs glob matched no files, so nothing was declared.\\n` +\n\t\t\t\t` Patterns: ${globs.join(', ')}\\n` +\n\t\t\t\t` Nothing will be provisioned — no containers, no env keys, no ` +\n\t\t\t\t`resources.\\n` +\n\t\t\t\t` A common cause is depth: '*.ts' matches one level, '**/*.ts' ` +\n\t\t\t\t`matches any.`,\n\t\t);\n\t\treturn;\n\t}\n\n\tif (declared === 0) {\n\t\tlogger.warn(\n\t\t\t`\\n⚠️ The constructs glob matched ${matched} file(s) but none declared ` +\n\t\t\t\t`anything.\\n` +\n\t\t\t\t` Patterns: ${globs.join(', ')}\\n` +\n\t\t\t\t` Discovery keeps exports with an 'id' that can 'declare()'. A ` +\n\t\t\t\t`construct that is\\n` +\n\t\t\t\t` built but never exported is invisible to it.`,\n\t\t);\n\t}\n}\n\n/** Two constructs claiming one id. */\nexport class DuplicateConstruct extends Error {\n\t/** The id claimed twice. */\n\treadonly id: string;\n\t/** The files that claimed it. */\n\treadonly sources: readonly string[];\n\n\tconstructor(id: string, sources: readonly string[]) {\n\t\tsuper('Two constructs declare the same id');\n\t\tthis.name = 'DuplicateConstruct';\n\t\tthis.id = id;\n\t\tthis.sources = sources;\n\t}\n}\n"],"mappings":";;;;;;;AAgBA,SAAgB,oBACfA,QAC8B;AAC9B,eACQ,WAAW,YAClB,WAAW,SACV,MAAM,QAAQ,OAAO,IACtB,WAAW;AAEZ;;;;;;;;;;;;;;;;;;;AA2HD,MAAaC,gBAAoD;CAEhE,KAAK;CAGL,QAAQ;AACR;;AAGD,MAAaC,kBAAwD;CACpE,KAAK;CACL,QAAQ;AACR;;;;;;;;;AAgCD,MAAaC,gBAA8B;;;;;;;;;;;;;;;AC1K3C,SAAgB,yBAA+B;CAC9C,MAAM,WACL,WACC;AACF,KAAI,mBAAmB,SAAS,UAAU,WACzC,UAAS,OAAO;AAEjB;AAOD,IAAsB,qBAAtB,MAAwE;CAGvE,aAAa,MACZC,SACAC,WACAC,WACAC,UACAC,SACa;EACb,MAAM,aAAa,MAAM,UAAU,KAAK,SAAS;AACjD,SAAO,UAAU,MAAM,SAAS,YAAY,WAAW,QAAQ;CAC/D;CASD,MAAM,KACLD,UACA,MAAM,QAAQ,KAAK,EACnB,YAAY,OACuB;EACnC,MAAME,WAAS;EAGf,IAAIC;EACJ,IAAIC;AAEJ,MAAI,oBAAoB,SAAS,EAAE;AAClC,kBAAe,MAAM,QAAQ,SAAS,MAAM,GACzC,SAAS,QACT,CAAC,SAAS,KAAM;AACnB,iBAAc,SAAS;EACvB,MACA,gBAAe,MAAM,QAAQ,SAAS,GACnC,WACA,WACC,CAAC,QAAS,IACV,CAAE;EAIP,MAAM,QAAQ,kBAAG,OAAO,cAAc;GACrC;GACA,UAAU;EACV,EAAC;AAKF,MAAI,UACH,yBAAwB;EAIzB,MAAMC,aAAsC,CAAE;AAE9C,aAAW,MAAM,KAAK,MACrB,KAAI;GACH,MAAM,OAAO,EAAE,UAAU;GAEzB,MAAM,aAAa,aAAa,EAAE,KAAK,KAAK,KAAK,KAAK,CAAC,IAAI;GAC3D,MAAMC,WAAS,MAAM,OAAO;GAG5B,MAAM,YAAY,cAAc,YAAY,KAAK;AAGjD,QAAK,MAAM,CAAC,KAAK,UAAU,IAAI,OAAO,QAAQA,SAAO,CACpD,KAAI,KAAK,YAAY,UAAU,CAC9B,YAAW,KAAK;IACf;IACA,MAAM,8BAAU,IAAI;IACpB;IACA,MAAM;KACL,UAAU;KACV,UAAU,wBAAS,QAAQ,KAAK,EAAE,KAAK;IACvC;IACD;GACA,EAAC;EAGJ,SAAQ,OAAO;GACf,MAAM,MAAM;AACZ,YAAO,OAAO,iBAAiB,EAAE,IAAI,IAAI,QAAQ,EAAE;AACnD,OAAI,IAAI,MACP,UAAO,MAAM,IAAI,MAAM;AAExB,SAAM;EACN;AAGF,SAAO;CACP;AACD;;;;;AC3GD,MAAM,SAAS;;;;;;;;AAef,SAAgB,aAAaC,OAAqC;AACjE,YAAW,UAAU,YAAY,UAAU,KAAM,QAAO;CAExD,MAAM,YAAY;AAElB,eACQ,UAAU,OAAO,YACxB,UAAU,GAAG,SAAS,YACf,UAAU,YAAY;AAE9B;;;;;;;;;AA2CD,eAAsB,SACrBC,SAC6B;CAC7B,MAAM,EACL,UACA,MAAM,QAAQ,KAAK,EACnB,YAAY,OACZ,SAAS,KACT,GAAG;AAIJ,KAAI,UAAW,yBAAwB;CAEvC,MAAMC,WAAwC,CAAE;;CAEhD,MAAMC,UAAkC,CAAE;;;;;;;;;;;;;;CAc1C,MAAM,uBAAO,IAAI;CAEjB,MAAM,QAAQ,MAAM,QAAQ,SAAS,GAAG,CAAC,GAAG,QAAS,IAAG,CAAC,QAAmB;CAC5E,MAAM,QAAQ,kBAAG,OAAO,OAAO;EAAE;EAAK,UAAU;CAAM,EAAC;;CAGvD,IAAI,UAAU;AAEd,YAAW,MAAM,SAAS,OAAO;AAChC;EACA,MAAM,OAAO,MAAM,UAAU;EAC7B,MAAMC,WAAS,OAAa,YAoG7B,QApG0C,EAAE,KAAK,KAAK,KAAK,KAAK,CAAC,KAoGhE,OApGoE;AAEpE,OAAK,MAAM,CAAC,YAAY,SAAS,IAAI,OAAO,QAAQA,SAAO,EAAE;AAC5D,QAAK,aAAa,SAAS,CAAE;AAK7B,OAAI,KAAK,IAAI,SAAmB,CAAE;AAClC,QAAK,IAAI,SAAmB;AAE5B,QAAK,MAAM,eAAe,SAAS,SAAS,EAAE;IAG7C,MAAM,KAAK,sCAAY,YAAY,GAAG;IACtC,MAAM,SAAS,wBAAS,KAAK,KAAK;IAElC,MAAM,UAAU,QAAQ;AACxB,QAAI,sBAAyB,YAAY,OACxC,OAAM,IAAI,mBAAmB,IAAI,CAAC,SAAS,MAAO;AAGnD,aAAS,MAAM;KAAE,GAAG;KAAa;IAAI;AACrC,YAAQ,MAAM;AACd,QAAI,IAAK,KAAI,MAAM;KAAE;KAAM;IAAY;GACvC;EACD;CACD;AAID,6CAAkB,SAAS;AAE3B,oBAAmB,OAAO,SAAS,OAAO,KAAK,SAAS,CAAC,OAAO;AAEhE,QAAO;AACP;;;;;;;;;;;;;;;;;;AAmBD,SAAS,mBACRC,OACAC,SACAC,UACO;AACP,KAAI,MAAM,WAAW,EAAG;AAExB,KAAI,YAAY,GAAG;AAClB,SAAO,MACL,qFACgB,MAAM,KAAK,KAAK,CAAC,4JAKlC;AACD;CACA;AAED,KAAI,aAAa,EAChB,QAAO,MACL,oCAAoC,QAAQ,qDAE5B,MAAM,KAAK,KAAK,CAAC,sIAIlC;AAEF;;AAGD,IAAa,qBAAb,cAAwC,MAAM;;CAE7C,AAAS;;CAET,AAAS;CAET,YAAYC,IAAYC,SAA4B;AACnD,QAAM,qCAAqC;AAC3C,OAAK,OAAO;AACZ,OAAK,KAAK;AACV,OAAK,UAAU;CACf;AACD"}
1
+ {"version":3,"file":"discover-DTmFeloL.cjs","names":["routes: Routes | undefined","DEFAULT_CACHE: Record<MainProvider, CacheBackend>","DEFAULT_STORAGE: Record<MainProvider, StorageBackend>","DEFAULT_EMAIL: EmailBackend","context: BuildContext","outputDir: string","generator: ConstructGenerator<T, R>","patterns?: Routes","options?: GeneratorOptions","logger","globPatterns: string[]","partitionFn: ((filepath: string) => string) | undefined","constructs: GeneratedConstruct<T>[]","module","value: unknown","options: DiscoverOptions","manifest: Record<string, Declaration>","sources: Record<string, string>","module","globs: readonly string[]","matched: number","declared: number","id: string","sources: readonly string[]"],"sources":["../src/types.ts","../src/generators/Generator.ts","../src/reconcile/discover.ts"],"sourcesContent":["import type { ServicesConfig } from './workspace/types.js';\n\nexport type MainProvider = 'aws' | 'server';\nexport type LegacyProvider =\n\t| 'server'\n\t| 'aws-apigatewayv1'\n\t| 'aws-apigatewayv2'\n\t| 'aws-lambda';\n\nexport interface PartitionedRoutes {\n\tpaths: string | string[];\n\tpartition: (filepath: string) => string;\n}\n\nexport type Routes = string | string[] | PartitionedRoutes;\n\nexport function isPartitionedRoutes(\n\troutes: Routes | undefined,\n): routes is PartitionedRoutes {\n\treturn (\n\t\ttypeof routes === 'object' &&\n\t\troutes !== null &&\n\t\t!Array.isArray(routes) &&\n\t\t'paths' in routes\n\t);\n}\n\nexport interface ProviderConfig {\n\tenabled?: boolean;\n\toutputDir?: string;\n}\n\nexport interface AWSApiGatewayConfig extends ProviderConfig {\n\t// Additional AWS API Gateway specific options\n}\n\nexport interface AWSLambdaConfig extends ProviderConfig {\n\t// Additional AWS Lambda specific options\n}\n\nexport interface ProductionConfig {\n\t/** Enable production mode (default: false) */\n\tenabled?: boolean;\n\t/** Bundle server into single file (default: true) */\n\tbundle?: boolean;\n\t/** Minify bundled output (default: true) */\n\tminify?: boolean;\n\t/** Health check endpoint path (default: '/health') */\n\thealthCheck?: string;\n\t/** Enable graceful shutdown handling (default: true) */\n\tgracefulShutdown?: boolean;\n\t/** Packages to exclude from bundling (default: []) */\n\texternal?: string[];\n\t/** Include subscribers in production build (default: 'exclude' for serverless) */\n\tsubscribers?: 'include' | 'exclude';\n\t/** Include OpenAPI spec in production (default: false) */\n\topenapi?: boolean;\n\t/**\n\t * Enable build-time optimized handler generation (default: true)\n\t * Generates specialized handlers based on endpoint tier:\n\t * - minimal: Near-raw-Hono performance for simple endpoints\n\t * - standard: Optimized handlers for auth/services\n\t * - full: Uses HonoEndpoint.addRoutes for complex endpoints\n\t */\n\toptimizedHandlers?: boolean;\n}\n\n/** Service-specific configuration for docker-compose */\nexport interface ServiceConfig {\n\t/**\n\t * Full Docker image reference (e.g., 'postgis/postgis:16-3.4-alpine').\n\t * When specified, overrides the default image entirely.\n\t */\n\timage?: string;\n\t/**\n\t * Docker image version/tag (e.g., '15-alpine' for postgres).\n\t * Only used when `image` is not specified.\n\t */\n\tversion?: string;\n}\n\n/**\n * Supported event backend types.\n *\n * - `pgboss`: Reuses PostgreSQL with a dedicated user/schema. Auto-enables db service.\n * - `sns`: Adds LocalStack container with SNS+SQS services.\n * - `rabbitmq`: Adds RabbitMQ container.\n *\n * All backends generate EVENT_PUBLISHER_CONNECTION_STRING and EVENT_SUBSCRIBER_CONNECTION_STRING.\n */\nexport type EventsBackend = 'pgboss' | 'sns' | 'rabbitmq';\n\n/**\n * Where a declared cache actually lives.\n *\n * A deployment choice, not an application one — the same code caches into any\n * of these — so it is config here beside `events` rather than a field on the\n * construct. Each backend is consistent between local and deployed, which is\n * the property worth having: a cache that behaves differently in the two places\n * is worse than a slower one.\n *\n * - `upstash` (default) — HTTP with a token. Reachable from a Lambda with no\n * VPC and no connection pool, which is why it is the default; locally the\n * `serverless-redis-http` proxy in front of Redis speaks the same protocol.\n * - `elasticache` — the Redis wire protocol, inside a VPC. Locally, plain Redis.\n * - `db` — a table in the database the app already declared. No infrastructure\n * at all, and the same relationship pg-boss has to Postgres: it creates its\n * own table on first use and needs a declared database to live in.\n */\nexport type CacheBackend = 'upstash' | 'elasticache' | 'db';\n\n/**\n * Where a declared bucket actually lives.\n *\n * The fourth of these selectors and the last to arrive, which is why\n * `UPLOADS_URL` had no answer on a container target: there was nothing to\n * choose with. Like the others it changes no application code — the `s3://`\n * scheme picks the driver, and every one of these speaks it.\n *\n * - `s3` — a real bucket. Nothing is created on the host; the URL is composed\n * against credentials you already hold, the same way `resend` is an API key\n * rather than infrastructure.\n * - `r2` — Cloudflare's, reached through the same S3 API and differing only in\n * its endpoint.\n * - `minio` — a container. Everything the app declares lives on the box, which\n * is what makes a single-host deploy self-contained, and what makes it a\n * single disk with no replication. Locally this is what every backend is.\n */\nexport type StorageBackend = 'minio' | 's3' | 'r2';\n\n/**\n * The backend a project gets when it says nothing, by where it deploys.\n *\n * Flat defaults could not be right for both: `upstash` on a single box means a\n * SaaS round-trip for a cache the database could have held, and `minio` on AWS\n * means running a bucket beside S3. So the default is a function of the target,\n * and an app that says nothing gets the obvious answer in both places — which\n * is the whole of what \"zero config\" can mean once there is more than one\n * target.\n *\n * `server` covers Dokploy and a bare host alike: the question these answer is\n * whether the app is next to a managed cloud or next to its own containers, and\n * on that question the two are the same.\n *\n * A per-construct, per-stage override is the layer above this — see\n * `constructs-outstanding.md` — so two buckets in one app can differ. This is\n * only what happens when nobody has said anything at all.\n */\nexport const DEFAULT_CACHE: Record<MainProvider, CacheBackend> = {\n\t// Reachable from a Lambda with no VPC and no connection pool.\n\taws: 'upstash',\n\t// The database is already there and already has a connection pool open to\n\t// it. Anything else is a second service to run for a table.\n\tserver: 'db',\n};\n\n/** Where a bucket lives when nobody said — see {@link DEFAULT_CACHE}. */\nexport const DEFAULT_STORAGE: Record<MainProvider, StorageBackend> = {\n\taws: 's3',\n\tserver: 'minio',\n};\n\n/**\n * Who delivers a declared app's mail.\n *\n * Unlike a cache, this changes *nothing* about the client. Every backend speaks\n * SMTP, so the declaration's `smtp://` URL is true of all of them and only the\n * provisioner differs — which is the same reason the declaration has no\n * `provider` field and there is no `ses://` scheme.\n *\n * - `ses` (default) — the only backend that can be a *chain* of resources: an\n * identity, a user, an access key, and an SMTP password derived from it,\n * because SES issues IAM credentials rather than SMTP ones. Hand it a URL and\n * it uses that instead, which is what you want when the credentials already\n * exist — provisioning a second set of sending credentials for an identity\n * that already has them is not a thing to do by default.\n * - `resend` — an API key you hold, composed into an SMTP URL. Nothing to\n * provision: a SaaS account is not infrastructure.\n * - `smtp` — a URL supplied whole, for a relay somebody else runs.\n *\n * Locally every one of them is Mailpit, because locally none of it matters.\n */\nexport type EmailBackend = 'resend' | 'ses' | 'smtp';\n\n/**\n * Who delivers mail when nobody said.\n *\n * Flat, unlike the others, and deliberately: mail is a SaaS on every target.\n * There is no self-hosted preset to default a container deploy to — Mailpit is\n * a development inbox, not a sender — so `server` has no answer of its own to\n * give and would only be `ses` written twice.\n */\nexport const DEFAULT_EMAIL: EmailBackend = 'ses';\n\n/** Supported docker-compose service names */\nexport type ComposeServiceName =\n\t| 'postgres'\n\t| 'redis'\n\t| 'rabbitmq'\n\t| 'minio'\n\t| 'mailpit'\n\t| 'localstack';\n\n/** Services configuration - can be boolean (use defaults) or object with version */\nexport type ComposeServicesConfig = {\n\t[K in ComposeServiceName]?: boolean | ServiceConfig;\n};\n\nexport interface DockerConfig {\n\t/** Container registry URL (e.g., 'ghcr.io/myorg') */\n\tregistry?: string;\n\t/** Docker image name (default: derived from package.json name) */\n\timageName?: string;\n\t/** Base Docker image (default: 'node:22-alpine') */\n\tbaseImage?: string;\n\t/** Container port (default: 3000) */\n\tport?: number;\n\t/** docker-compose services to include */\n\tcompose?: {\n\t\t/**\n\t\t * Services to include in docker-compose.\n\t\t * Can be an object with service configs or an array of service names (legacy).\n\t\t *\n\t\t * @example Object format (recommended)\n\t\t * services: {\n\t\t * postgres: { version: '15-alpine' },\n\t\t * redis: true, // use default version\n\t\t * }\n\t\t *\n\t\t * @example Array format (legacy, uses default versions)\n\t\t * services: ['postgres', 'redis']\n\t\t */\n\t\tservices?: ComposeServicesConfig | ComposeServiceName[];\n\t};\n}\n\nexport interface ServerConfig extends ProviderConfig {\n\tenableOpenApi?: boolean;\n\tport?: number;\n\t/** Production build configuration */\n\tproduction?: ProductionConfig;\n}\n\nexport type Runtime = 'node' | 'bun';\n\nexport interface TelescopeConfig {\n\t/** Enable/disable telescope (default: true in development) */\n\tenabled?: boolean;\n\t/** Port for telescope to use (defaults to server port) */\n\tport?: number;\n\t/** Path prefix for telescope UI (default: /__telescope) */\n\tpath?: string;\n\t/** Ignore patterns for telescope (e.g., ['/health', '/metrics']) */\n\tignore?: string[];\n\t/** Record request/response bodies (default: true) */\n\trecordBody?: boolean;\n\t/** Maximum entries to keep in memory (default: 1000) */\n\tmaxEntries?: number;\n\t/** Enable WebSocket for real-time updates (default: true, requires @hono/node-ws for Node.js) */\n\twebsocket?: boolean;\n}\n\nexport interface StudioConfig {\n\t/** Enable/disable studio (default: true in development) */\n\tenabled?: boolean;\n\t/** Path prefix for studio UI (default: /__studio) */\n\tpath?: string;\n\t/** Schema to introspect (default: 'public') */\n\tschema?: string;\n}\n\nexport interface OpenApiConfig {\n\t/** Enable OpenAPI generation (default: true) */\n\tenabled?: boolean;\n\t/** API title */\n\ttitle?: string;\n\t/** API version */\n\tversion?: string;\n\t/** API description */\n\tdescription?: string;\n}\n\nexport interface HooksConfig {\n\t/**\n\t * Path to a module exporting server lifecycle hooks.\n\t * The module should export `beforeSetup` and/or `afterSetup` functions.\n\t *\n\t * @example\n\t * ```typescript\n\t * // src/config/hooks.ts\n\t * import type { Hono } from 'hono';\n\t * import type { Logger } from '@geekmidas/logger';\n\t * import type { EnvironmentParser } from '@geekmidas/envkit';\n\t *\n\t * // Called BEFORE gkm endpoints are registered\n\t * export function beforeSetup(app: Hono, ctx: { envParser: EnvironmentParser; logger: Logger }) {\n\t * app.use('*', cors());\n\t * app.get('/custom/health', (c) => c.json({ status: 'ok' }));\n\t * }\n\t *\n\t * // Called AFTER gkm endpoints are registered\n\t * export function afterSetup(app: Hono, ctx: { envParser: EnvironmentParser; logger: Logger }) {\n\t * app.notFound((c) => c.json({ error: 'Not found' }, 404));\n\t * app.onError((err, c) => c.json({ error: err.message }, 500));\n\t * }\n\t * ```\n\t */\n\tserver?: string;\n}\n\n/** Dokploy deployment configuration */\nexport interface DokployProviderConfig {\n\t/** Dokploy API endpoint (e.g., 'https://dokploy.example.com') */\n\tendpoint: string;\n\t/** Project ID in Dokploy */\n\tprojectId: string;\n\t/** Application ID in Dokploy */\n\tapplicationId: string;\n\t/** Container registry (overrides docker.registry if set) */\n\tregistry?: string;\n\t/** Registry ID in Dokploy (recommended for private registries) */\n\tregistryId?: string;\n}\n\nexport interface ProvidersConfig {\n\taws?: {\n\t\tapiGateway?: {\n\t\t\tv1?: boolean | AWSApiGatewayConfig;\n\t\t\tv2?: boolean | AWSApiGatewayConfig;\n\t\t};\n\t\tlambda?: {\n\t\t\tfunctions?: boolean | AWSLambdaConfig;\n\t\t\tcrons?: boolean | AWSLambdaConfig;\n\t\t};\n\t};\n\tserver?: boolean | ServerConfig;\n\t/** Dokploy deployment configuration */\n\tdokploy?: boolean | DokployProviderConfig;\n}\n\nexport interface GkmConfig {\n\t/**\n\t * What this app is called, and therefore how everything it declares is named.\n\t *\n\t * The same field `sst.config.ts` carries, doing the same job: every physical\n\t * name is `{stage}-{name}-{construct}`, so `Database` in the `production`\n\t * stage of `kitchen-sink` is `production-kitchen-sink-database` on AWS and on\n\t * Dokploy alike. One statement, in the config, rather than a rule each\n\t * provider restates.\n\t *\n\t * `defineWorkspace` has always had this. A single-app config did not, so its\n\t * names fell back to whatever the directory happened to be called — the\n\t * monorepo's package name for the project and the app's for the application,\n\t * two accidents of layout standing in for a decision.\n\t *\n\t * Defaults to the package.json name with any scope stripped, then the\n\t * directory name.\n\t */\n\tname?: string;\n\t/**\n\t * Where the things no construct implies live.\n\t *\n\t * The same block a workspace config carries, and the same split: `db` and\n\t * `storage` are derived from the declared `KyselyDatabase` and\n\t * `ObjectStorage` and are ignored here, while `cache`, `mail`, and `events`\n\t * name a *backend* — where the cache lives, who delivers the mail, which\n\t * broker carries events. A single-app project had no way to say any of that\n\t * before, so its event backend could only be guessed.\n\t */\n\tservices?: ServicesConfig;\n\t/**\n\t * Constructs glob pattern — one glob, every kind.\n\t *\n\t * What reconcile reads to derive the containers a stage needs: a database\n\t * implies Postgres, objects imply MinIO, mail implies Mailpit. A glob per\n\t * kind cannot find a resource, because a declared `ObjectStorage` has no\n\t * kind to be listed under.\n\t *\n\t * @example './src/**\\/*.ts'\n\t */\n\tconstructs: Routes;\n\tproviders?: ProvidersConfig;\n\t/**\n\t * Server lifecycle hooks for customizing the Hono app.\n\t * Allows adding custom routes, middleware, error handlers, etc.\n\t *\n\t * @example\n\t * hooks: {\n\t * server: './src/config/hooks'\n\t * }\n\t */\n\thooks?: HooksConfig;\n\t/**\n\t * Telescope configuration for debugging/monitoring.\n\t * Can be:\n\t * - A string path to a module that exports a Telescope instance (recommended)\n\t * - A boolean to enable/disable with defaults\n\t * - A TelescopeConfig object for inline configuration\n\t */\n\ttelescope?: string | boolean | TelescopeConfig;\n\t/**\n\t * Studio configuration for database browsing.\n\t * Can be:\n\t * - A string path to a module that exports a Studio instance (recommended)\n\t * - A boolean to enable/disable with defaults\n\t * - A StudioConfig object for inline configuration\n\t *\n\t * Requires a database connection configured via services.\n\t */\n\tstudio?: string | boolean | StudioConfig;\n\t/**\n\t * OpenAPI generation configuration.\n\t * Can be:\n\t * - A boolean to enable/disable with defaults (output: ./src/api/openapi.ts)\n\t * - An OpenApiConfig object for customization\n\t *\n\t * When enabled, OpenAPI spec is generated on startup and regenerated on route changes.\n\t *\n\t * @example\n\t * openapi: true\n\t *\n\t * @example\n\t * openapi: {\n\t * output: './src/api/openapi.ts',\n\t * title: 'My API',\n\t * version: '1.0.0',\n\t * }\n\t */\n\topenapi?: boolean | OpenApiConfig;\n\t/** Runtime to use for dev server (default: 'node') */\n\truntime?: Runtime;\n\t/**\n\t * Environment file(s) to load for development.\n\t * Can be:\n\t * - A string path to a single env file (e.g., '.env.local')\n\t * - An array of paths to load in order (later files override earlier)\n\t * - Defaults to '.env' if not specified\n\t *\n\t * @example\n\t * env: '.env.local'\n\t *\n\t * @example\n\t * env: ['.env', '.env.local']\n\t */\n\tenv?: string | string[];\n\t/**\n\t * Docker deployment configuration.\n\t * Used by `gkm docker` and `gkm prepack` commands.\n\t *\n\t * @example\n\t * docker: {\n\t * registry: 'ghcr.io/myorg',\n\t * imageName: 'my-api',\n\t * compose: {\n\t * services: ['postgres', 'redis']\n\t * }\n\t * }\n\t */\n\tdocker?: DockerConfig;\n\t/**\n\t * Where this app deploys, and what the target needs to know.\n\t *\n\t * Absent until now, which meant a single-app project could not configure a\n\t * Dokploy deploy at all: the wrap that turns one into a workspace hardcoded\n\t * `{ default: 'dokploy' }` and dropped everything else, so there was no\n\t * endpoint, no registry and no domain — and `resolveHost` failed with \"no\n\t * domain configured for stage\" on a config that had nowhere to put one.\n\t *\n\t * The same shape a workspace config uses, so moving from one to the other is\n\t * a rename rather than a rewrite.\n\t */\n\tdeploy?: import('./workspace/types').DeployConfig;\n}\n\nexport interface BuildOptions {\n\tprovider?: MainProvider;\n\t// Legacy support - will be deprecated\n\tproviders?: LegacyProvider[];\n\tenableOpenApi?: boolean;\n\t/** Build for production (no dev tools, bundled output) */\n\tproduction?: boolean;\n\t/** Skip bundling step in production build */\n\tskipBundle?: boolean;\n\t/** Stage for secrets injection (e.g., 'production', 'staging') */\n\tstage?: string;\n\t/**\n\t * When true, optional environment variables (those with `.optional()` or\n\t * `.default()` in the envParser) are suffixed with `?` within each\n\t * construct's `environment` array in the manifest (e.g. `PORT?`).\n\t * Defaults to false.\n\t */\n\tmarkOptional?: boolean;\n}\n\n/** Result from build command when secrets are injected */\nexport interface BuildResult {\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\n// The deployment manifest types are a shared, dependency-free data contract\n// in `@geekmidas/manifest`, re-exported here for back-compat with existing\n// `@geekmidas/cli` imports.\nexport type {\n\tCronInfo,\n\tFunctionInfo,\n\tManifest,\n\tQueueInfo,\n\tRouteInfo,\n\tSubscriberInfo,\n\tTopicInfo,\n} from '@geekmidas/manifest';\n","import { relative } from 'node:path';\nimport type { Construct } from '@geekmidas/constructs';\nimport fg from 'fast-glob';\nimport kebabCase from 'lodash.kebabcase';\nimport type { BuildContext } from '../build/types';\nimport {\n\tisPartitionedRoutes,\n\ttype LegacyProvider,\n\ttype Routes,\n} from '../types';\n\n/**\n * Zod v4 maintains a process-wide registry of schemas registered via\n * `.meta({ id })`. When we cache-bust user module imports (by appending a\n * `?t=timestamp` query), those modules re-execute and try to register the\n * same ids again, which throws `ID X already exists in the registry`.\n *\n * Clearing the registry in the CLI process is safe: the user's running\n * server lives in a subprocess and has its own isolated registry.\n *\n * Exported for tests.\n */\nexport function clearZodGlobalRegistry(): void {\n\tconst registry = (\n\t\tglobalThis as { __zod_globalRegistry?: { clear?: () => void } }\n\t).__zod_globalRegistry;\n\tif (registry && typeof registry.clear === 'function') {\n\t\tregistry.clear();\n\t}\n}\n\nexport interface GeneratorOptions {\n\tprovider?: LegacyProvider;\n\t[key: string]: any;\n}\n\nexport abstract class ConstructGenerator<T extends Construct, R = void> {\n\tabstract isConstruct(value: any): value is T;\n\n\tstatic async build<T extends Construct, R = void>(\n\t\tcontext: BuildContext,\n\t\toutputDir: string,\n\t\tgenerator: ConstructGenerator<T, R>,\n\t\tpatterns?: Routes,\n\t\toptions?: GeneratorOptions,\n\t): Promise<R> {\n\t\tconst constructs = await generator.load(patterns);\n\t\treturn generator.build(context, constructs, outputDir, options);\n\t}\n\n\tabstract build(\n\t\tcontext: BuildContext,\n\t\tconstructs: GeneratedConstruct<T>[],\n\t\toutputDir: string,\n\t\toptions?: GeneratorOptions,\n\t): Promise<R>;\n\n\tasync load(\n\t\tpatterns?: Routes,\n\t\tcwd = process.cwd(),\n\t\tbustCache = false,\n\t): Promise<GeneratedConstruct<T>[]> {\n\t\tconst logger = console;\n\n\t\t// Extract glob patterns and optional partition function\n\t\tlet globPatterns: string[];\n\t\tlet partitionFn: ((filepath: string) => string) | undefined;\n\n\t\tif (isPartitionedRoutes(patterns)) {\n\t\t\tglobPatterns = Array.isArray(patterns.paths)\n\t\t\t\t? patterns.paths\n\t\t\t\t: [patterns.paths];\n\t\t\tpartitionFn = patterns.partition;\n\t\t} else {\n\t\t\tglobPatterns = Array.isArray(patterns)\n\t\t\t\t? patterns\n\t\t\t\t: patterns\n\t\t\t\t\t? [patterns]\n\t\t\t\t\t: [];\n\t\t}\n\n\t\t// Find all files\n\t\tconst files = fg.stream(globPatterns, {\n\t\t\tcwd,\n\t\t\tabsolute: true,\n\t\t});\n\n\t\t// Cache-busted re-imports re-execute user modules, which would try to\n\t\t// re-register Zod schemas with the same `.meta({ id })`. Clear the\n\t\t// global registry once per load to prevent duplicate-id errors.\n\t\tif (bustCache) {\n\t\t\tclearZodGlobalRegistry();\n\t\t}\n\n\t\t// Load constructs\n\t\tconst constructs: GeneratedConstruct<T>[] = [];\n\n\t\tfor await (const f of files) {\n\t\t\ttry {\n\t\t\t\tconst file = f.toString();\n\t\t\t\t// Append cache-busting query param to force re-import of changed modules\n\t\t\t\tconst importPath = bustCache ? `${file}?t=${Date.now()}` : file;\n\t\t\t\tconst module = await import(importPath);\n\n\t\t\t\t// Compute partition name for this file (if partition function provided)\n\t\t\t\tconst partition = partitionFn ? partitionFn(file) : undefined;\n\n\t\t\t\t// Check all exports for constructs\n\t\t\t\tfor (const [key, construct] of Object.entries(module)) {\n\t\t\t\t\tif (this.isConstruct(construct)) {\n\t\t\t\t\t\tconstructs.push({\n\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\tname: kebabCase(key),\n\t\t\t\t\t\t\tconstruct,\n\t\t\t\t\t\t\tpath: {\n\t\t\t\t\t\t\t\tabsolute: file,\n\t\t\t\t\t\t\t\trelative: relative(process.cwd(), file),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tpartition,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error as Error;\n\t\t\t\tlogger.error(`Failed to load ${f}: ${err.message}`);\n\t\t\t\tif (err.stack) {\n\t\t\t\t\tlogger.error(err.stack);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\treturn constructs;\n\t}\n}\n\nexport interface GeneratedConstruct<T extends Construct> {\n\tkey: string;\n\tname: string;\n\tconstruct: T;\n\tpath: {\n\t\tabsolute: string;\n\t\trelative: string;\n\t};\n\t/** Partition name assigned by the partition function, if configured. */\n\tpartition?: string;\n}\n","/**\n * Discovery — one glob, one structural test.\n *\n * A glob per kind is the same specialness the construct model removes, and\n * resources have no kind to be listed under: a declared `ObjectStorage` would\n * never be found by `routes:` or `crons:`. So there is one `constructs` glob,\n * every export of every matching module is inspected, and the per-kind\n * `isConstruct` checks collapse into a single question — does it have an id and\n * can it declare?\n *\n * Structural rather than `instanceof` on purpose. A construct from a different\n * copy of `@geekmidas/constructs` — a linked workspace, two versions in a\n * lockfile — is still a construct, and `instanceof` is exactly the check that\n * says otherwise.\n */\n\nimport { relative } from 'node:path';\nimport {\n\tassertDerivations,\n\ttype ConstructManifest,\n\tcanonicalId,\n\ttype Declaration,\n} from '@geekmidas/manifest';\nimport fg from 'fast-glob';\nimport { clearZodGlobalRegistry } from '../generators/Generator';\n\n/** Matches the rest of the CLI, which logs through `console` directly. */\nconst logger = console;\n\n/** The construct face discovery needs: an id, and the ability to declare. */\ninterface Declarable {\n\tid: string;\n\tdeclare(): Declaration[];\n}\n\n/**\n * Whether a module export is a construct.\n *\n * Deliberately loose about what else it carries — a construct that also has a\n * `service` is still a construct, and requiring one would exclude the kinds that\n * own no address.\n */\nexport function isDeclarable(value: unknown): value is Declarable {\n\tif (typeof value !== 'object' || value === null) return false;\n\n\tconst candidate = value as Partial<Declarable>;\n\n\treturn (\n\t\ttypeof candidate.id === 'string' &&\n\t\tcandidate.id.length > 0 &&\n\t\ttypeof candidate.declare === 'function'\n\t);\n}\n\nexport interface DiscoverOptions {\n\t/** The glob(s) to search. One pattern, not one per kind. */\n\tpatterns: string | readonly string[];\n\tcwd?: string;\n\t/**\n\t * Re-import changed modules, for the dev watcher.\n\t *\n\t * A single glob makes discovery more central, not less: a new construct file\n\t * has to be picked up on `add`, not only on `change`.\n\t */\n\tbustCache?: boolean;\n\t/**\n\t * Filled in with where each construct was found, when provided.\n\t *\n\t * Discovery is the only thing that knows: it globbed the files and imported\n\t * them. Everything downstream that needs to *write* an import — the build,\n\t * generating a server entry that reads a surface's logger — would otherwise\n\t * have to be told the path in config, which is the string this removes.\n\t *\n\t * An out-parameter rather than a second return value so that every existing\n\t * caller, which wants the manifest and nothing else, is unchanged.\n\t */\n\tsources?: Record<string, ConstructSource>;\n}\n\n/** Where a construct was declared, and under what name. */\nexport interface ConstructSource {\n\t/** Absolute path to the module that exported it. */\n\tfile: string;\n\t/** The name it was exported as — what an import statement has to say. */\n\texportName: string;\n}\n\n/**\n * Build the construct manifest for a project.\n *\n * Runs the manifest's own validation before returning, so an app is wrong here\n * — where the error names a file — rather than at deploy.\n *\n * @throws {DuplicateConstruct} when two exports claim one id.\n */\nexport async function discover(\n\toptions: DiscoverOptions,\n): Promise<ConstructManifest> {\n\tconst {\n\t\tpatterns,\n\t\tcwd = process.cwd(),\n\t\tbustCache = false,\n\t\tsources: out,\n\t} = options;\n\n\t// Re-importing user modules re-executes them, and Zod v4 throws on a\n\t// re-registered `.meta({ id })`. Same reason the generators clear it.\n\tif (bustCache) clearZodGlobalRegistry();\n\n\tconst manifest: Record<string, Declaration> = {};\n\t/** Which file declared each id, for the error when two of them do. */\n\tconst sources: Record<string, string> = {};\n\t/**\n\t * Constructs already seen, by identity.\n\t *\n\t * A re-export is the *same object* reached through a second file — ESM\n\t * bindings are live, so `export * from './database.js'` hands back the\n\t * binding rather than a copy. Without this, a barrel file made every\n\t * construct in it appear declared twice, and `constructs/index.ts` is the\n\t * first thing anyone writes in a shared folder.\n\t *\n\t * Identity rather than file is also the more honest rule: what may not\n\t * happen twice is two *different* constructs claiming one id, and that is\n\t * still an error below.\n\t */\n\tconst seen = new WeakSet<object>();\n\n\tconst globs = Array.isArray(patterns) ? [...patterns] : [patterns as string];\n\tconst files = fg.stream(globs, { cwd, absolute: true });\n\n\t/** Matched files, for the diagnostic below. */\n\tlet matched = 0;\n\n\tfor await (const found of files) {\n\t\tmatched++;\n\t\tconst file = found.toString();\n\t\tconst module = await import(bustCache ? `${file}?t=${Date.now()}` : file);\n\n\t\tfor (const [exportName, exported] of Object.entries(module)) {\n\t\t\tif (!isDeclarable(exported)) continue;\n\n\t\t\t// The same construct, reached again through a re-export. Skipped\n\t\t\t// rather than re-declared: it has already claimed its id, from the\n\t\t\t// file that defined it.\n\t\t\tif (seen.has(exported as object)) continue;\n\t\t\tseen.add(exported as object);\n\n\t\t\tfor (const declaration of exported.declare()) {\n\t\t\t\t// Canonicalise here too: a construct built by hand rather than through\n\t\t\t\t// a constructor is still subject to the same identity rule.\n\t\t\t\tconst id = canonicalId(declaration.id);\n\t\t\t\tconst source = relative(cwd, file);\n\n\t\t\t\tconst claimed = sources[id];\n\t\t\t\tif (claimed !== undefined && claimed !== source) {\n\t\t\t\t\tthrow new DuplicateConstruct(id, [claimed, source]);\n\t\t\t\t}\n\n\t\t\t\tmanifest[id] = { ...declaration, id };\n\t\t\t\tsources[id] = source;\n\t\t\t\tif (out) out[id] = { file, exportName };\n\t\t\t}\n\t\t}\n\t}\n\n\t// Reference integrity before anyone reads it — a schema tenant naming a\n\t// database that was deleted is a manifest error, not a reconcile failure.\n\tassertDerivations(manifest);\n\n\twarnIfNothingFound(globs, matched, Object.keys(manifest).length);\n\n\treturn manifest;\n}\n\n/**\n * Say so when a glob was configured and found nothing.\n *\n * The glob is not a filter over declarations already known — it is *how they\n * are found*, by importing what it matches. So a file it misses is a\n * declaration that does not exist, and every symptom of that appears somewhere\n * other than the cause: no container starts, no env key is written, and the\n * application fails on first use against a resource it can see in its own\n * source.\n *\n * Nothing reported it before, because an unmatched file is indistinguishable\n * from a file nobody wrote. These two cases are the ones that are *not*\n * ambiguous — a glob was configured, so something was expected.\n *\n * Silent when no glob is configured at all: that is a project which has not\n * adopted constructs, and has nothing to be missing.\n */\nfunction warnIfNothingFound(\n\tglobs: readonly string[],\n\tmatched: number,\n\tdeclared: number,\n): void {\n\tif (globs.length === 0) return;\n\n\tif (matched === 0) {\n\t\tlogger.warn(\n\t\t\t`\\n⚠️ The constructs glob matched no files, so nothing was declared.\\n` +\n\t\t\t\t` Patterns: ${globs.join(', ')}\\n` +\n\t\t\t\t` Nothing will be provisioned — no containers, no env keys, no ` +\n\t\t\t\t`resources.\\n` +\n\t\t\t\t` A common cause is depth: '*.ts' matches one level, '**/*.ts' ` +\n\t\t\t\t`matches any.`,\n\t\t);\n\t\treturn;\n\t}\n\n\tif (declared === 0) {\n\t\tlogger.warn(\n\t\t\t`\\n⚠️ The constructs glob matched ${matched} file(s) but none declared ` +\n\t\t\t\t`anything.\\n` +\n\t\t\t\t` Patterns: ${globs.join(', ')}\\n` +\n\t\t\t\t` Discovery keeps exports with an 'id' that can 'declare()'. A ` +\n\t\t\t\t`construct that is\\n` +\n\t\t\t\t` built but never exported is invisible to it.`,\n\t\t);\n\t}\n}\n\n/** Two constructs claiming one id. */\nexport class DuplicateConstruct extends Error {\n\t/** The id claimed twice. */\n\treadonly id: string;\n\t/** The files that claimed it. */\n\treadonly sources: readonly string[];\n\n\tconstructor(id: string, sources: readonly string[]) {\n\t\tsuper('Two constructs declare the same id');\n\t\tthis.name = 'DuplicateConstruct';\n\t\tthis.id = id;\n\t\tthis.sources = sources;\n\t}\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { AppsRecord, Framework, GkmConfig, InferredWorkspaceConfig, LoadedConfig, NormalizedWorkspace, Routes, WorkspaceConfig, WorkspaceInput } from "./types-C-arnQ1r.mjs";
1
+ import { AppsRecord, Framework, GkmConfig, InferredWorkspaceConfig, LoadedConfig, NormalizedWorkspace, Routes, WorkspaceConfig, WorkspaceInput } from "./types-BLSNdrk0.cjs";
2
2
 
3
3
  //#region src/workspace/publicEnv.d.ts
4
4
 
@@ -172,4 +172,4 @@ declare function getEndpointForStage(dokployConfig: {
172
172
  //# sourceMappingURL=index.d.ts.map
173
173
  //#endregion
174
174
  export { PUBLIC_ENV_PREFIXES, allConstructGlobs, appConstructGlobs, constructPatterns, defineWorkspace, getAppBuildOrder, getAppGkmConfig, getDependencyEnvVars, getEndpointForStage, getPublicEnvPrefix, normalizeWorkspace, processConfig, stripPublicPrefix, workspaceConstructGlobs, wrapSingleAppAsWorkspace };
175
- //# sourceMappingURL=index-BThUzEIp.d.mts.map
175
+ //# sourceMappingURL=index-BGGdjueF.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-BGGdjueF.d.cts","names":[],"sources":["../src/workspace/publicEnv.ts","../src/workspace/index.ts"],"sourcesContent":[],"mappings":";;;;;;AASA;AAoBA;AAoBA;;;cAxCa;AC0Hb;;;;;;;AAE0B;AA8B1B;;;;AAGsB;AA8CtB;AAAwC,iBDvLxB,kBAAA,CCuLwB,SAAA,CAAA,EDvLO,SCuLP,CAAA,EAAA,MAAA;;;AAGlB;AAqEtB;AAA6B,iBD3Ob,iBAAA,CC2Oa,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;;;;;;;;AAzJ7B;;;;;;;AAE0B;AA8B1B;;;;AAGsB;AA8CtB;;;;AAGsB;AAqEtB;;;;;AAGe;AA6Bf;AAmBA;AAwBA;AAaA;AAiBA;;;;AAGY;AAkCZ;AAmCA;;;;AAIS;AAgDO,iBA9XA,eAgYqB,CAAA,oBAhYe,UAgYT,CAAA,CAAA,MAAA,EA/XlC,cA+XkC,CA/XnB,KA+XmB,CAAA,CAAA,EA9XxC,uBA8XwC,CA9XhB,KA8XgB,CAAA;;;;iBAhW3B,kBAAA,SACP,+BAEN;;;;;iBA8Ca,wBAAA,SACP,yBAEN;;;;;iBAqEa,aAAA,SACP,YAAY,+BAElB;;;;;;;iBA6Ba,uBAAA,YACJ;;;;;;;;;;;;iBAkBI,iBAAA,YACJ;;;;;;;iBAuBI,iBAAA,YAA6B;;iBAa7B,iBAAA,SAA0B;;;;;iBAiB1B,eAAA,YACJ,uCAET;;;;;iBAkCa,gBAAA,YAA4B;;;;;;;;;;iBAmC5B,oBAAA,YACJ,2DAGT;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgDa,mBAAA;;cAEqB"}
@@ -1,4 +1,4 @@
1
- import { AppsRecord, Framework, GkmConfig, InferredWorkspaceConfig, LoadedConfig, NormalizedWorkspace, Routes, WorkspaceConfig, WorkspaceInput } from "./types-abJQuk7m.cjs";
1
+ import { AppsRecord, Framework, GkmConfig, InferredWorkspaceConfig, LoadedConfig, NormalizedWorkspace, Routes, WorkspaceConfig, WorkspaceInput } from "./types-C3POXhw3.mjs";
2
2
 
3
3
  //#region src/workspace/publicEnv.d.ts
4
4
 
@@ -172,4 +172,4 @@ declare function getEndpointForStage(dokployConfig: {
172
172
  //# sourceMappingURL=index.d.ts.map
173
173
  //#endregion
174
174
  export { PUBLIC_ENV_PREFIXES, allConstructGlobs, appConstructGlobs, constructPatterns, defineWorkspace, getAppBuildOrder, getAppGkmConfig, getDependencyEnvVars, getEndpointForStage, getPublicEnvPrefix, normalizeWorkspace, processConfig, stripPublicPrefix, workspaceConstructGlobs, wrapSingleAppAsWorkspace };
175
- //# sourceMappingURL=index-CHB4tSzX.d.cts.map
175
+ //# sourceMappingURL=index-DEbaleny.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-DEbaleny.d.mts","names":[],"sources":["../src/workspace/publicEnv.ts","../src/workspace/index.ts"],"sourcesContent":[],"mappings":";;;;;;AASA;AAoBA;AAoBA;;;cAxCa;AC0Hb;;;;;;;AAE0B;AA8B1B;;;;AAGsB;AA8CtB;AAAwC,iBDvLxB,kBAAA,CCuLwB,SAAA,CAAA,EDvLO,SCuLP,CAAA,EAAA,MAAA;;;AAGlB;AAqEtB;AAA6B,iBD3Ob,iBAAA,CC2Oa,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;;;;;;;;AAzJ7B;;;;;;;AAE0B;AA8B1B;;;;AAGsB;AA8CtB;;;;AAGsB;AAqEtB;;;;;AAGe;AA6Bf;AAmBA;AAwBA;AAaA;AAiBA;;;;AAGY;AAkCZ;AAmCA;;;;AAIS;AAgDO,iBA9XA,eAgYqB,CAAA,oBAhYe,UAgYT,CAAA,CAAA,MAAA,EA/XlC,cA+XkC,CA/XnB,KA+XmB,CAAA,CAAA,EA9XxC,uBA8XwC,CA9XhB,KA8XgB,CAAA;;;;iBAhW3B,kBAAA,SACP,+BAEN;;;;;iBA8Ca,wBAAA,SACP,yBAEN;;;;;iBAqEa,aAAA,SACP,YAAY,+BAElB;;;;;;;iBA6Ba,uBAAA,YACJ;;;;;;;;;;;;iBAkBI,iBAAA,YACJ;;;;;;;iBAuBI,iBAAA,YAA6B;;iBAa7B,iBAAA,SAA0B;;;;;iBAiB1B,eAAA,YACJ,uCAET;;;;;iBAkCa,gBAAA,YAA4B;;;;;;;;;;iBAmC5B,oBAAA,YACJ,2DAGT;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgDa,mBAAA;;cAEqB"}