@geekmidas/cli 10.0.0-alpha.44 → 10.0.0-alpha.46

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 (80) hide show
  1. package/README.md +144 -183
  2. package/dist/{OpenApiTsGenerator-B4Kj_qNU.cjs → OpenApiTsGenerator-BN0PIZxd.cjs} +17 -71
  3. package/dist/OpenApiTsGenerator-BN0PIZxd.cjs.map +1 -0
  4. package/dist/{OpenApiTsGenerator-CEh3EKN3.mjs → OpenApiTsGenerator-BzIGy8KE.mjs} +17 -71
  5. package/dist/OpenApiTsGenerator-BzIGy8KE.mjs.map +1 -0
  6. package/dist/{config-CJ-PAb7M.cjs → config-CsYSyUya.cjs} +3 -3
  7. package/dist/{config-CJ-PAb7M.cjs.map → config-CsYSyUya.cjs.map} +1 -1
  8. package/dist/{config-5watB1D0.mjs → config-D8J5rrTb.mjs} +3 -3
  9. package/dist/{config-5watB1D0.mjs.map → config-D8J5rrTb.mjs.map} +1 -1
  10. package/dist/config.cjs +2 -2
  11. package/dist/config.d.cts +2 -2
  12. package/dist/config.d.mts +2 -2
  13. package/dist/config.mjs +2 -2
  14. package/dist/{discover-BLhf7V_J.mjs → discover-Cf2jAcgF.mjs} +4 -56
  15. package/dist/discover-Cf2jAcgF.mjs.map +1 -0
  16. package/dist/{discover-B0f_kuNN.cjs → discover-DXNviAh-.cjs} +3 -79
  17. package/dist/discover-DXNviAh-.cjs.map +1 -0
  18. package/dist/{emit-Bzq7JsMP.mjs → emit-C1e8qD08.mjs} +12 -43
  19. package/dist/emit-C1e8qD08.mjs.map +1 -0
  20. package/dist/{emit-BdoPFjZf.cjs → emit-Dmc1nvdS.cjs} +11 -54
  21. package/dist/emit-Dmc1nvdS.cjs.map +1 -0
  22. package/dist/{github-D5MthUlR.mjs → github-BfpadQWQ.mjs} +3 -3
  23. package/dist/{github-D5MthUlR.mjs.map → github-BfpadQWQ.mjs.map} +1 -1
  24. package/dist/{github-BAr8WGkR.cjs → github-BrI1tv6u.cjs} +3 -3
  25. package/dist/{github-BAr8WGkR.cjs.map → github-BrI1tv6u.cjs.map} +1 -1
  26. package/dist/{index-B3PeGOQp.d.cts → index-BQhJXpaQ.d.mts} +2 -2
  27. package/dist/{index-B3PeGOQp.d.cts.map → index-BQhJXpaQ.d.mts.map} +1 -1
  28. package/dist/{index-BOyaGcCm.d.mts → index-DHJePE0y.d.cts} +2 -2
  29. package/dist/{index-BOyaGcCm.d.mts.map → index-DHJePE0y.d.cts.map} +1 -1
  30. package/dist/index.cjs +395 -660
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.mjs +397 -662
  33. package/dist/index.mjs.map +1 -1
  34. package/dist/{migrate-BRklCm7b.mjs → migrate-B3opyC3o.mjs} +5 -5
  35. package/dist/{migrate-BRklCm7b.mjs.map → migrate-B3opyC3o.mjs.map} +1 -1
  36. package/dist/{migrate-PJi4b1ga.cjs → migrate-Dp5HFzuQ.cjs} +5 -5
  37. package/dist/{migrate-PJi4b1ga.cjs.map → migrate-Dp5HFzuQ.cjs.map} +1 -1
  38. package/dist/openapi.cjs +2 -2
  39. package/dist/openapi.d.cts +1 -1
  40. package/dist/openapi.d.mts +1 -1
  41. package/dist/openapi.mjs +2 -2
  42. package/dist/{ready-3RfHxdA0.cjs → ready-L7I68yXf.cjs} +67 -8
  43. package/dist/ready-L7I68yXf.cjs.map +1 -0
  44. package/dist/{ready-ZqOuNvKD.mjs → ready-V6Ou6-B6.mjs} +49 -8
  45. package/dist/ready-V6Ou6-B6.mjs.map +1 -0
  46. package/dist/reconcile/public.cjs +2 -4
  47. package/dist/reconcile/public.d.cts +6 -10
  48. package/dist/reconcile/public.d.cts.map +1 -1
  49. package/dist/reconcile/public.d.mts +6 -10
  50. package/dist/reconcile/public.d.mts.map +1 -1
  51. package/dist/reconcile/public.mjs +3 -3
  52. package/dist/{types-0R124hrY.d.mts → types-BbrNu83u.d.mts} +14 -109
  53. package/dist/{types-0R124hrY.d.mts.map → types-BbrNu83u.d.mts.map} +1 -1
  54. package/dist/{types-DfdYe31u.d.cts → types-DNqEsctD.d.cts} +14 -109
  55. package/dist/{types-DfdYe31u.d.cts.map → types-DNqEsctD.d.cts.map} +1 -1
  56. package/dist/vitest.cjs +1 -1
  57. package/dist/vitest.mjs +1 -1
  58. package/dist/workspace/index.cjs +1 -1
  59. package/dist/workspace/index.d.cts +2 -2
  60. package/dist/workspace/index.d.mts +2 -2
  61. package/dist/workspace/index.mjs +1 -1
  62. package/dist/{workspace-BKytVCzb.mjs → workspace-CHEC80sp.mjs} +6 -7
  63. package/dist/workspace-CHEC80sp.mjs.map +1 -0
  64. package/dist/{workspace-dV6f-3vv.cjs → workspace-D9z2ymBW.cjs} +11 -12
  65. package/dist/workspace-D9z2ymBW.cjs.map +1 -0
  66. package/dist/{workspace-RYSYtHP7.cjs → workspace-DEibcoBe.cjs} +11 -25
  67. package/dist/{workspace-RYSYtHP7.cjs.map → workspace-DEibcoBe.cjs.map} +1 -1
  68. package/dist/{workspace-DOmIhiau.mjs → workspace-DNzWgM1J.mjs} +11 -25
  69. package/dist/{workspace-DOmIhiau.mjs.map → workspace-DNzWgM1J.mjs.map} +1 -1
  70. package/package.json +14 -14
  71. package/dist/OpenApiTsGenerator-B4Kj_qNU.cjs.map +0 -1
  72. package/dist/OpenApiTsGenerator-CEh3EKN3.mjs.map +0 -1
  73. package/dist/discover-B0f_kuNN.cjs.map +0 -1
  74. package/dist/discover-BLhf7V_J.mjs.map +0 -1
  75. package/dist/emit-BdoPFjZf.cjs.map +0 -1
  76. package/dist/emit-Bzq7JsMP.mjs.map +0 -1
  77. package/dist/ready-3RfHxdA0.cjs.map +0 -1
  78. package/dist/ready-ZqOuNvKD.mjs.map +0 -1
  79. package/dist/workspace-BKytVCzb.mjs.map +0 -1
  80. package/dist/workspace-dV6f-3vv.cjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  import { n as __require, t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
3
- import { c as isPartitionedRoutes, i as ConstructGenerator, t as discover } from "./discover-BLhf7V_J.mjs";
4
- import { d as parseModuleConfig, f as appKey, l as loadWorkspaceConfig, n as NotABackendApp, o as loadAppConfig, p as derivedApps, r as NotInAnApp, s as loadConfig, t as ConfigNotFound, u as loadWorkspaceSettings } from "./config-5watB1D0.mjs";
5
- import { D as assertDeployedStage, E as TEST_STAGE, O as deployedProblems, T as InvalidStages, a as getAppBuildOrder, b as getDeployTargetError, k as stageProblems, m as stripPublicPrefix, o as getAppGkmConfig, p as getPublicEnvPrefix, t as allConstructGlobs, x as isDeployTargetSupported } from "./workspace-DOmIhiau.mjs";
3
+ import { i as ConstructGenerator, t as discover } from "./discover-Cf2jAcgF.mjs";
4
+ import { d as parseModuleConfig, f as appKey, l as loadWorkspaceConfig, n as NotABackendApp, o as loadAppConfig, p as derivedApps, r as NotInAnApp, s as loadConfig, t as ConfigNotFound, u as loadWorkspaceSettings } from "./config-D8J5rrTb.mjs";
5
+ import { D as assertDeployedStage, E as TEST_STAGE, O as deployedProblems, T as InvalidStages, a as getAppBuildOrder, b as getDeployTargetError, k as stageProblems, m as stripPublicPrefix, o as getAppGkmConfig, p as getPublicEnvPrefix, t as allConstructGlobs, x as isDeployTargetSupported } from "./workspace-DNzWgM1J.mjs";
6
6
  import { a as withCustomSecret, i as toEmbeddableSecrets, n as maskPassword, t as initStageSecrets } from "./storage-BzaUxmqe.mjs";
7
7
  import { a as getKeyPath, i as FileSecretsStore, n as secretsStoreFor } from "./store-DeGATCS-.mjs";
8
- import { C as createEntryWrapper, D as loadSecretsForApp, E as loadEnvFiles, O as prepareEntryCredentials, S as createCredentialsPreload, T as isPortAvailable, _ as primaryPortKey, a as reconcileWorkspace, b as providerOf, c as LOCAL_CA_PATH, d as postgresDatabaseNames, f as quoteIdentifier, g as fakeKey, h as rootSite, i as derivedContainers, l as forgetState, m as resourceName, n as backendsOf, p as pgClient, r as constructGlobs, s as COMPOSE_PATH, t as FAKE_ENV, u as applyPostgres, v as cacheBackendFor, w as findAvailablePort, x as storageBackendFor, y as eventsBackendFor } from "./workspace-BKytVCzb.mjs";
9
- import { n as EndpointGenerator, r as runtimeFor } from "./OpenApiTsGenerator-CEh3EKN3.mjs";
8
+ import { C as createEntryWrapper, D as loadSecretsForApp, E as loadEnvFiles, O as prepareEntryCredentials, S as createCredentialsPreload, T as isPortAvailable, _ as primaryPortKey, a as reconcileWorkspace, b as providerOf, c as LOCAL_CA_PATH, d as postgresDatabaseNames, f as quoteIdentifier, g as fakeKey, h as rootSite, i as derivedContainers, l as forgetState, m as resourceName, n as backendsOf$1, p as pgClient, r as constructGlobs, s as COMPOSE_PATH, t as FAKE_ENV, u as applyPostgres, v as cacheBackendFor, w as findAvailablePort, x as storageBackendFor, y as eventsBackendFor } from "./workspace-CHEC80sp.mjs";
9
+ import { n as EndpointGenerator, r as runtimeFor } from "./OpenApiTsGenerator-BzIGy8KE.mjs";
10
10
  import { generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi.mjs";
11
- import { a as writeTestHarness, c as driversFor, i as TEST_MANIFEST_ENV, n as TEST_READY_FILE, o as SubscriberGenerator, s as cacheBackendsIn, t as TEST_READY_ENV } from "./ready-ZqOuNvKD.mjs";
12
- import { i as withRoutes, r as withCompute } from "./emit-Bzq7JsMP.mjs";
11
+ import { a as writeTestHarness, c as driversFor, i as TEST_MANIFEST_ENV, n as TEST_READY_FILE, o as SubscriberGenerator, s as cacheBackendsIn, t as TEST_READY_ENV } from "./ready-V6Ou6-B6.mjs";
12
+ import { n as withCompute, r as withRoutes, t as manifestModule } from "./emit-C1e8qD08.mjs";
13
13
  import { createRequire } from "node:module";
14
14
  import { Command } from "commander";
15
15
  import { stdin, stdout } from "node:process";
16
16
  import * as readline from "node:readline/promises";
17
17
  import { copyFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
18
- import { mkdir, readFile, writeFile } from "node:fs/promises";
18
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
19
19
  import { homedir } from "node:os";
20
20
  import { basename, dirname, isAbsolute, join, parse, relative, resolve } from "node:path";
21
21
  import { execFile, execSync, spawn } from "node:child_process";
@@ -40,7 +40,7 @@ import { Queue } from "@geekmidas/constructs/queue";
40
40
  import { Topic } from "@geekmidas/constructs/topic";
41
41
  import { lookup } from "node:dns/promises";
42
42
  //#region package.json
43
- var version = "10.0.0-alpha.44";
43
+ var version = "10.0.0-alpha.46";
44
44
  //#endregion
45
45
  //#region src/auth/credentials.ts
46
46
  /**
@@ -174,7 +174,7 @@ async function getHostingerToken(options) {
174
174
  }
175
175
  //#endregion
176
176
  //#region src/auth/index.ts
177
- const logger$13 = console;
177
+ const logger$12 = console;
178
178
  /**
179
179
  * Validate Dokploy token by making a test API call
180
180
  */
@@ -241,52 +241,52 @@ async function prompt$1(message, hidden = false) {
241
241
  async function loginCommand(options) {
242
242
  const { provider, token: providedToken, endpoint: providedEndpoint } = options;
243
243
  if (provider === "dokploy") {
244
- logger$13.log("\n🔐 Logging in to Dokploy...\n");
244
+ logger$12.log("\n🔐 Logging in to Dokploy...\n");
245
245
  let endpoint = providedEndpoint;
246
246
  if (!endpoint) endpoint = await prompt$1("Dokploy URL (e.g., https://dokploy.example.com): ");
247
247
  endpoint = endpoint.replace(/\/$/, "");
248
248
  try {
249
249
  new URL(endpoint);
250
250
  } catch {
251
- logger$13.error("Invalid URL format");
251
+ logger$12.error("Invalid URL format");
252
252
  process.exit(1);
253
253
  }
254
254
  let token = providedToken;
255
255
  if (!token) {
256
- logger$13.log(`\nGenerate a token at: ${endpoint}/settings/profile\n`);
256
+ logger$12.log(`\nGenerate a token at: ${endpoint}/settings/profile\n`);
257
257
  token = await prompt$1("API Token: ", true);
258
258
  }
259
259
  if (!token) {
260
- logger$13.error("Token is required");
260
+ logger$12.error("Token is required");
261
261
  process.exit(1);
262
262
  }
263
- logger$13.log("\nValidating credentials...");
263
+ logger$12.log("\nValidating credentials...");
264
264
  if (!await validateDokployToken(endpoint, token)) {
265
- logger$13.error("\n✗ Invalid credentials. Please check your token and try again.");
265
+ logger$12.error("\n✗ Invalid credentials. Please check your token and try again.");
266
266
  process.exit(1);
267
267
  }
268
268
  await storeDokployCredentials(token, endpoint);
269
- logger$13.log("\n✓ Successfully logged in to Dokploy!");
270
- logger$13.log(` Endpoint: ${endpoint}`);
271
- logger$13.log(` Credentials stored in: ${getCredentialsPath()}`);
272
- logger$13.log("\nYou can now use deploy commands without setting DOKPLOY_API_TOKEN.");
269
+ logger$12.log("\n✓ Successfully logged in to Dokploy!");
270
+ logger$12.log(` Endpoint: ${endpoint}`);
271
+ logger$12.log(` Credentials stored in: ${getCredentialsPath()}`);
272
+ logger$12.log("\nYou can now use deploy commands without setting DOKPLOY_API_TOKEN.");
273
273
  }
274
274
  if (provider === "hostinger") {
275
- logger$13.log("\n🔐 Logging in to Hostinger...\n");
275
+ logger$12.log("\n🔐 Logging in to Hostinger...\n");
276
276
  let token = providedToken;
277
277
  if (!token) {
278
- logger$13.log("\nGenerate a token at: https://hpanel.hostinger.com/profile/api\n");
278
+ logger$12.log("\nGenerate a token at: https://hpanel.hostinger.com/profile/api\n");
279
279
  token = await prompt$1("API Token: ", true);
280
280
  }
281
281
  if (!token) {
282
- logger$13.error("Token is required");
282
+ logger$12.error("Token is required");
283
283
  process.exit(1);
284
284
  }
285
285
  await storeHostingerToken(token);
286
- logger$13.log("\n✓ Hostinger token stored.");
287
- logger$13.log(` Credentials stored in: ${getCredentialsPath()}`);
288
- logger$13.log(" Not validated here: every DNS endpoint is scoped to a domain, so the\n first deploy that touches DNS is where a bad token reports itself.");
289
- logger$13.log("\nYou can now use it as a DNS provider without setting HOSTINGER_API_TOKEN.");
286
+ logger$12.log("\n✓ Hostinger token stored.");
287
+ logger$12.log(` Credentials stored in: ${getCredentialsPath()}`);
288
+ logger$12.log(" Not validated here: every DNS endpoint is scoped to a domain, so the\n first deploy that touches DNS is where a bad token reports itself.");
289
+ logger$12.log("\nYou can now use it as a DNS provider without setting HOSTINGER_API_TOKEN.");
290
290
  }
291
291
  }
292
292
  /**
@@ -295,27 +295,27 @@ async function loginCommand(options) {
295
295
  async function logoutCommand(options) {
296
296
  const { provider = "dokploy" } = options;
297
297
  if (provider === "all") {
298
- if (await removeDokployCredentials()) logger$13.log("\n✓ Logged out from all services");
299
- else logger$13.log("\nNo stored credentials found");
298
+ if (await removeDokployCredentials()) logger$12.log("\n✓ Logged out from all services");
299
+ else logger$12.log("\nNo stored credentials found");
300
300
  return;
301
301
  }
302
302
  if (provider === "dokploy") {
303
- if (await removeDokployCredentials()) logger$13.log("\n✓ Logged out from Dokploy");
304
- else logger$13.log("\nNo Dokploy credentials found");
303
+ if (await removeDokployCredentials()) logger$12.log("\n✓ Logged out from Dokploy");
304
+ else logger$12.log("\nNo Dokploy credentials found");
305
305
  }
306
306
  }
307
307
  /**
308
308
  * Show current login status
309
309
  */
310
310
  async function whoamiCommand() {
311
- logger$13.log("\n📋 Current credentials:\n");
311
+ logger$12.log("\n📋 Current credentials:\n");
312
312
  const dokploy = await getDokployCredentials();
313
313
  if (dokploy) {
314
- logger$13.log(" Dokploy:");
315
- logger$13.log(` Endpoint: ${dokploy.endpoint}`);
316
- logger$13.log(` Token: ${maskToken(dokploy.token)}`);
317
- } else logger$13.log(" Dokploy: Not logged in");
318
- logger$13.log(`\n Credentials file: ${getCredentialsPath()}`);
314
+ logger$12.log(" Dokploy:");
315
+ logger$12.log(` Endpoint: ${dokploy.endpoint}`);
316
+ logger$12.log(` Token: ${maskToken(dokploy.token)}`);
317
+ } else logger$12.log(" Dokploy: Not logged in");
318
+ logger$12.log(`\n Credentials file: ${getCredentialsPath()}`);
319
319
  }
320
320
  /**
321
321
  * Mask a token for display
@@ -325,85 +325,14 @@ function maskToken(token) {
325
325
  return `${token.slice(0, 4)}...${token.slice(-4)}`;
326
326
  }
327
327
  //#endregion
328
- //#region src/build/providerResolver.ts
329
- /**
330
- * Resolves provider configuration from the new simplified system
331
- * to the internal legacy format for backward compatibility
332
- */
333
- function resolveProviders(config, options) {
334
- const providers = [];
335
- let enableOpenApi = options.enableOpenApi || false;
336
- if (options.providers) return {
337
- providers: options.providers,
338
- enableOpenApi
339
- };
340
- if (options.provider) {
341
- const resolvedProviders = resolveMainProvider(options.provider, config.providers);
342
- providers.push(...resolvedProviders.providers);
343
- enableOpenApi = resolvedProviders.enableOpenApi || enableOpenApi;
344
- } else if (config.providers) {
345
- const resolvedProviders = resolveAllConfiguredProviders(config.providers);
346
- providers.push(...resolvedProviders.providers);
347
- enableOpenApi = resolvedProviders.enableOpenApi || enableOpenApi;
348
- } else providers.push("aws-apigatewayv2", "aws-lambda");
349
- return {
350
- providers: [...new Set(providers)],
351
- enableOpenApi
352
- };
353
- }
354
- function resolveMainProvider(mainProvider, providersConfig) {
355
- const providers = [];
356
- let enableOpenApi = false;
357
- if (mainProvider === "aws") {
358
- const awsConfig = providersConfig?.aws;
359
- if (awsConfig?.apiGateway) {
360
- if (isEnabled(awsConfig.apiGateway.v1)) providers.push("aws-apigatewayv1");
361
- if (isEnabled(awsConfig.apiGateway.v2)) providers.push("aws-apigatewayv2");
362
- } else providers.push("aws-apigatewayv2");
363
- if (awsConfig?.lambda) {
364
- if (isEnabled(awsConfig.lambda.functions) || isEnabled(awsConfig.lambda.crons)) providers.push("aws-lambda");
365
- } else providers.push("aws-lambda");
366
- } else if (mainProvider === "server") {
367
- providers.push("server");
368
- const serverConfig = providersConfig?.server;
369
- if (typeof serverConfig === "object" && serverConfig?.enableOpenApi) enableOpenApi = true;
370
- }
371
- return {
372
- providers,
373
- enableOpenApi
374
- };
375
- }
376
- function resolveAllConfiguredProviders(providersConfig) {
377
- const providers = [];
378
- let enableOpenApi = false;
379
- if (providersConfig.aws) {
380
- const awsProviders = resolveMainProvider("aws", providersConfig);
381
- providers.push(...awsProviders.providers);
382
- }
383
- if (providersConfig.server && isEnabled(providersConfig.server)) {
384
- providers.push("server");
385
- if (typeof providersConfig.server === "object" && providersConfig.server.enableOpenApi) enableOpenApi = true;
386
- }
387
- return {
388
- providers,
389
- enableOpenApi
390
- };
391
- }
392
- function isEnabled(config) {
393
- if (config === void 0) return false;
394
- if (typeof config === "boolean") return config;
395
- return config.enabled !== false;
396
- }
397
- //#endregion
398
328
  //#region src/workspace/client-generator.ts
399
329
  /**
400
330
  * Normalize routes to an array of patterns.
401
- * Handles string, string[], and PartitionedRoutes (extracts paths).
331
+ * Handles a glob or a list of them.
402
332
  * @internal Exported for use in dev command
403
333
  */
404
334
  function normalizeRoutes(routes) {
405
335
  if (!routes) return [];
406
- if (isPartitionedRoutes(routes)) return Array.isArray(routes.paths) ? routes.paths : [routes.paths];
407
336
  return Array.isArray(routes) ? routes : [routes];
408
337
  }
409
338
  //#endregion
@@ -422,7 +351,7 @@ function normalizeRoutes(routes) {
422
351
  * trade than one that tells you what to type. Caddy's own `caddy trust` draws
423
352
  * the line in the same place.
424
353
  */
425
- const logger$12 = console;
354
+ const logger$11 = console;
426
355
  const run = promisify(execFile);
427
356
  /**
428
357
  * How each platform is told about a new authority.
@@ -474,15 +403,15 @@ async function trustCommand(options = {}) {
474
403
  const certificate = join(workspace.root, LOCAL_CA_PATH);
475
404
  if (!existsSync(certificate)) throw new NoLocalAuthority(certificate);
476
405
  const { describe, steps } = installation(process.platform, certificate);
477
- logger$12.log(`\n🔐 Trusting the local authority in ${describe}`);
478
- logger$12.log(` ${certificate}\n`);
406
+ logger$11.log(`\n🔐 Trusting the local authority in ${describe}`);
407
+ logger$11.log(` ${certificate}\n`);
479
408
  if (options.dryRun) {
480
- for (const [command, args] of steps) logger$12.log(` ${command} ${args.join(" ")}`);
409
+ for (const [command, args] of steps) logger$11.log(` ${command} ${args.join(" ")}`);
481
410
  return;
482
411
  }
483
- logger$12.log(" This needs an admin password.\n");
412
+ logger$11.log(" This needs an admin password.\n");
484
413
  for (const [command, args] of steps) await run(command, args, { stdio: "inherit" });
485
- logger$12.log("\n✅ Local https addresses are trusted. Restart your browser.\n");
414
+ logger$11.log("\n✅ Local https addresses are trusted. Restart your browser.\n");
486
415
  }
487
416
  /**
488
417
  * Trust the authority if this machine lacks it and its owner agrees.
@@ -499,7 +428,7 @@ async function ensureTrusted(root, url, options = {}) {
499
428
  if (await isTrusted(url)) return;
500
429
  if (!options.assumeYes) {
501
430
  if (!process.stdin.isTTY) {
502
- logger$12.log(" Local https addresses are not trusted here. Run \"gkm trust\", or \"gkm setup --yes\".");
431
+ logger$11.log(" Local https addresses are not trusted here. Run \"gkm trust\", or \"gkm setup --yes\".");
503
432
  return;
504
433
  }
505
434
  if (await declined(root)) return;
@@ -511,7 +440,7 @@ async function ensureTrusted(root, url, options = {}) {
511
440
  });
512
441
  if (!install) {
513
442
  await recordDeclined(root);
514
- logger$12.log(" Skipped. Run \"gkm trust\" later.");
443
+ logger$11.log(" Skipped. Run \"gkm trust\" later.");
515
444
  return;
516
445
  }
517
446
  }
@@ -1061,7 +990,7 @@ function closeFakes(served) {
1061
990
  }
1062
991
  //#endregion
1063
992
  //#region src/exec/index.ts
1064
- const logger$11 = console;
993
+ const logger$10 = console;
1065
994
  /** `gkm exec` was given nothing to run. */
1066
995
  var NoCommandSpecified = class extends Error {
1067
996
  constructor() {
@@ -1084,17 +1013,17 @@ async function execCommand(commandArgs, options = {}) {
1084
1013
  const cwd = options.cwd ?? process.cwd();
1085
1014
  if (!commandArgs[0]) throw new NoCommandSpecified();
1086
1015
  const defaultEnv = loadEnvFiles(".env");
1087
- if (defaultEnv.loaded.length > 0) logger$11.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
1016
+ if (defaultEnv.loaded.length > 0) logger$10.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
1088
1017
  const { credentials, secretsJsonPath, appName } = await prepareEntryCredentials({ cwd });
1089
- if (appName) logger$11.log(`📦 App: ${appName}`);
1018
+ if (appName) logger$10.log(`📦 App: ${appName}`);
1090
1019
  const secretCount = Object.keys(credentials).filter((k) => k !== "PORT").length;
1091
- if (secretCount > 0) logger$11.log(`🔐 Loaded ${secretCount} secret(s)`);
1020
+ if (secretCount > 0) logger$10.log(`🔐 Loaded ${secretCount} secret(s)`);
1092
1021
  const preloadDir = join(cwd, ".gkm");
1093
1022
  await mkdir(preloadDir, { recursive: true });
1094
1023
  const preloadPath = join(preloadDir, "credentials-preload.mjs");
1095
1024
  await createCredentialsPreload(preloadPath, secretsJsonPath);
1096
1025
  const [cmd, ...args] = commandArgs;
1097
- logger$11.log(`🚀 Running: ${[cmd, ...args].join(" ")}`);
1026
+ logger$10.log(`🚀 Running: ${[cmd, ...args].join(" ")}`);
1098
1027
  const nodeOptions = [(process.env.NODE_OPTIONS ?? "").replace(/--import[= ]\S+/g, "").trim(), `--import=${preloadPath}`].filter(Boolean).join(" ");
1099
1028
  const child = spawn(cmd, args, {
1100
1029
  cwd,
@@ -1108,7 +1037,7 @@ async function execCommand(commandArgs, options = {}) {
1108
1037
  const exitCode = await new Promise((resolve) => {
1109
1038
  child.on("close", (code) => resolve(code ?? 0));
1110
1039
  child.on("error", (error) => {
1111
- logger$11.error(`Failed to run command: ${error.message}`);
1040
+ logger$10.error(`Failed to run command: ${error.message}`);
1112
1041
  resolve(1);
1113
1042
  });
1114
1043
  });
@@ -1116,7 +1045,7 @@ async function execCommand(commandArgs, options = {}) {
1116
1045
  }
1117
1046
  //#endregion
1118
1047
  //#region src/dev/index.ts
1119
- const logger$10 = console;
1048
+ const logger$9 = console;
1120
1049
  /**
1121
1050
  * Normalize telescope configuration
1122
1051
  * @internal Exported for testing
@@ -1181,39 +1110,36 @@ function normalizeHooksConfig(config, cwd = process.cwd()) {
1181
1110
  return { serverHooksPath: resolve(cwd, serverPath) };
1182
1111
  }
1183
1112
  /**
1184
- * Normalize production configuration
1113
+ * What a `--production` build is.
1114
+ *
1115
+ * A server runs its worker's background work itself — queues polled, crons
1116
+ * scheduled, subscribers drained — so the production entry wires all three.
1117
+ * The build writes their files on every server build, empty when there is
1118
+ * nothing, so including them costs an app without any nothing.
1119
+ *
1185
1120
  * @internal Exported for testing
1186
1121
  */
1187
- function normalizeProductionConfig(cliProduction, configProduction) {
1122
+ function normalizeProductionConfig(cliProduction) {
1188
1123
  if (!cliProduction) return;
1189
- const config = configProduction ?? {};
1190
1124
  return {
1191
1125
  enabled: true,
1192
- bundle: config.bundle ?? true,
1193
- minify: config.minify ?? true,
1194
- healthCheck: config.healthCheck ?? "/health",
1195
- gracefulShutdown: config.gracefulShutdown ?? true,
1196
- external: config.external ?? [],
1197
- subscribers: config.subscribers ?? "exclude",
1198
- openapi: config.openapi ?? false,
1199
- optimizedHandlers: config.optimizedHandlers ?? true
1126
+ bundle: true,
1127
+ minify: true,
1128
+ healthCheck: "/health",
1129
+ gracefulShutdown: true,
1130
+ external: [],
1131
+ subscribers: "include",
1132
+ openapi: false,
1133
+ optimizedHandlers: true
1200
1134
  };
1201
1135
  }
1202
- /**
1203
- * Get production config from GkmConfig
1204
- * @internal
1205
- */
1206
- function getProductionConfigFromGkm(config) {
1207
- const serverConfig = config.providers?.server;
1208
- if (typeof serverConfig === "object") return serverConfig.production;
1209
- }
1210
1136
  /** Read by the generated server: no subscriber is subscribed or polled. */
1211
1137
  const SUBSCRIBERS_ENV = "GKM_SUBSCRIBERS";
1212
1138
  async function devCommand(options) {
1213
1139
  if (options.subscribers === false) process.env[SUBSCRIBERS_ENV] = "off";
1214
1140
  if (options.entry) return entryDevCommand(options);
1215
1141
  const defaultEnv = loadEnvFiles(".env");
1216
- if (defaultEnv.loaded.length > 0) logger$10.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
1142
+ if (defaultEnv.loaded.length > 0) logger$9.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
1217
1143
  const appConfig = await loadAppConfig().catch((error) => {
1218
1144
  if (error instanceof NotInAnApp || error instanceof NotABackendApp) return;
1219
1145
  throw error;
@@ -1229,7 +1155,7 @@ async function devCommand(options) {
1229
1155
  const workspaceAppPort = appConfig.app.port;
1230
1156
  const workspace = appConfig.workspace;
1231
1157
  if (appConfig.app.entry) {
1232
- logger$10.log(`📄 Using entry point: ${appConfig.app.entry}`);
1158
+ logger$9.log(`📄 Using entry point: ${appConfig.app.entry}`);
1233
1159
  return entryDevCommand({
1234
1160
  ...options,
1235
1161
  entry: appConfig.app.entry,
@@ -1239,19 +1165,18 @@ async function devCommand(options) {
1239
1165
  }
1240
1166
  if (config.env) {
1241
1167
  const { loaded, missing } = loadEnvFiles(config.env, appRoot);
1242
- if (loaded.length > 0) logger$10.log(`📦 Loaded env: ${loaded.join(", ")}`);
1243
- if (missing.length > 0) logger$10.warn(`⚠️ Missing env files: ${missing.join(", ")}`);
1168
+ if (loaded.length > 0) logger$9.log(`📦 Loaded env: ${loaded.join(", ")}`);
1169
+ if (missing.length > 0) logger$9.warn(`⚠️ Missing env files: ${missing.join(", ")}`);
1244
1170
  }
1245
- const resolved = resolveProviders(config, { provider: "server" });
1246
1171
  const telescope = normalizeTelescopeConfig(config.telescope);
1247
1172
  const studio = normalizeStudioConfig(config.studio);
1248
1173
  const hooks = normalizeHooksConfig(config.hooks, appRoot);
1249
- const enableOpenApi = resolveOpenApiConfig(config).enabled || resolved.enableOpenApi;
1174
+ const enableOpenApi = resolveOpenApiConfig(config).enabled;
1250
1175
  const build = (bustCache = false) => quietly(() => buildApp({
1251
1176
  config,
1252
1177
  workspaceRoot: workspace?.root ?? secretsRoot,
1253
1178
  appRoot,
1254
- providers: ["server"],
1179
+ target: "server",
1255
1180
  enableOpenApi,
1256
1181
  cacheBackend: cacheBackendFor(providerOf(workspace ?? config)),
1257
1182
  telescope,
@@ -1272,8 +1197,8 @@ async function devCommand(options) {
1272
1197
  ...options.fake ? { fake: true } : {}
1273
1198
  });
1274
1199
  if (reconciled.changed && reconciled.plan.containers.length > 0) {
1275
- logger$10.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
1276
- for (const [container, address] of Object.entries(reconciled.addresses)) logger$10.log(` ${container}: ${address}`);
1200
+ logger$9.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
1201
+ for (const [container, address] of Object.entries(reconciled.addresses)) logger$9.log(` ${container}: ${address}`);
1277
1202
  }
1278
1203
  Object.assign(appSecrets, reconciled.env);
1279
1204
  const own = reconciled.plan.resources.find((r) => (r.kind === "rest-api" || r.kind === "site") && appKey(r.id) === workspaceAppName);
@@ -1286,7 +1211,7 @@ async function devCommand(options) {
1286
1211
  secretsJsonPath = join(secretsDir, secretsFileName);
1287
1212
  await writeFile(secretsJsonPath, JSON.stringify(appSecrets, null, 2));
1288
1213
  }
1289
- const devServer = new DevServer(resolved.providers[0], options.port ?? workspaceAppPort ?? 3e3, options.portExplicit ?? workspaceAppPort !== void 0, enableOpenApi, telescope, studio, runtime, appRoot, secretsJsonPath, initial.selfServing !== void 0);
1214
+ const devServer = new DevServer(options.port ?? workspaceAppPort ?? 3e3, options.portExplicit ?? workspaceAppPort !== void 0, enableOpenApi, telescope, studio, runtime, appRoot, secretsJsonPath, initial.selfServing !== void 0);
1290
1215
  devServer.label = workspaceAppName ?? "server";
1291
1216
  devServer.publicUrl = publicUrl;
1292
1217
  await devServer.start();
@@ -1307,11 +1232,11 @@ async function devCommand(options) {
1307
1232
  cwd: appRoot
1308
1233
  });
1309
1234
  watcher.on("error", (error) => {
1310
- logger$10.error("❌ Watcher error:", error);
1235
+ logger$9.error("❌ Watcher error:", error);
1311
1236
  });
1312
1237
  let rebuildTimeout = null;
1313
1238
  watcher.on("change", async (path) => {
1314
- logger$10.log(`🔄 ${path} changed — rebuilding`);
1239
+ logger$9.log(`🔄 ${path} changed — rebuilding`);
1315
1240
  if (rebuildTimeout) clearTimeout(rebuildTimeout);
1316
1241
  rebuildTimeout = setTimeout(async () => {
1317
1242
  try {
@@ -1320,8 +1245,8 @@ async function devCommand(options) {
1320
1245
  await devServer.restart();
1321
1246
  } catch (error) {
1322
1247
  const err = error;
1323
- logger$10.error(`❌ Rebuild failed: ${err.message}`);
1324
- if (err.stack) logger$10.error(err.stack);
1248
+ logger$9.error(`❌ Rebuild failed: ${err.message}`);
1249
+ if (err.stack) logger$9.error(err.stack);
1325
1250
  }
1326
1251
  }, 300);
1327
1252
  });
@@ -1329,9 +1254,9 @@ async function devCommand(options) {
1329
1254
  const shutdown = () => {
1330
1255
  if (isShuttingDown) return;
1331
1256
  isShuttingDown = true;
1332
- logger$10.log("\n🛑 Shutting down...");
1257
+ logger$9.log("\n🛑 Shutting down...");
1333
1258
  Promise.all([watcher.close(), devServer.stop()]).catch((err) => {
1334
- logger$10.error("Error during shutdown:", err);
1259
+ logger$9.error("Error during shutdown:", err);
1335
1260
  }).finally(() => {
1336
1261
  process.exit(0);
1337
1262
  });
@@ -1533,7 +1458,7 @@ async function workspaceDevCommand(workspace, options) {
1533
1458
  const frontendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "web");
1534
1459
  const conflicts = checkPortConflicts(workspace);
1535
1460
  if (conflicts.length > 0) {
1536
- for (const conflict of conflicts) logger$10.error(`❌ Port conflict: Apps "${conflict.app1}" and "${conflict.app2}" both use port ${conflict.port}`);
1461
+ for (const conflict of conflicts) logger$9.error(`❌ Port conflict: Apps "${conflict.app1}" and "${conflict.app2}" both use port ${conflict.port}`);
1537
1462
  throw new Error("Port conflicts detected. Please assign unique ports to each app.");
1538
1463
  }
1539
1464
  if (frontendApps.length > 0) {
@@ -1542,10 +1467,10 @@ async function workspaceDevCommand(workspace, options) {
1542
1467
  for (const result of validationResults) {
1543
1468
  if (!result.valid) {
1544
1469
  hasErrors = true;
1545
- logger$10.error(`\n❌ Frontend app "${result.appName}" validation failed:`);
1546
- for (const error of result.errors) logger$10.error(` • ${error}`);
1470
+ logger$9.error(`\n❌ Frontend app "${result.appName}" validation failed:`);
1471
+ for (const error of result.errors) logger$9.error(` • ${error}`);
1547
1472
  }
1548
- for (const warning of result.warnings) logger$10.warn(` ⚠️ ${result.appName}: ${warning}`);
1473
+ for (const warning of result.warnings) logger$9.warn(` ⚠️ ${result.appName}: ${warning}`);
1549
1474
  }
1550
1475
  if (hasErrors) throw new Error("Frontend app validation failed. Fix the issues above and try again.");
1551
1476
  }
@@ -1555,20 +1480,20 @@ async function workspaceDevCommand(workspace, options) {
1555
1480
  ...options.fake ? { fake: true } : {}
1556
1481
  });
1557
1482
  if (reconciled.changed && reconciled.plan.containers.length > 0) {
1558
- logger$10.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
1559
- for (const [container, address] of Object.entries(reconciled.addresses)) logger$10.log(` ${container}: ${address}`);
1483
+ logger$9.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
1484
+ for (const [container, address] of Object.entries(reconciled.addresses)) logger$9.log(` ${container}: ${address}`);
1560
1485
  }
1561
1486
  const secretsEnv = {
1562
1487
  ...rawSecrets,
1563
1488
  ...reconciled.env
1564
1489
  };
1565
1490
  const fakes = options.fake ? await serveFakes(reconciled.fakes, reconciled.ports) : [];
1566
- for (const { id, port } of fakes) logger$10.log(`🎭 ${id} fake: http://localhost:${port}`);
1491
+ for (const { id, port } of fakes) logger$9.log(`🎭 ${id} fake: http://localhost:${port}`);
1567
1492
  if (options.migrate || options.seed) {
1568
- const { prepareDevDatabases } = await import("./migrate-BRklCm7b.mjs");
1493
+ const { prepareDevDatabases } = await import("./migrate-B3opyC3o.mjs");
1569
1494
  await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
1570
- } else await import("./migrate-BRklCm7b.mjs").then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
1571
- logger$10.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
1495
+ } else await import("./migrate-B3opyC3o.mjs").then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
1496
+ logger$9.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
1572
1497
  });
1573
1498
  const appUrls = {};
1574
1499
  for (const resource of reconciled.plan.resources) {
@@ -1589,11 +1514,11 @@ async function workspaceDevCommand(workspace, options) {
1589
1514
  else {
1590
1515
  const { filters, unpackaged } = turboFilters(workspace);
1591
1516
  turboFilter = filters.flatMap((name) => ["--filter", name]);
1592
- if (unpackaged.length > 0) logger$10.warn(`⚠️ No package.json with a name for: ${unpackaged.join(", ")} — turbo cannot run ${unpackaged.length === 1 ? "it" : "them"}.`);
1517
+ if (unpackaged.length > 0) logger$9.warn(`⚠️ No package.json with a name for: ${unpackaged.join(", ")} — turbo cannot run ${unpackaged.length === 1 ? "it" : "them"}.`);
1593
1518
  }
1594
1519
  const buildOrder = getAppBuildOrder(workspace);
1595
1520
  const width = Math.max(...buildOrder.map((name) => name.length));
1596
- logger$10.log(`\n${workspace.name}: ${appCount} app(s)`);
1521
+ logger$9.log(`\n${workspace.name}: ${appCount} app(s)`);
1597
1522
  for (const appName of buildOrder) {
1598
1523
  const app = workspace.apps[appName];
1599
1524
  if (!app) continue;
@@ -1601,14 +1526,14 @@ async function workspaceDevCommand(workspace, options) {
1601
1526
  const own = reconciled.plan.resources.find((r) => r.kind === "mobile-app" && appKey(r.id) === appName);
1602
1527
  const scheme = own ? reconciled.env[own.envKey] : void 0;
1603
1528
  const device = Object.entries(reconciled.env).find(([key]) => key.endsWith("_DEVICE_URL"))?.[1];
1604
- logger$10.log(` ${appName.padEnd(width)} ${scheme ? `${scheme}://` : "mobile"}${device ? ` — devices reach ${new URL(device).hostname}` : ""}`);
1529
+ logger$9.log(` ${appName.padEnd(width)} ${scheme ? `${scheme}://` : "mobile"}${device ? ` — devices reach ${new URL(device).hostname}` : ""}`);
1605
1530
  continue;
1606
1531
  }
1607
1532
  const local = `http://localhost:${app.port}`;
1608
1533
  const url = Object.entries(appUrls).find(([id]) => appKey(id) === appName)?.[1];
1609
- logger$10.log(` ${appName.padEnd(width)} ${url ? `${url} -> ${local}` : local}`);
1534
+ logger$9.log(` ${appName.padEnd(width)} ${url ? `${url} -> ${local}` : local}`);
1610
1535
  }
1611
- logger$10.log("");
1536
+ logger$9.log("");
1612
1537
  const configFiles = [
1613
1538
  "gkm.config.ts",
1614
1539
  "gkm.config.js",
@@ -1655,7 +1580,7 @@ async function workspaceDevCommand(workspace, options) {
1655
1580
  const shutdown = () => {
1656
1581
  if (isShuttingDown) return;
1657
1582
  isShuttingDown = true;
1658
- logger$10.log("\n🛑 Shutting down workspace...");
1583
+ logger$9.log("\n🛑 Shutting down workspace...");
1659
1584
  closeFakes(fakes);
1660
1585
  const pid = turboProcess.pid;
1661
1586
  if (pid) try {
@@ -1680,7 +1605,7 @@ async function workspaceDevCommand(workspace, options) {
1680
1605
  process.on("SIGTERM", shutdown);
1681
1606
  return new Promise((resolve, reject) => {
1682
1607
  turboProcess.on("error", (error) => {
1683
- logger$10.error("❌ Turbo error:", error);
1608
+ logger$9.error("❌ Turbo error:", error);
1684
1609
  reject(error);
1685
1610
  });
1686
1611
  turboProcess.on("exit", (code) => {
@@ -1699,11 +1624,11 @@ async function entryDevCommand(options) {
1699
1624
  const entryPath = resolve(process.cwd(), entry);
1700
1625
  if (!existsSync(entryPath)) throw new Error(`Entry file not found: ${entryPath}`);
1701
1626
  const defaultEnv = loadEnvFiles(".env");
1702
- if (defaultEnv.loaded.length > 0) logger$10.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
1627
+ if (defaultEnv.loaded.length > 0) logger$9.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
1703
1628
  const { credentials, resolvedPort, secretsJsonPath, appName } = await prepareEntryCredentials({ explicitPort: options.portExplicit ? options.port : void 0 });
1704
- if (appName) logger$10.log(`📦 App: ${appName} (port ${resolvedPort})`);
1705
- logger$10.log(`🚀 Starting entry file: ${entry} on port ${resolvedPort}`);
1706
- if (Object.keys(credentials).length > 1) logger$10.log(`🔐 Loaded ${Object.keys(credentials).length - 1} secret(s) + PORT`);
1629
+ if (appName) logger$9.log(`📦 App: ${appName} (port ${resolvedPort})`);
1630
+ logger$9.log(`🚀 Starting entry file: ${entry} on port ${resolvedPort}`);
1631
+ if (Object.keys(credentials).length > 1) logger$9.log(`🔐 Loaded ${Object.keys(credentials).length - 1} secret(s) + PORT`);
1707
1632
  const wrapperDir = join(process.cwd(), ".gkm");
1708
1633
  await mkdir(wrapperDir, { recursive: true });
1709
1634
  const wrapperPath = join(wrapperDir, "entry-wrapper.ts");
@@ -1714,7 +1639,7 @@ async function entryDevCommand(options) {
1714
1639
  const shutdown = () => {
1715
1640
  if (isShuttingDown) return;
1716
1641
  isShuttingDown = true;
1717
- logger$10.log("\n🛑 Shutting down...");
1642
+ logger$9.log("\n🛑 Shutting down...");
1718
1643
  runner.stop();
1719
1644
  process.exit(0);
1720
1645
  };
@@ -1750,14 +1675,14 @@ var EntryRunner = class {
1750
1675
  });
1751
1676
  let restartTimeout = null;
1752
1677
  this.watcher.on("change", (path) => {
1753
- logger$10.log(`📝 File changed: ${path}`);
1678
+ logger$9.log(`📝 File changed: ${path}`);
1754
1679
  if (restartTimeout) clearTimeout(restartTimeout);
1755
1680
  restartTimeout = setTimeout(async () => {
1756
- logger$10.log("🔄 Restarting...");
1681
+ logger$9.log("🔄 Restarting...");
1757
1682
  await this.restart();
1758
1683
  }, 300);
1759
1684
  });
1760
- logger$10.log(`👀 Watching for changes in: ${watchDir}`);
1685
+ logger$9.log(`👀 Watching for changes in: ${watchDir}`);
1761
1686
  }
1762
1687
  }
1763
1688
  async runProcess() {
@@ -1772,17 +1697,17 @@ var EntryRunner = class {
1772
1697
  });
1773
1698
  this.isRunning = true;
1774
1699
  this.childProcess.on("error", (error) => {
1775
- logger$10.error("❌ Process error:", error);
1700
+ logger$9.error("❌ Process error:", error);
1776
1701
  });
1777
1702
  this.childProcess.on("exit", (code) => {
1778
- if (code !== null && code !== 0 && code !== 143) logger$10.error(`❌ Process exited with code ${code}`);
1703
+ if (code !== null && code !== 0 && code !== 143) logger$9.error(`❌ Process exited with code ${code}`);
1779
1704
  this.isRunning = false;
1780
1705
  });
1781
1706
  await new Promise((resolve) => setTimeout(resolve, 500));
1782
1707
  if (this.isRunning) {
1783
- logger$10.log("");
1784
- logger$10.log(` \x1b[32m✓ Ready\x1b[0m at \x1b[36mhttp://localhost:${this.port}\x1b[0m`);
1785
- logger$10.log("");
1708
+ logger$9.log("");
1709
+ logger$9.log(` \x1b[32m✓ Ready\x1b[0m at \x1b[36mhttp://localhost:${this.port}\x1b[0m`);
1710
+ logger$9.log("");
1786
1711
  }
1787
1712
  }
1788
1713
  async restart() {
@@ -1960,7 +1885,6 @@ function listenerOn(port) {
1960
1885
  }
1961
1886
  }
1962
1887
  var DevServer = class {
1963
- provider;
1964
1888
  requestedPort;
1965
1889
  portExplicit;
1966
1890
  enableOpenApi;
@@ -1974,8 +1898,7 @@ var DevServer = class {
1974
1898
  isRunning = false;
1975
1899
  actualPort;
1976
1900
  startTime = Date.now();
1977
- constructor(provider, requestedPort, portExplicit, enableOpenApi, telescope, studio, runtime = "node", appRoot = process.cwd(), secretsJsonPath, selfServing = false) {
1978
- this.provider = provider;
1901
+ constructor(requestedPort, portExplicit, enableOpenApi, telescope, studio, runtime = "node", appRoot = process.cwd(), secretsJsonPath, selfServing = false) {
1979
1902
  this.requestedPort = requestedPort;
1980
1903
  this.portExplicit = portExplicit;
1981
1904
  this.enableOpenApi = enableOpenApi;
@@ -2002,9 +1925,9 @@ var DevServer = class {
2002
1925
  this.actualPort = this.requestedPort;
2003
1926
  } else {
2004
1927
  this.actualPort = await findAvailablePort(this.requestedPort);
2005
- if (this.actualPort !== this.requestedPort) logger$10.log(`ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`);
1928
+ if (this.actualPort !== this.requestedPort) logger$9.log(`ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`);
2006
1929
  }
2007
- const serverEntryPath = join(this.appRoot, ".gkm", this.provider, "server.ts");
1930
+ const serverEntryPath = join(this.appRoot, ".gkm", "server", "server.ts");
2008
1931
  await this.createServerEntry();
2009
1932
  this.serverProcess = spawn(...tsxCommand(this.appRoot, [
2010
1933
  serverEntryPath,
@@ -2021,10 +1944,10 @@ var DevServer = class {
2021
1944
  });
2022
1945
  this.isRunning = true;
2023
1946
  this.serverProcess.on("error", (error) => {
2024
- logger$10.error("❌ Server error:", error);
1947
+ logger$9.error("❌ Server error:", error);
2025
1948
  });
2026
1949
  this.serverProcess.on("exit", (code, signal) => {
2027
- if (code !== null && code !== 0 && signal !== "SIGTERM") logger$10.error(`❌ Server exited with code ${code}`);
1950
+ if (code !== null && code !== 0 && signal !== "SIGTERM") logger$9.error(`❌ Server exited with code ${code}`);
2028
1951
  this.isRunning = false;
2029
1952
  });
2030
1953
  await new Promise((resolve) => setTimeout(resolve, 1e3));
@@ -2032,13 +1955,13 @@ var DevServer = class {
2032
1955
  const local = `http://localhost:${this.actualPort}`;
2033
1956
  const address = this.publicUrl ? `${this.publicUrl} -> ${local}` : local;
2034
1957
  const seconds = ((Date.now() - this.startTime) / 1e3).toFixed(1);
2035
- logger$10.log(`\x1b[32m✓\x1b[0m ${this.label} ready in ${seconds}s ${address}`);
1958
+ logger$9.log(`\x1b[32m✓\x1b[0m ${this.label} ready in ${seconds}s ${address}`);
2036
1959
  const tools = this.selfServing ? [] : [
2037
1960
  ...this.enableOpenApi ? ["docs /__docs"] : [],
2038
1961
  ...this.telescope ? [`telescope ${this.telescope.path}`] : [],
2039
1962
  ...this.studio ? [`studio ${this.studio.path}`] : []
2040
1963
  ];
2041
- if (tools.length > 0) logger$10.log(` ${tools.join(" · ")}`);
1964
+ if (tools.length > 0) logger$9.log(` ${tools.join(" · ")}`);
2042
1965
  }
2043
1966
  }
2044
1967
  async stop() {
@@ -2076,7 +1999,7 @@ var DevServer = class {
2076
1999
  }
2077
2000
  async createServerEntry() {
2078
2001
  const { writeFile: fsWriteFile } = await import("node:fs/promises");
2079
- await fsWriteFile(join(this.appRoot, ".gkm", this.provider, "server.ts"), generateServerEntryContent({
2002
+ await fsWriteFile(join(this.appRoot, ".gkm", "server", "server.ts"), generateServerEntryContent({
2080
2003
  secretsJsonPath: this.secretsJsonPath,
2081
2004
  runtime: this.runtime,
2082
2005
  enableOpenApi: this.enableOpenApi,
@@ -2088,22 +2011,22 @@ var DevServer = class {
2088
2011
  //#region src/generators/CronGenerator.ts
2089
2012
  var CronGenerator = class extends ConstructGenerator {
2090
2013
  async build(context, constructs, outputDir, options) {
2091
- const provider = options?.provider || "aws-lambda";
2014
+ const target = options?.target ?? "aws";
2092
2015
  const logger = console;
2093
2016
  const cronInfos = [];
2094
- if (provider === "server") {
2017
+ if (target === "server") {
2095
2018
  await this.generateServerCronsFile(outputDir, constructs);
2096
2019
  logger.log(`Generated server crons file with ${constructs.length} crons`);
2097
2020
  return cronInfos;
2098
2021
  }
2099
- if (constructs.length === 0 || provider !== "aws-lambda") return cronInfos;
2022
+ if (constructs.length === 0) return cronInfos;
2100
2023
  const cronsDir = join(outputDir, "crons");
2101
2024
  await mkdir(cronsDir, { recursive: true });
2102
2025
  for (const { key, construct, path } of constructs) {
2103
2026
  const handlerFile = await this.generateCronHandler(cronsDir, path.relative, key, context, construct.owner);
2104
2027
  cronInfos.push({
2105
2028
  name: key,
2106
- handler: relative(process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
2029
+ handler: relative(options?.root ?? process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
2107
2030
  schedule: construct.schedule || "rate(1 hour)",
2108
2031
  timeout: construct.timeout,
2109
2032
  memorySize: construct.memorySize,
@@ -2293,17 +2216,17 @@ var FunctionGenerator = class extends ConstructGenerator {
2293
2216
  return Function.isFunction(value);
2294
2217
  }
2295
2218
  async build(context, constructs, outputDir, options) {
2296
- const provider = options?.provider || "aws-lambda";
2219
+ const target = options?.target ?? "aws";
2297
2220
  const logger = console;
2298
2221
  const functionInfos = [];
2299
- if (constructs.length === 0 || provider !== "aws-lambda") return functionInfos;
2222
+ if (constructs.length === 0 || target === "server") return functionInfos;
2300
2223
  const functionsDir = join(outputDir, "functions");
2301
2224
  await mkdir(functionsDir, { recursive: true });
2302
2225
  for (const { key, construct, path } of constructs) {
2303
2226
  const handlerFile = await this.generateFunctionHandler(functionsDir, path.relative, key, context, construct.owner);
2304
2227
  functionInfos.push({
2305
2228
  name: key,
2306
- handler: relative(process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
2229
+ handler: relative(options?.root ?? process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
2307
2230
  timeout: construct.timeout,
2308
2231
  memorySize: construct.memorySize,
2309
2232
  environment: await construct.getEnvironment({ markOptional: context.markOptional }),
@@ -2347,22 +2270,22 @@ var QueueGenerator = class extends ConstructGenerator {
2347
2270
  return Queue.isQueue(value);
2348
2271
  }
2349
2272
  async build(context, constructs, outputDir, options) {
2350
- const provider = options?.provider || "aws-lambda";
2273
+ const target = options?.target ?? "aws";
2351
2274
  const logger = console;
2352
2275
  const queueInfos = [];
2353
- if (provider === "server") {
2276
+ if (target === "server") {
2354
2277
  await this.generateServerQueuesFile(outputDir, constructs);
2355
2278
  logger.log(`Generated server queues file with ${constructs.length} queues (polling mode)`);
2356
2279
  return queueInfos;
2357
2280
  }
2358
- if (constructs.length === 0 || provider !== "aws-lambda") return queueInfos;
2281
+ if (constructs.length === 0) return queueInfos;
2359
2282
  const queuesDir = join(outputDir, "queues");
2360
2283
  await mkdir(queuesDir, { recursive: true });
2361
2284
  for (const { key, construct, path } of constructs) {
2362
2285
  const handlerFile = await this.generateQueueHandler(queuesDir, path.relative, key, context, construct.owner);
2363
2286
  queueInfos.push({
2364
2287
  name: construct.name,
2365
- handler: relative(process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
2288
+ handler: relative(options?.root ?? process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
2366
2289
  batchSize: construct.batchSize,
2367
2290
  fifo: construct.fifo,
2368
2291
  timeout: construct.timeout,
@@ -2528,217 +2451,6 @@ var TopicGenerator = class extends ConstructGenerator {
2528
2451
  }
2529
2452
  };
2530
2453
  //#endregion
2531
- //#region src/build/manifests.ts
2532
- const logger$9 = console;
2533
- function isPartitioned(field) {
2534
- return !Array.isArray(field);
2535
- }
2536
- /**
2537
- * Serialize a manifest field to a TypeScript string.
2538
- * Flat arrays serialize as JSON arrays, partitioned fields as objects of arrays.
2539
- */
2540
- function serializeField(field, indent = 2) {
2541
- if (Array.isArray(field)) return JSON.stringify(field, null, indent);
2542
- return `{\n${Object.entries(field).map(([key, value]) => ` ${JSON.stringify(key)}: ${JSON.stringify(value, null, indent)}`).join(",\n")},\n }`;
2543
- }
2544
- /**
2545
- * Count total items across a manifest field (flat or partitioned).
2546
- */
2547
- function countItems(field) {
2548
- if (Array.isArray(field)) return field.length;
2549
- return Object.values(field).reduce((sum, arr) => sum + arr.length, 0);
2550
- }
2551
- /**
2552
- * Generate derived types for a construct field.
2553
- * @param fieldName - The field name in the manifest (e.g., 'routes')
2554
- * @param typeName - The exported type name (e.g., 'Route')
2555
- * @param partitioned - Whether this field is partitioned
2556
- */
2557
- function generateDerivedType(fieldName, typeName, partitioned) {
2558
- if (partitioned) {
2559
- const partitionTypeName = `${typeName}Partition`;
2560
- return [`export type ${partitionTypeName} = keyof typeof manifest.${fieldName};`, `export type ${typeName}<P extends ${partitionTypeName} = ${partitionTypeName}> = (typeof manifest.${fieldName})[P][number];`].join("\n");
2561
- }
2562
- return `export type ${typeName} = (typeof manifest.${fieldName})[number];`;
2563
- }
2564
- /** A manifest field as a flat list, whether or not it is partitioned. */
2565
- function flatten(field) {
2566
- return Array.isArray(field) ? field : Object.values(field).flat();
2567
- }
2568
- async function generateAwsManifest(outputDir, routes, functions, crons, subscribers, queues = [], topics = [], constructs = {}, backends = {}) {
2569
- const manifestDir = join(outputDir, "manifest");
2570
- await mkdir(manifestDir, { recursive: true });
2571
- const awsRoutes = filterAllRoutes(routes);
2572
- const routesPartitioned = isPartitioned(awsRoutes);
2573
- const functionsPartitioned = isPartitioned(functions);
2574
- const cronsPartitioned = isPartitioned(crons);
2575
- const subscribersPartitioned = isPartitioned(subscribers);
2576
- const queuesPartitioned = isPartitioned(queues);
2577
- const topicsPartitioned = isPartitioned(topics);
2578
- const awsConstructs = withCompute(withRoutes(constructs, flatten(awsRoutes), { perRoute: true }), {
2579
- functions: flatten(functions),
2580
- crons: flatten(crons),
2581
- queues: flatten(queues),
2582
- subscribers: flatten(subscribers)
2583
- });
2584
- const content = `export const manifest = {
2585
- routes: ${serializeField(awsRoutes)},
2586
- functions: ${serializeField(functions)},
2587
- crons: ${serializeField(crons)},
2588
- subscribers: ${serializeField(subscribers)},
2589
- queues: ${serializeField(queues)},
2590
- topics: ${serializeField(topics)},
2591
- } as const;
2592
-
2593
- /**
2594
- * What the application declares, as opposed to what this build generated.
2595
- *
2596
- * Discovery imports application code, so it runs once here rather than in every
2597
- * consumer — a deploy config calling it would evaluate the whole runtime graph
2598
- * inside its own toolchain.
2599
- */
2600
- export const constructs = ${JSON.stringify(awsConstructs, null, 2)} as const;
2601
-
2602
- /**
2603
- * Where the backends that are config rather than declaration resolved to.
2604
- *
2605
- * Recorded because they were answered twice: once by the build, which registers
2606
- * exactly one cache driver, and once by a deploy, which told the provisioner
2607
- * something else — leaving the running code with a URL it had no driver for.
2608
- */
2609
- export const backends = ${JSON.stringify(backends, null, 2)} as const;
2610
-
2611
- // Derived types
2612
- ${generateDerivedType("routes", "Route", routesPartitioned)}
2613
- ${generateDerivedType("functions", "Function", functionsPartitioned)}
2614
- ${generateDerivedType("crons", "Cron", cronsPartitioned)}
2615
- ${generateDerivedType("subscribers", "Subscriber", subscribersPartitioned)}
2616
- ${generateDerivedType("queues", "Queue", queuesPartitioned)}
2617
- ${generateDerivedType("topics", "Topic", topicsPartitioned)}
2618
-
2619
- export type ConstructId = keyof typeof constructs;
2620
- export type Construct<Id extends ConstructId> = (typeof constructs)[Id];
2621
-
2622
- // Useful union types
2623
- export type Authorizer = Route['authorizer'];
2624
- export type HttpMethod = Route['method'];
2625
- export type RoutePath = Route['path'];
2626
- `;
2627
- const manifestPath = join(manifestDir, "aws.ts");
2628
- await writeFile(manifestPath, content);
2629
- logger$9.log(`Generated AWS manifest with ${countItems(awsRoutes)} routes, ${countItems(functions)} functions, ${countItems(crons)} crons, ${countItems(subscribers)} subscribers, ${countItems(queues)} queues, ${countItems(topics)} topics`);
2630
- logger$9.log(`Manifest: ${relative(process.cwd(), manifestPath)}`);
2631
- }
2632
- async function generateServerManifest(outputDir, appInfo, routes, subscribers, queues = [], topics = [], constructs = {}, backends = {}) {
2633
- const manifestDir = join(outputDir, "manifest");
2634
- await mkdir(manifestDir, { recursive: true });
2635
- const serverRoutes = mapRouteMetadata(filterAllRoutes(routes));
2636
- const serverSubscribers = mapSubscriberMetadata(subscribers);
2637
- const serverQueues = mapQueueMetadata(queues);
2638
- const serverTopics = mapTopicMetadata(topics);
2639
- const routesPartitioned = isPartitioned(serverRoutes);
2640
- const subscribersPartitioned = isPartitioned(serverSubscribers);
2641
- const queuesPartitioned = isPartitioned(serverQueues);
2642
- const topicsPartitioned = isPartitioned(serverTopics);
2643
- const content = `export const manifest = {
2644
- app: ${JSON.stringify(appInfo, null, 2)},
2645
- routes: ${serializeField(serverRoutes)},
2646
- subscribers: ${serializeField(serverSubscribers)},
2647
- queues: ${serializeField(serverQueues)},
2648
- topics: ${serializeField(serverTopics)},
2649
- } as const;
2650
-
2651
- /**
2652
- * What the application declares, as opposed to what this build generated.
2653
- *
2654
- * Discovery imports application code, so it runs once in the build rather than
2655
- * in every consumer — a deploy config calling it would evaluate the whole
2656
- * runtime graph inside its own toolchain.
2657
- */
2658
- export const constructs = ${JSON.stringify(constructs, null, 2)} as const;
2659
-
2660
- /**
2661
- * Where the backends that are config rather than declaration resolved to.
2662
- *
2663
- * Recorded because they were answered twice: once by the build, which registers
2664
- * exactly one cache driver, and once by a deploy, which told the provisioner
2665
- * something else — leaving the running code with a URL it had no driver for.
2666
- */
2667
- export const backends = ${JSON.stringify(backends, null, 2)} as const;
2668
-
2669
- // Derived types
2670
- ${generateDerivedType("routes", "Route", routesPartitioned)}
2671
- ${generateDerivedType("subscribers", "Subscriber", subscribersPartitioned)}
2672
- ${generateDerivedType("queues", "Queue", queuesPartitioned)}
2673
- ${generateDerivedType("topics", "Topic", topicsPartitioned)}
2674
-
2675
- export type ConstructId = keyof typeof constructs;
2676
- export type Construct<Id extends ConstructId> = (typeof constructs)[Id];
2677
-
2678
- // Useful union types
2679
- export type Authorizer = Route['authorizer'];
2680
- export type HttpMethod = Route['method'];
2681
- export type RoutePath = Route['path'];
2682
- `;
2683
- const manifestPath = join(manifestDir, "server.ts");
2684
- await writeFile(manifestPath, content);
2685
- logger$9.log(`Generated server manifest with ${countItems(serverRoutes)} routes, ${countItems(serverSubscribers)} subscribers, ${countItems(serverQueues)} queues, ${countItems(serverTopics)} topics`);
2686
- logger$9.log(`Manifest: ${relative(process.cwd(), manifestPath)}`);
2687
- }
2688
- /**
2689
- * Filter out 'ALL' method routes from a manifest field (flat or partitioned).
2690
- */
2691
- function filterAllRoutes(routes) {
2692
- if (Array.isArray(routes)) return routes.filter((r) => r.method !== "ALL");
2693
- const result = {};
2694
- for (const [partition, partitionRoutes] of Object.entries(routes)) result[partition] = partitionRoutes.filter((r) => r.method !== "ALL");
2695
- return result;
2696
- }
2697
- /**
2698
- * Map routes to server metadata (path, method, authorizer only).
2699
- */
2700
- function mapRouteMetadata(routes) {
2701
- const mapFn = (r) => ({
2702
- path: r.path,
2703
- method: r.method,
2704
- authorizer: r.authorizer
2705
- });
2706
- if (Array.isArray(routes)) return routes.map(mapFn);
2707
- const result = {};
2708
- for (const [partition, partitionRoutes] of Object.entries(routes)) result[partition] = partitionRoutes.map(mapFn);
2709
- return result;
2710
- }
2711
- /**
2712
- * Map subscribers to server metadata (name, subscribedEvents only).
2713
- */
2714
- function mapSubscriberMetadata(subscribers) {
2715
- const mapFn = (s) => ({
2716
- name: s.name,
2717
- subscribedEvents: s.subscribedEvents
2718
- });
2719
- if (Array.isArray(subscribers)) return subscribers.map(mapFn);
2720
- const result = {};
2721
- for (const [partition, partitionSubs] of Object.entries(subscribers)) result[partition] = partitionSubs.map(mapFn);
2722
- return result;
2723
- }
2724
- function mapQueueMetadata(queues) {
2725
- const mapFn = (q) => ({ name: q.name });
2726
- if (Array.isArray(queues)) return queues.map(mapFn);
2727
- const result = {};
2728
- for (const [partition, partitionQueues] of Object.entries(queues)) result[partition] = partitionQueues.map(mapFn);
2729
- return result;
2730
- }
2731
- function mapTopicMetadata(topics) {
2732
- const mapFn = (t) => ({
2733
- name: t.name,
2734
- events: t.events
2735
- });
2736
- if (Array.isArray(topics)) return topics.map(mapFn);
2737
- const result = {};
2738
- for (const [partition, partitionTopics] of Object.entries(topics)) result[partition] = partitionTopics.map(mapFn);
2739
- return result;
2740
- }
2741
- //#endregion
2742
2454
  //#region src/build/owners.ts
2743
2455
  /**
2744
2456
  * What a generated entry imports its runtime from, read off the discovered
@@ -2808,27 +2520,6 @@ function restApis(declared) {
2808
2520
  function servedSurface(declared, workspaceRoot, appRoot) {
2809
2521
  return restApis(declared).find((d) => resolve(workspaceRoot, d.path) === resolve(appRoot));
2810
2522
  }
2811
- /**
2812
- * Check if any construct across the given arrays has a non-undefined partition.
2813
- * When true, the manifest should use the partitioned shape for that construct type.
2814
- */
2815
- function hasPartitions(constructs) {
2816
- return constructs.some((c) => c.partition !== void 0);
2817
- }
2818
- /**
2819
- * Group an info array by partition, using the partition values from
2820
- * the corresponding construct array. Both arrays must be the same length
2821
- * and in the same order.
2822
- */
2823
- function groupInfosByPartition(infos, constructs) {
2824
- const groups = {};
2825
- for (let i = 0; i < infos.length; i++) {
2826
- const partition = constructs[i]?.partition ?? "default";
2827
- if (!groups[partition]) groups[partition] = [];
2828
- groups[partition].push(infos[i]);
2829
- }
2830
- return groups;
2831
- }
2832
2523
  //#endregion
2833
2524
  //#region src/build/surfaceEntry.ts
2834
2525
  /** Whether a construct serves itself, asked of the construct. */
@@ -2905,6 +2596,24 @@ function rootGkmConfig(workspace) {
2905
2596
  constructs: allConstructGlobs(workspace)
2906
2597
  };
2907
2598
  }
2599
+ /** Directories under `.gkm/` that the removed per-provider builds wrote. */
2600
+ const SUPERSEDED_OUTPUT = [
2601
+ "aws-apigatewayv1",
2602
+ "aws-apigatewayv2",
2603
+ "aws-lambda"
2604
+ ];
2605
+ /** Whether `--provider` named something a build can target. */
2606
+ function isMainProvider(value) {
2607
+ return value === "aws" || value === "server";
2608
+ }
2609
+ var UnknownBuildProvider = class extends Error {
2610
+ provider;
2611
+ constructor(provider) {
2612
+ super(`'${provider}' is not a build target. Pass --provider aws or --provider server, or leave it out to build for where gkm.config.ts deploys.`);
2613
+ this.provider = provider;
2614
+ this.name = "UnknownBuildProvider";
2615
+ }
2616
+ };
2908
2617
  async function buildCommand(options) {
2909
2618
  const loadedConfig = await loadWorkspaceConfig();
2910
2619
  if (loadedConfig.type === "workspace") {
@@ -2918,25 +2627,62 @@ async function buildCommand(options) {
2918
2627
  }
2919
2628
  }
2920
2629
  const config = loadedConfig.type !== "workspace" ? await loadConfig() : resolve(process.cwd()) === resolve(loadedConfig.workspace.root) ? rootGkmConfig(loadedConfig.workspace) : (await loadAppConfig()).gkmConfig;
2921
- const resolved = resolveProviders(config, options);
2922
- const cacheBackend = cacheBackendFor(providerOf(loadedConfig.workspace));
2923
- const productionConfigFromGkm = getProductionConfigFromGkm(config);
2924
- const production = normalizeProductionConfig(options.production ?? false, productionConfigFromGkm);
2630
+ const target = options.provider ?? providerOf(loadedConfig.workspace);
2631
+ const workspaceRoot = loadedConfig.workspace.root;
2632
+ const appRoot = process.cwd();
2633
+ const output = await buildOneApp({
2634
+ config,
2635
+ options,
2636
+ workspace: loadedConfig.workspace,
2637
+ appRoot,
2638
+ target
2639
+ });
2640
+ if (resolve(appRoot) === resolve(workspaceRoot)) await writeManifest({
2641
+ workspaceRoot,
2642
+ target,
2643
+ builds: output.built ? [output.built] : [],
2644
+ constructs: await declaredIn(config, workspaceRoot),
2645
+ backends: backendsOf(loadedConfig.workspace)
2646
+ });
2647
+ return output;
2648
+ }
2649
+ /** What the workspace declares, read through a config's globs. */
2650
+ async function declaredIn(config, cwd) {
2651
+ return discover({
2652
+ patterns: config.constructs,
2653
+ cwd
2654
+ });
2655
+ }
2656
+ /**
2657
+ * The backend choices the manifest records — answered by the deploy target,
2658
+ * the same place reconcile and the build's drivers read them.
2659
+ */
2660
+ function backendsOf(workspace) {
2661
+ return {
2662
+ cache: cacheBackendFor(providerOf(workspace)),
2663
+ email: "ses"
2664
+ };
2665
+ }
2666
+ /** One app's build, from its config and the command's options. */
2667
+ async function buildOneApp(input) {
2668
+ const { config, options, workspace, appRoot, target } = input;
2669
+ const cacheBackend = cacheBackendFor(providerOf(workspace));
2670
+ const production = normalizeProductionConfig(options.production ?? false);
2925
2671
  if (production) logger$8.log(`🏭 Building for PRODUCTION`);
2926
- logger$8.log(`Building with providers: ${resolved.providers.join(", ")}`);
2672
+ logger$8.log(`Building for ${target}`);
2927
2673
  logger$8.log(`Loading constructs from: ${formatRoutes(config.constructs)}`);
2928
2674
  const telescope = production ? void 0 : normalizeTelescopeConfig(config.telescope);
2929
2675
  if (telescope) logger$8.log(`🔭 Telescope enabled at ${telescope.path}`);
2930
2676
  const studio = production ? void 0 : normalizeStudioConfig(config.studio);
2931
2677
  if (studio) logger$8.log(`🗄️ Studio enabled at ${studio.path}`);
2932
- const hooks = normalizeHooksConfig(config.hooks);
2678
+ const hooks = normalizeHooksConfig(config.hooks, appRoot);
2933
2679
  if (hooks) logger$8.log(`🪝 Server hooks enabled`);
2934
2680
  return buildApp({
2935
2681
  config,
2936
- workspaceRoot: loadedConfig.workspace.root,
2937
- appRoot: process.cwd(),
2938
- providers: resolved.providers,
2939
- enableOpenApi: resolved.enableOpenApi,
2682
+ workspaceRoot: workspace.root,
2683
+ appRoot,
2684
+ target,
2685
+ enableOpenApi: options.enableOpenApi ?? false,
2940
2686
  cacheBackend,
2941
2687
  production,
2942
2688
  telescope,
@@ -2948,6 +2694,33 @@ async function buildCommand(options) {
2948
2694
  });
2949
2695
  }
2950
2696
  /**
2697
+ * Write the application's manifest: every declared construct, with what each
2698
+ * app generated folded into it.
2699
+ *
2700
+ * The manifest belongs to the whole application, so only the root writes it —
2701
+ * once, after every app has built, from the constructs the workspace declares.
2702
+ * An app's own build generates its handlers and nothing else.
2703
+ */
2704
+ async function writeManifest(input) {
2705
+ const { workspaceRoot, target, builds, constructs, backends } = input;
2706
+ const manifestDir = join(workspaceRoot, ".gkm", "manifest");
2707
+ await mkdir(manifestDir, { recursive: true });
2708
+ await rm(join(manifestDir, `${target === "aws" ? "server" : "aws"}.ts`), { force: true });
2709
+ const withServed = builds.reduce((manifest, build) => withRoutes(manifest, build.routes.filter((route) => route.method !== "ALL"), {
2710
+ perRoute: true,
2711
+ ...build.surface ? { surface: build.surface } : {}
2712
+ }), constructs);
2713
+ const folded = withCompute(withServed, {
2714
+ functions: builds.flatMap((build) => build.functions),
2715
+ crons: builds.flatMap((build) => build.crons),
2716
+ queues: builds.flatMap((build) => build.queues),
2717
+ subscribers: builds.flatMap((build) => build.subscribers)
2718
+ });
2719
+ const path = join(manifestDir, `${target}.ts`);
2720
+ await writeFile(path, manifestModule(folded, backends));
2721
+ logger$8.log(`Manifest: ${relative(process.cwd(), path)}`);
2722
+ }
2723
+ /**
2951
2724
  * Turn one app's constructs into its generated entry.
2952
2725
  *
2953
2726
  * `gkm build` and `gkm dev` both run this. They used to be two copies of it,
@@ -2956,8 +2729,8 @@ async function buildCommand(options) {
2956
2729
  * dev did not, and failed on exactly the apps the build had learned to handle.
2957
2730
  */
2958
2731
  async function buildApp(input) {
2959
- const { config, workspaceRoot, appRoot, providers, enableOpenApi, cacheBackend, production, telescope, studio, hooks, bustCache = false } = input;
2960
- const constructGlobs = typeof config.constructs === "string" || Array.isArray(config.constructs) ? config.constructs : void 0;
2732
+ const { config, workspaceRoot, appRoot, target, enableOpenApi, cacheBackend, production, telescope, studio, hooks, bustCache = false } = input;
2733
+ const constructGlobs = config.constructs;
2961
2734
  const constructSources = {};
2962
2735
  const declared = constructGlobs ? await discover({
2963
2736
  patterns: constructGlobs,
@@ -3023,50 +2796,58 @@ async function buildApp(input) {
3023
2796
  logger$8.log("No endpoints, functions, crons, subscribers, queues, or topics found to process");
3024
2797
  return {};
3025
2798
  }
3026
- const rootOutputDir = join(appRoot, ".gkm");
3027
- await mkdir(rootOutputDir, { recursive: true });
3028
- let result = {};
3029
- for (const provider of providers) {
3030
- const providerResult = await buildForProvider(provider, buildContext, appRoot, declared, endpointGenerator, functionGenerator, cronGenerator, subscriberGenerator, queueGenerator, topicGenerator, allEndpoints, allFunctions, allCrons, allSubscribers, allQueues, allTopics, enableOpenApi, input.skipBundle ?? false, input.stage);
3031
- if (providerResult.masterKey) result = providerResult;
3032
- }
2799
+ const result = await buildForTarget(target, buildContext, appRoot, workspaceRoot, endpointGenerator, functionGenerator, cronGenerator, subscriberGenerator, queueGenerator, topicGenerator, allEndpoints, allFunctions, allCrons, allSubscribers, allQueues, allTopics, enableOpenApi, input.skipBundle ?? false, input.stage);
3033
2800
  await generateOpenApi(allEndpoints.map(({ construct }) => construct), { openapi: config.openapi });
3034
2801
  return result;
3035
2802
  }
3036
- async function buildForProvider(provider, context, appRoot, discovered, endpointGenerator, functionGenerator, cronGenerator, subscriberGenerator, queueGenerator, topicGenerator, endpoints, functions, crons, subscribers, queues, topics, enableOpenApi, skipBundle, stage) {
3037
- const rootOutputDir = join(appRoot, ".gkm");
3038
- const outputDir = join(rootOutputDir, provider);
2803
+ async function buildForTarget(target, context, appRoot, workspaceRoot, endpointGenerator, functionGenerator, cronGenerator, subscriberGenerator, queueGenerator, topicGenerator, endpoints, functions, crons, subscribers, queues, topics, enableOpenApi, skipBundle, stage) {
2804
+ const outputDir = join(appRoot, ".gkm", target);
2805
+ await Promise.all([...SUPERSEDED_OUTPUT.map((dir) => join(appRoot, ".gkm", dir)), ...appRoot === workspaceRoot ? [] : [join(appRoot, ".gkm", "manifest")]].map((dir) => rm(dir, {
2806
+ recursive: true,
2807
+ force: true
2808
+ })));
2809
+ if (target === "aws") await rm(outputDir, {
2810
+ recursive: true,
2811
+ force: true
2812
+ });
3039
2813
  await mkdir(outputDir, { recursive: true });
3040
- logger$8.log(`\nGenerating handlers for provider: ${provider}`);
2814
+ logger$8.log(`\nGenerating handlers for ${target}`);
2815
+ const options = {
2816
+ target,
2817
+ root: workspaceRoot
2818
+ };
3041
2819
  const [routes, functionInfos, cronInfos, subscriberInfos, queueInfos, topicInfos] = await Promise.all([
3042
2820
  endpointGenerator.build(context, endpoints, outputDir, {
3043
- provider,
2821
+ ...options,
3044
2822
  enableOpenApi
3045
2823
  }),
3046
- functionGenerator.build(context, functions, outputDir, { provider }),
3047
- cronGenerator.build(context, crons, outputDir, { provider }),
3048
- subscriberGenerator.build(context, subscribers, outputDir, { provider }),
3049
- queueGenerator.build(context, queues, outputDir, { provider }),
3050
- topicGenerator.build(context, topics, outputDir, { provider })
2824
+ functionGenerator.build(context, functions, outputDir, options),
2825
+ cronGenerator.build(context, crons, outputDir, options),
2826
+ subscriberGenerator.build(context, subscribers, outputDir, options),
2827
+ queueGenerator.build(context, queues, outputDir, options),
2828
+ topicGenerator.build(context, topics, outputDir, options)
3051
2829
  ]);
3052
- logger$8.log(`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers, ${queueInfos.length} queues, ${topicInfos.length} topics for ${provider}`);
3053
- const manifestRoutes = assembleManifestField(routes, endpoints);
3054
- const manifestFunctions = assembleManifestField(functionInfos, functions);
3055
- const manifestCrons = assembleManifestField(cronInfos, crons);
3056
- const manifestQueues = assembleManifestField(queueInfos, queues);
3057
- const manifestTopics = assembleManifestField(topicInfos, topics);
3058
- const manifestSubscribers = assembleManifestField(subscriberInfos, subscribers);
3059
- if (provider === "server") {
3060
- const serverRouteField = assembleManifestField(await Promise.all(endpoints.map(async ({ construct }) => ({
3061
- path: construct._path,
3062
- method: construct.method,
3063
- handler: "",
3064
- authorizer: construct.authorizer?.name ?? "none"
3065
- }))), endpoints);
3066
- await generateServerManifest(rootOutputDir, {
3067
- handler: relative(appRoot, join(outputDir, "app.ts")),
3068
- endpoints: relative(appRoot, join(outputDir, "endpoints.ts"))
3069
- }, serverRouteField, manifestSubscribers, manifestQueues, manifestTopics);
2830
+ logger$8.log(`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers, ${queueInfos.length} queues, ${topicInfos.length} topics for ${target}`);
2831
+ const fields = {
2832
+ ...context.surface ? { surface: context.surface.id } : {},
2833
+ functions: functionInfos,
2834
+ crons: cronInfos,
2835
+ queues: queueInfos,
2836
+ subscribers: subscriberInfos
2837
+ };
2838
+ if (target === "server") {
2839
+ const handler = relative(workspaceRoot, join(outputDir, "app.ts"));
2840
+ const built = {
2841
+ ...fields,
2842
+ routes: await Promise.all(endpoints.map(async ({ construct }) => ({
2843
+ path: construct._path,
2844
+ method: construct.method,
2845
+ handler,
2846
+ environment: await construct.getEnvironment({ markOptional: context.markOptional }),
2847
+ dependencies: construct.constructs,
2848
+ authorizer: construct.authorizer?.name ?? "none"
2849
+ })))
2850
+ };
3070
2851
  let masterKey;
3071
2852
  if (context.production?.bundle && !skipBundle) {
3072
2853
  logger$8.log(`\n📦 Bundling production server...`);
@@ -3093,12 +2874,15 @@ async function buildForProvider(provider, context, appRoot, discovered, endpoint
3093
2874
  logger$8.log(` Deploy with: GKM_MASTER_KEY=${masterKey}`);
3094
2875
  }
3095
2876
  }
3096
- return { masterKey };
3097
- } else await generateAwsManifest(rootOutputDir, manifestRoutes, manifestFunctions, manifestCrons, manifestSubscribers, manifestQueues, manifestTopics, discovered, {
3098
- cache: context.cacheBackend,
3099
- email: context.emailBackend
3100
- });
3101
- return {};
2877
+ return {
2878
+ masterKey,
2879
+ built
2880
+ };
2881
+ }
2882
+ return { built: {
2883
+ ...fields,
2884
+ routes
2885
+ } };
3102
2886
  }
3103
2887
  /**
3104
2888
  * Detect available package manager.
@@ -3120,8 +2904,8 @@ function detectPackageManager$2() {
3120
2904
  *
3121
2905
  * @internal Exported for testing
3122
2906
  */
3123
- function getTurboCommand(pm, filters = []) {
3124
- const filterArgs = (typeof filters === "string" ? [filters] : filters).map((f) => ` --filter=${f}`).join("");
2907
+ function getTurboCommand(pm, filters = [], { only = false } = {}) {
2908
+ const filterArgs = (typeof filters === "string" ? [filters] : filters).map((f) => ` --filter=${f}`).join("") + (only ? " --only" : "");
3125
2909
  switch (pm) {
3126
2910
  case "pnpm": return `pnpm exec turbo run build${filterArgs}`;
3127
2911
  case "yarn": return `yarn turbo run build${filterArgs}`;
@@ -3141,10 +2925,11 @@ function getTurboCommand(pm, filters = []) {
3141
2925
  *
3142
2926
  * @internal Exported for testing
3143
2927
  */
3144
- function turboFilters(workspace) {
2928
+ function turboFilters(workspace, { exclude } = {}) {
3145
2929
  const filters = [];
3146
2930
  const unpackaged = [];
3147
- for (const appName of Object.keys(workspace.apps)) {
2931
+ for (const [appName, app] of Object.entries(workspace.apps)) {
2932
+ if (app.type === exclude) continue;
3148
2933
  const name = appPackageName(workspace, appName);
3149
2934
  if (name) filters.push(name);
3150
2935
  else unpackaged.push(appName);
@@ -3183,31 +2968,59 @@ async function workspaceBuildCommand(workspace, options) {
3183
2968
  if (options.production) logger$8.log(` 🏭 Production mode enabled`);
3184
2969
  const buildOrder = getAppBuildOrder(workspace);
3185
2970
  logger$8.log(` Build order: ${buildOrder.join(" → ")}`);
3186
- const pm = detectPackageManager$2();
3187
- logger$8.log(`\n📦 Using ${pm} with Turbo for parallel builds...\n`);
3188
2971
  try {
3189
- const { filters, unpackaged } = turboFilters(workspace);
3190
- if (unpackaged.length > 0) throw new Error(`No package.json for workspace app(s): ${unpackaged.join(", ")}. Each app needs one — turbo builds packages, and gkm reads the name back to know which app it is building.`);
3191
- const turboCommand = getTurboCommand(pm, filters);
3192
- logger$8.log(`Running: ${turboCommand}`);
3193
- await new Promise((resolve, reject) => {
3194
- const child = spawn(turboCommand, {
3195
- shell: true,
3196
- cwd: workspace.root,
3197
- stdio: "inherit",
3198
- env: {
3199
- ...process.env,
3200
- NODE_ENV: options.production ? "production" : "development"
3201
- }
3202
- });
3203
- child.on("close", (code) => {
3204
- if (code === 0) resolve();
3205
- else reject(/* @__PURE__ */ new Error(`Turbo build failed with exit code ${code}`));
3206
- });
3207
- child.on("error", (err) => {
3208
- reject(err);
2972
+ const target = options.provider ?? providerOf(workspace);
2973
+ const builds = [];
2974
+ for (const appName of buildOrder) {
2975
+ const app = workspace.apps[appName];
2976
+ const config = getAppGkmConfig(workspace, appName);
2977
+ if (!app || app.type !== "backend" || !config) continue;
2978
+ logger$8.log(`\n⚙️ ${appName}`);
2979
+ const output = await buildOneApp({
2980
+ config,
2981
+ options,
2982
+ workspace,
2983
+ appRoot: join(workspace.root, app.path),
2984
+ target
3209
2985
  });
2986
+ if (output.built) builds.push(output.built);
2987
+ }
2988
+ await writeManifest({
2989
+ workspaceRoot: workspace.root,
2990
+ target,
2991
+ builds,
2992
+ constructs: await discover({
2993
+ patterns: allConstructGlobs(workspace),
2994
+ cwd: workspace.root
2995
+ }),
2996
+ backends: backendsOf(workspace)
3210
2997
  });
2998
+ const { filters, unpackaged } = turboFilters(workspace, { exclude: "backend" });
2999
+ if (unpackaged.length > 0) throw new Error(`No package.json for workspace app(s): ${unpackaged.join(", ")}. Each app needs one — turbo builds packages, and gkm reads the name back to know which app it is building.`);
3000
+ const pm = detectPackageManager$2();
3001
+ const turboCommand = getTurboCommand(pm, filters, { only: true });
3002
+ if (filters.length > 0) {
3003
+ logger$8.log(`\n📦 Using ${pm} with Turbo for the other apps...\n`);
3004
+ logger$8.log(`Running: ${turboCommand}`);
3005
+ await new Promise((resolve, reject) => {
3006
+ const child = spawn(turboCommand, {
3007
+ shell: true,
3008
+ cwd: workspace.root,
3009
+ stdio: "inherit",
3010
+ env: {
3011
+ ...process.env,
3012
+ NODE_ENV: options.production ? "production" : "development"
3013
+ }
3014
+ });
3015
+ child.on("close", (code) => {
3016
+ if (code === 0) resolve();
3017
+ else reject(/* @__PURE__ */ new Error(`Turbo build failed with exit code ${code}`));
3018
+ });
3019
+ child.on("error", (err) => {
3020
+ reject(err);
3021
+ });
3022
+ });
3023
+ }
3211
3024
  for (const [appName, app] of apps) {
3212
3025
  const outputPath = getAppOutputPath(workspace, appName, app);
3213
3026
  results.push({
@@ -3250,23 +3063,11 @@ function getAppOutputPath(workspace, _appName, app) {
3250
3063
  }
3251
3064
  return join(appPath, ".gkm");
3252
3065
  }
3253
- /**
3254
- * Format routes for logging, handling PartitionedRoutes.
3255
- */
3066
+ /** Format routes for logging. */
3256
3067
  function formatRoutes(routes) {
3257
- if (isPartitionedRoutes(routes)) return `${Array.isArray(routes.paths) ? routes.paths.join(", ") : routes.paths} (partitioned)`;
3258
3068
  return Array.isArray(routes) ? routes.join(", ") : routes;
3259
3069
  }
3260
3070
  /**
3261
- * Assemble a ManifestField from build infos and constructs.
3262
- * If any construct has a partition, returns a Record<string, T[]>.
3263
- * Otherwise, returns a flat T[].
3264
- */
3265
- function assembleManifestField(infos, constructs) {
3266
- if (!hasPartitions(constructs)) return infos;
3267
- return groupInfosByPartition(infos, constructs);
3268
- }
3269
- /**
3270
3071
  * Format a fatal error for display.
3271
3072
  * Always includes the full stack trace since these are process-ending errors.
3272
3073
  */
@@ -5412,9 +5213,8 @@ async function dockerCommand(options) {
5412
5213
  logger$6.log("📦 Detected workspace configuration");
5413
5214
  return workspaceDockerCommand(loadedConfig.workspace);
5414
5215
  }
5415
- const config = await loadConfig();
5416
- const dockerConfig = resolveDockerConfig(config);
5417
- const healthCheckPath = (typeof config.providers?.server === "object" ? config.providers.server : void 0)?.production?.healthCheck ?? "/health";
5216
+ const dockerConfig = resolveDockerConfig(await loadConfig());
5217
+ const healthCheckPath = "/health";
5418
5218
  const useSlim = options.slim === true;
5419
5219
  if (useSlim) {
5420
5220
  const distDir = join(process.cwd(), ".gkm", "server", "dist");
@@ -7420,6 +7220,10 @@ async function workspaceDeployCommand(configured, options) {
7420
7220
  } else logger$4.log(` Deploying all apps: ${appsToDeployNames.join(", ")}`);
7421
7221
  const dokployApps = appsToDeployNames.filter((name) => {
7422
7222
  const target = workspace.apps[name].resolvedDeployTarget;
7223
+ if (target === "sst") {
7224
+ logger$4.log(` ⚠️ Skipping ${name}: it deploys with SST — run \`gkm build && sst deploy --stage ${stage}\``);
7225
+ return false;
7226
+ }
7423
7227
  if (!isDeployTargetSupported(target)) {
7424
7228
  logger$4.log(` ⚠️ Skipping ${name}: ${getDeployTargetError(target, name)}`);
7425
7229
  return false;
@@ -7940,66 +7744,6 @@ async function createApi(endpoint) {
7940
7744
  });
7941
7745
  }
7942
7746
  /**
7943
- * Update gkm.config.ts with Dokploy configuration
7944
- */
7945
- /**
7946
- * Whether the file's `dokploy` block is more than a flat literal.
7947
- *
7948
- * A spread, a `process.env`, a ternary or a nested object all defeat the
7949
- * single-level regex below — the first two by being expressions it would
7950
- * stringify away, the last by ending the match at the wrong brace.
7951
- */
7952
- function unsafeToRewrite(content) {
7953
- const start = content.indexOf("dokploy:");
7954
- if (start === -1) return false;
7955
- const block = content.slice(start, content.indexOf("\n },", start) + 1);
7956
- return /\.\.\.|process\.env|\?\s|`|\{[^}]*\{/s.test(block);
7957
- }
7958
- async function updateConfig(config, cwd = process.cwd()) {
7959
- const configPath = join(cwd, "gkm.config.ts");
7960
- if (!existsSync(configPath)) {
7961
- logger$3.warn("\n gkm.config.ts not found. Add this configuration manually:\n");
7962
- logger$3.log(` providers: {`);
7963
- logger$3.log(` dokploy: {`);
7964
- logger$3.log(` endpoint: '${config.endpoint}',`);
7965
- logger$3.log(` projectId: '${config.projectId}',`);
7966
- logger$3.log(` applicationId: '${config.applicationId}',`);
7967
- logger$3.log(` },`);
7968
- logger$3.log(` },`);
7969
- return;
7970
- }
7971
- const content = await readFile(configPath, "utf-8");
7972
- if (unsafeToRewrite(content)) {
7973
- logger$3.warn("\n gkm.config.ts holds a dokploy block this cannot safely rewrite —\n it contains an expression or a nested object. Leaving it alone.");
7974
- logger$3.log(`\n Discovered, if you want them written down:`);
7975
- logger$3.log(` projectId: '${config.projectId}'`);
7976
- logger$3.log(` applicationId: '${config.applicationId}'`);
7977
- if (config.registryId) logger$3.log(` registryId: '${config.registryId}'`);
7978
- return;
7979
- }
7980
- if (content.includes("dokploy:") && content.includes("applicationId:")) {
7981
- logger$3.log("\n Dokploy config already exists in gkm.config.ts");
7982
- logger$3.log(" Updating with new values...");
7983
- }
7984
- const registryLine = config.registryId ? `\n\t\t\tregistryId: '${config.registryId}',` : "";
7985
- const dokployConfigStr = `dokploy: {
7986
- endpoint: '${config.endpoint}',
7987
- projectId: '${config.projectId}',
7988
- applicationId: '${config.applicationId}',${registryLine}
7989
- }`;
7990
- let newContent;
7991
- if (content.includes("providers:")) {
7992
- if (content.includes("dokploy:")) newContent = content.replace(/dokploy:\s*\{[^}]*\}/s, dokployConfigStr);
7993
- else newContent = content.replace(/providers:\s*\{/, `providers: {\n\t\t${dokployConfigStr},`);
7994
- } else newContent = content.replace(/}\s*\)\s*;?\s*$/, `
7995
- providers: {
7996
- ${dokployConfigStr},
7997
- },
7998
- });`);
7999
- await writeFile(configPath, newContent);
8000
- logger$3.log("\n ✓ Updated gkm.config.ts with Dokploy configuration");
8001
- }
8002
- /**
8003
7747
  * Initialize Dokploy deployment configuration
8004
7748
  */
8005
7749
  async function deployInitCommand(options) {
@@ -8052,7 +7796,6 @@ async function deployInitCommand(options) {
8052
7796
  projectId,
8053
7797
  applicationId: application.applicationId
8054
7798
  };
8055
- await updateConfig(config);
8056
7799
  logger$3.log(`\n✅ Dokploy deployment initialized!`);
8057
7800
  logger$3.log(`\n📋 Configuration:`);
8058
7801
  logger$3.log(` Project ID: ${projectId}`);
@@ -9322,26 +9065,26 @@ function loadPackageJson() {
9322
9065
  * Run: pnpm --filter @geekmidas/cli sync-versions
9323
9066
  */
9324
9067
  const GEEKMIDAS_VERSIONS = {
9325
- "@geekmidas/audit": "~10.0.0-alpha.44",
9326
- "@geekmidas/auth": "~10.0.0-alpha.44",
9327
- "@geekmidas/cache": "~10.0.0-alpha.44",
9328
- "@geekmidas/client": "~10.0.0-alpha.44",
9329
- "@geekmidas/cloud": "~10.0.0-alpha.44",
9330
- "@geekmidas/constructs": "~10.0.0-alpha.44",
9331
- "@geekmidas/db": "~10.0.0-alpha.44",
9332
- "@geekmidas/emailkit": "~10.0.0-alpha.44",
9333
- "@geekmidas/envkit": "~10.0.0-alpha.44",
9334
- "@geekmidas/errors": "~10.0.0-alpha.44",
9335
- "@geekmidas/events": "~10.0.0-alpha.44",
9336
- "@geekmidas/logger": "~10.0.0-alpha.44",
9337
- "@geekmidas/manifest": "~10.0.0-alpha.44",
9338
- "@geekmidas/rate-limit": "~10.0.0-alpha.44",
9339
- "@geekmidas/schema": "~10.0.0-alpha.44",
9340
- "@geekmidas/services": "~10.0.0-alpha.44",
9341
- "@geekmidas/storage": "~10.0.0-alpha.44",
9342
- "@geekmidas/studio": "~10.0.0-alpha.44",
9343
- "@geekmidas/telescope": "~10.0.0-alpha.44",
9344
- "@geekmidas/testkit": "~10.0.0-alpha.44",
9068
+ "@geekmidas/audit": "~10.0.0-alpha.46",
9069
+ "@geekmidas/auth": "~10.0.0-alpha.46",
9070
+ "@geekmidas/cache": "~10.0.0-alpha.46",
9071
+ "@geekmidas/client": "~10.0.0-alpha.46",
9072
+ "@geekmidas/cloud": "~10.0.0-alpha.46",
9073
+ "@geekmidas/constructs": "~10.0.0-alpha.46",
9074
+ "@geekmidas/db": "~10.0.0-alpha.46",
9075
+ "@geekmidas/emailkit": "~10.0.0-alpha.46",
9076
+ "@geekmidas/envkit": "~10.0.0-alpha.46",
9077
+ "@geekmidas/errors": "~10.0.0-alpha.46",
9078
+ "@geekmidas/events": "~10.0.0-alpha.46",
9079
+ "@geekmidas/logger": "~10.0.0-alpha.46",
9080
+ "@geekmidas/manifest": "~10.0.0-alpha.46",
9081
+ "@geekmidas/rate-limit": "~10.0.0-alpha.46",
9082
+ "@geekmidas/schema": "~10.0.0-alpha.46",
9083
+ "@geekmidas/services": "~10.0.0-alpha.46",
9084
+ "@geekmidas/storage": "~10.0.0-alpha.46",
9085
+ "@geekmidas/studio": "~10.0.0-alpha.46",
9086
+ "@geekmidas/telescope": "~10.0.0-alpha.46",
9087
+ "@geekmidas/testkit": "~10.0.0-alpha.46",
9345
9088
  "@geekmidas/cli": `~${loadPackageJson().version}`
9346
9089
  };
9347
9090
  //#endregion
@@ -10868,7 +10611,7 @@ function deployPackage(options) {
10868
10611
  case "sst": {
10869
10612
  const v = GEEKMIDAS_VERSIONS;
10870
10613
  return {
10871
- scripts: perStage(options, (stage) => `gkm build --provider aws && sst deploy --stage ${stage}`),
10614
+ scripts: perStage(options, (stage) => `gkm build && sst deploy --stage ${stage}`),
10872
10615
  dependencies: {
10873
10616
  "@geekmidas/cloud": v["@geekmidas/cloud"],
10874
10617
  "@geekmidas/db": v["@geekmidas/db"],
@@ -10933,7 +10676,7 @@ export default $config({
10933
10676
 
10934
10677
  async run() {
10935
10678
  const { App, fromManifest, Stack } = await import('@geekmidas/cloud/sst');
10936
- // Written by \`gkm build --provider aws\`. Imported rather than discovered,
10679
+ // Written by \`gkm build\`. Imported rather than discovered,
10937
10680
  // so SST's toolchain never evaluates the application's own modules.
10938
10681
  const { backends, constructs } = await import('./.gkm/manifest/aws.js');
10939
10682
 
@@ -11171,8 +10914,11 @@ coverage/
11171
10914
  ${isSst ? "\n# SST\n.sst/\n" : ""}`;
11172
10915
  const tsConfig = {
11173
10916
  compilerOptions: {
10917
+ paths: {
10918
+ ...isFullstack ? { [`@${options.name}/constructs/*`]: ["./constructs/*"] } : {},
10919
+ [`@${options.name}/manifest`]: [`./.gkm/manifest/${isSst ? "aws" : "server"}.ts`]
10920
+ },
11174
10921
  ...isFullstack ? {
11175
- paths: { [`@${options.name}/constructs/*`]: ["./constructs/*"] },
11176
10922
  allowImportingTsExtensions: true,
11177
10923
  noEmit: true
11178
10924
  } : {},
@@ -11313,7 +11059,7 @@ export default defineWorkspace({
11313
11059
  // \`production-${options.name}-database\` on Dokploy and on AWS alike.
11314
11060
  name: '${options.name}',
11315
11061
  ${stagesBlock(options.stages)}
11316
-
11062
+ ${deployBlock(options)}
11317
11063
  // Every kind, in every app. A declared database is why a Postgres exists, a
11318
11064
  // declared bucket is why MinIO does, a declared topic is why a broker does —
11319
11065
  // none of it listed here. It is also where the apps come from: a
@@ -11331,6 +11077,18 @@ ${workspaceConstructsGlobs(options.routesStructure, dirname(options.apiPath)).ma
11331
11077
  `;
11332
11078
  }
11333
11079
  /**
11080
+ * Where it deploys, when that is not the default Dokploy: what a bare
11081
+ * `gkm build` builds for, and which backends a cache or a broker resolves to.
11082
+ */
11083
+ function deployBlock(options) {
11084
+ if (options.deployTarget !== "sst") return "";
11085
+ return `
11086
+ // Deployed with \`sst deploy\`, from the manifest \`gkm build\` writes to
11087
+ // \`.gkm/manifest/aws.ts\`. Each construct becomes a managed AWS resource.
11088
+ deploy: { default: 'sst' },
11089
+ `;
11090
+ }
11091
+ /**
11334
11092
  * Where a deployed stage's secrets live. On AWS that is SSM in the stage's own
11335
11093
  * account, which the deploy job reaches with the role it already assumed; a
11336
11094
  * server target keeps the encrypted local file, the default.
@@ -11356,7 +11114,7 @@ function secretsStore(options) {
11356
11114
  */
11357
11115
  function generateRootConstructs(options) {
11358
11116
  if (!options.monorepo || options.template !== "fullstack") return [];
11359
- const { name, frontendFramework } = options;
11117
+ const { frontendFramework } = options;
11360
11118
  const { cache, uploads, mail } = options.constructs;
11361
11119
  const db = databaseFor();
11362
11120
  const files = [];
@@ -12116,7 +11874,7 @@ const serverlessTemplate = {
12116
11874
  },
12117
11875
  scripts: {
12118
11876
  dev: "gkm dev",
12119
- build: "gkm build --provider aws-apigatewayv2",
11877
+ build: "gkm build --provider aws",
12120
11878
  test: "vitest",
12121
11879
  "test:once": "vitest run",
12122
11880
  typecheck: "tsc --noEmit",
@@ -12125,7 +11883,7 @@ const serverlessTemplate = {
12125
11883
  "fmt:check": "biome format ."
12126
11884
  },
12127
11885
  files: (options) => {
12128
- const { loggerType, routesStructure, name } = options;
11886
+ const { loggerType, routesStructure } = options;
12129
11887
  const loggerContent = `import { createLogger } from '@geekmidas/logger/${loggerType}';
12130
11888
 
12131
11889
  export const logger = createLogger();
@@ -12410,10 +12168,6 @@ export const telescope = new Telescope({
12410
12168
  */
12411
12169
  const OPENAPI_OUTPUT_PATH = "./.gkm/openapi/api.ts";
12412
12170
  /**
12413
- * Endpoints output path (generated by gkm dev/build)
12414
- */
12415
- const ENDPOINTS_OUTPUT_PATH = "./.gkm/server/endpoints.ts";
12416
- /**
12417
12171
  * All available templates
12418
12172
  */
12419
12173
  const templates = {
@@ -12626,10 +12380,7 @@ function generatePackageJson(options, template) {
12626
12380
  private: true,
12627
12381
  type: "module",
12628
12382
  ...!monorepo && options.packageManager === "pnpm" ? { packageManager: PNPM_VERSION } : {},
12629
- exports: {
12630
- "./client": OPENAPI_OUTPUT_PATH,
12631
- "./endpoints": ENDPOINTS_OUTPUT_PATH
12632
- },
12383
+ exports: { "./client": OPENAPI_OUTPUT_PATH },
12633
12384
  scripts,
12634
12385
  dependencies: sortObject(dependencies),
12635
12386
  devDependencies: sortObject(devDependencies)
@@ -15355,7 +15106,7 @@ function printNextSteps(projectName, options, pkgManager) {
15355
15106
  }
15356
15107
  if (options.deployTarget === "sst") {
15357
15108
  console.log("🚀 Deployment (AWS, through SST):");
15358
- for (const stage of deployed) console.log(` ${getRunCommand(pkgManager, `deploy:${stage}`)} # gkm build --provider aws, then sst deploy`);
15109
+ for (const stage of deployed) console.log(` ${getRunCommand(pkgManager, `deploy:${stage}`)} # gkm build, then sst deploy`);
15359
15110
  console.log(` Uses your AWS credentials, in ${options.region}.`);
15360
15111
  if (options.constructs.mail) console.log(" Set MAIL_FROM to a sender verified in SES first.");
15361
15112
  console.log("");
@@ -16015,7 +15766,7 @@ async function testCommand(options = {}) {
16015
15766
  root: workspace.root,
16016
15767
  targets: [workspace.root, ...Object.values(workspace.apps).map((app) => isAbsolute(app.path) ? app.path : join(workspace.root, app.path))],
16017
15768
  patterns: constructGlobs(workspace),
16018
- cacheBackend: backendsOf(workspace).cache,
15769
+ cacheBackend: backendsOf$1(workspace).cache,
16019
15770
  stage: TEST_STAGE,
16020
15771
  env: finalCredentials,
16021
15772
  factories: join(workspace.root, workspace.test.factories ?? "test/factories")
@@ -16090,8 +15841,8 @@ function testDatabases(reconciled) {
16090
15841
  * starts without the reference data the app needs.
16091
15842
  */
16092
15843
  async function migrateTestStage(workspace, env) {
16093
- const { migrateAndSeed } = await import("./migrate-BRklCm7b.mjs");
16094
- const { discover } = await import("./discover-BLhf7V_J.mjs").then((n) => n.n);
15844
+ const { migrateAndSeed } = await import("./migrate-B3opyC3o.mjs");
15845
+ const { discover } = await import("./discover-Cf2jAcgF.mjs").then((n) => n.n);
16095
15846
  const sources = {};
16096
15847
  const manifest = await discover({
16097
15848
  patterns: constructGlobs(workspace),
@@ -16456,29 +16207,13 @@ program.command("setup").description("Reconcile declared constructs — containe
16456
16207
  process.exit(1);
16457
16208
  }
16458
16209
  });
16459
- program.command("build").description("Build handlers and the manifest from declared constructs").option("--provider <provider>", "Target provider for generated handlers (aws, server)").option("--providers <providers>", "[DEPRECATED] Use --provider instead. Target providers for generated handlers (comma-separated)").option("--enable-openapi", "Enable OpenAPI documentation generation for server builds").option("--production", "Build for production (no dev tools, bundled output)").option("--skip-bundle", "Skip bundling step in production build").option("--stage <stage>", "Inject encrypted secrets for deployment stage").option("--mark-optional", "Suffix optional env vars with ? in manifest envVars field (e.g. PORT?)").action(async (options) => {
16210
+ program.command("build").description("Build handlers and the manifest from declared constructs").option("--provider <provider>", "Build for aws or server (default: where gkm.config.ts deploys)").option("--enable-openapi", "Enable OpenAPI documentation generation for server builds").option("--production", "Build for production (no dev tools, bundled output)").option("--skip-bundle", "Skip bundling step in production build").option("--stage <stage>", "Inject encrypted secrets for deployment stage").option("--mark-optional", "Suffix optional env vars with ? in manifest envVars field (e.g. PORT?)").action(async (options) => {
16460
16211
  try {
16461
16212
  const globalOptions = program.opts();
16462
16213
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16463
- if (options.provider) {
16464
- if (!["aws", "server"].includes(options.provider)) process.exit(1);
16465
- await buildCommand({
16466
- provider: options.provider,
16467
- enableOpenApi: options.enableOpenapi || false,
16468
- production: options.production || false,
16469
- skipBundle: options.skipBundle || false,
16470
- stage: options.stage,
16471
- markOptional: options.markOptional || false
16472
- });
16473
- } else if (options.providers) await buildCommand({
16474
- providers: [...new Set(options.providers.split(",").map((p) => p.trim()))],
16475
- enableOpenApi: options.enableOpenapi || false,
16476
- production: options.production || false,
16477
- skipBundle: options.skipBundle || false,
16478
- stage: options.stage,
16479
- markOptional: options.markOptional || false
16480
- });
16481
- else await buildCommand({
16214
+ if (options.provider !== void 0 && !isMainProvider(options.provider)) throw new UnknownBuildProvider(options.provider);
16215
+ await buildCommand({
16216
+ ...options.provider ? { provider: options.provider } : {},
16482
16217
  enableOpenApi: options.enableOpenapi || false,
16483
16218
  production: options.production || false,
16484
16219
  skipBundle: options.skipBundle || false,
@@ -16537,7 +16272,7 @@ program.command("migrate").description("Apply each database construct's migratio
16537
16272
  try {
16538
16273
  const globalOptions = program.opts();
16539
16274
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16540
- const { migrateCommand } = await import("./migrate-BRklCm7b.mjs");
16275
+ const { migrateCommand } = await import("./migrate-B3opyC3o.mjs");
16541
16276
  await migrateCommand({
16542
16277
  ...options,
16543
16278
  ...construct ? { construct } : {}
@@ -16551,7 +16286,7 @@ program.command("seed").description("Migrate, then run each database construct's
16551
16286
  try {
16552
16287
  const globalOptions = program.opts();
16553
16288
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16554
- const { seedCommand } = await import("./migrate-BRklCm7b.mjs");
16289
+ const { seedCommand } = await import("./migrate-B3opyC3o.mjs");
16555
16290
  await seedCommand({
16556
16291
  ...options,
16557
16292
  ...construct ? { construct } : {}
@@ -16565,7 +16300,7 @@ program.command("migration").description("Write a construct's next migration: an
16565
16300
  try {
16566
16301
  const globalOptions = program.opts();
16567
16302
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16568
- const { migrationCommand } = await import("./migrate-BRklCm7b.mjs");
16303
+ const { migrationCommand } = await import("./migrate-B3opyC3o.mjs");
16569
16304
  await migrationCommand({
16570
16305
  construct,
16571
16306
  ...name ? { name } : {}
@@ -16704,7 +16439,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
16704
16439
  console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
16705
16440
  process.exit(1);
16706
16441
  }
16707
- const { derivedContainers } = await import("./workspace-BKytVCzb.mjs").then((n) => n.o);
16442
+ const { derivedContainers } = await import("./workspace-CHEC80sp.mjs").then((n) => n.o);
16708
16443
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
16709
16444
  if (!result) {
16710
16445
  console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
@@ -16763,7 +16498,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
16763
16498
  try {
16764
16499
  const globalOptions = program.opts();
16765
16500
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16766
- const { deployGithubCommand } = await import("./github-D5MthUlR.mjs");
16501
+ const { deployGithubCommand } = await import("./github-BfpadQWQ.mjs");
16767
16502
  await deployGithubCommand(options);
16768
16503
  } catch (error) {
16769
16504
  console.error(formatError(error));