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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/{config-C21ocYNd.cjs → config-CNz5KaSR.cjs} +16 -45
  2. package/dist/config-CNz5KaSR.cjs.map +1 -0
  3. package/dist/{config-WRdsQYZK.mjs → config-CxP7P-qG.mjs} +17 -46
  4. package/dist/config-CxP7P-qG.mjs.map +1 -0
  5. package/dist/config.cjs +2 -2
  6. package/dist/config.d.cts +2 -2
  7. package/dist/config.d.mts +2 -2
  8. package/dist/config.mjs +2 -2
  9. package/dist/{credentials-BBIRJVp4.mjs → credentials-BK3Z93MX.mjs} +5 -5
  10. package/dist/credentials-BK3Z93MX.mjs.map +1 -0
  11. package/dist/{credentials-G1JwBoXH.cjs → credentials-DzsXZzkx.cjs} +5 -5
  12. package/dist/credentials-DzsXZzkx.cjs.map +1 -0
  13. package/dist/discover-CAlFgYec.mjs.map +1 -1
  14. package/dist/discover-DTmFeloL.cjs.map +1 -1
  15. package/dist/{index-BThUzEIp.d.mts → index-BGGdjueF.d.cts} +2 -2
  16. package/dist/index-BGGdjueF.d.cts.map +1 -0
  17. package/dist/{index-CHB4tSzX.d.cts → index-DEbaleny.d.mts} +2 -2
  18. package/dist/index-DEbaleny.d.mts.map +1 -0
  19. package/dist/index.cjs +307 -121
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.mjs +307 -121
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/{openapi-kkQbsRVj.mjs → openapi-Bw8DWFWz.mjs} +104 -50
  24. package/dist/openapi-Bw8DWFWz.mjs.map +1 -0
  25. package/dist/{openapi-CA0yJUTN.cjs → openapi-D-dx9Akj.cjs} +122 -50
  26. package/dist/openapi-D-dx9Akj.cjs.map +1 -0
  27. package/dist/openapi.cjs +5 -3
  28. package/dist/openapi.d.cts +46 -10
  29. package/dist/openapi.d.cts.map +1 -1
  30. package/dist/openapi.d.mts +46 -10
  31. package/dist/openapi.d.mts.map +1 -1
  32. package/dist/openapi.mjs +4 -4
  33. package/dist/reconcile/public.d.cts +1 -1
  34. package/dist/reconcile/public.d.mts +1 -1
  35. package/dist/setup-8IWPsRIN.cjs +11 -0
  36. package/dist/{setup-DXvRLTZN.mjs → setup-BWMhbW-p.mjs} +3 -3
  37. package/dist/{setup-DXvRLTZN.mjs.map → setup-BWMhbW-p.mjs.map} +1 -1
  38. package/dist/{setup-Cm2UnAdh.cjs → setup-D7L4BgBT.cjs} +3 -3
  39. package/dist/{setup-Cm2UnAdh.cjs.map → setup-D7L4BgBT.cjs.map} +1 -1
  40. package/dist/{setup-ZijS4qeC.mjs → setup-DGIPd2fh.mjs} +4 -4
  41. package/dist/{types-C-arnQ1r.d.mts → types-BLSNdrk0.d.cts} +5 -13
  42. package/dist/{types-C-arnQ1r.d.mts.map → types-BLSNdrk0.d.cts.map} +1 -1
  43. package/dist/{types-abJQuk7m.d.cts → types-C3POXhw3.d.mts} +2 -10
  44. package/dist/{types-abJQuk7m.d.cts.map → types-C3POXhw3.d.mts.map} +1 -1
  45. package/dist/workspace/index.cjs +1 -1
  46. package/dist/workspace/index.d.cts +2 -2
  47. package/dist/workspace/index.d.mts +2 -2
  48. package/dist/workspace/index.mjs +1 -1
  49. package/dist/{workspace-DdJss8I6.cjs → workspace-CGaUAw5A.cjs} +3 -3
  50. package/dist/workspace-CnTqIlB3.mjs +8 -0
  51. package/dist/{workspace-BFMrAKqI.mjs → workspace-DIuZx-cc.mjs} +1 -17
  52. package/dist/{workspace-BFMrAKqI.mjs.map → workspace-DIuZx-cc.mjs.map} +1 -1
  53. package/dist/{workspace-BcucjwvA.cjs → workspace-LKiNyaDy.cjs} +1 -17
  54. package/dist/{workspace-BcucjwvA.cjs.map → workspace-LKiNyaDy.cjs.map} +1 -1
  55. package/package.json +11 -11
  56. package/dist/config-C21ocYNd.cjs.map +0 -1
  57. package/dist/config-WRdsQYZK.mjs.map +0 -1
  58. package/dist/credentials-BBIRJVp4.mjs.map +0 -1
  59. package/dist/credentials-G1JwBoXH.cjs.map +0 -1
  60. package/dist/index-BThUzEIp.d.mts.map +0 -1
  61. package/dist/index-CHB4tSzX.d.cts.map +0 -1
  62. package/dist/openapi-CA0yJUTN.cjs.map +0 -1
  63. package/dist/openapi-kkQbsRVj.mjs.map +0 -1
  64. package/dist/setup-B5_oHHUj.cjs +0 -11
  65. package/dist/workspace-k4l3rsFh.mjs +0 -8
@@ -1,6 +1,7 @@
1
1
  import { ConstructGenerator, isPartitionedRoutes } from "./discover-CAlFgYec.mjs";
2
- import { loadWorkspaceConfig } from "./config-WRdsQYZK.mjs";
3
- import { dirname, join, relative } from "node:path";
2
+ import { loadWorkspaceConfig } from "./config-CxP7P-qG.mjs";
3
+ import { dirname, join, relative, resolve } from "node:path";
4
+ import { kebabCase } from "@geekmidas/manifest";
4
5
  import { mkdir, writeFile } from "node:fs/promises";
5
6
  import { spawn } from "node:child_process";
6
7
  import { Endpoint } from "@geekmidas/constructs/endpoints";
@@ -602,24 +603,21 @@ export async function setupEndpoints(
602
603
  *
603
604
  * Falls back to the configured paths for an app that declares no surface.
604
605
  */
605
- function runtimeFor(context, fromDir) {
606
- const module = context.surface?.module;
606
+ function runtimeFor(context, fromDir, owner) {
607
+ const module = owner ? context.owners?.[owner] ?? context.surface?.module : context.surface?.module;
607
608
  if (module) {
608
609
  const specifier = importSpecifier(fromDir, module.specifier);
609
610
  return {
610
611
  imports: `import { ${module.exportName} as __surface } from '${specifier}';`,
611
612
  bindings: [
612
- "// What this surface was declared with. Objects, not module paths:",
613
- "// the entry imports the surface, so there is nothing to print.",
613
+ "// What this construct was declared with. Objects, not module paths:",
614
+ "// the entry imports the owner, so there is nothing to print.",
614
615
  "const envParser = __surface.envParser;",
615
616
  "const logger = __surface.logger;"
616
617
  ].join("\n")
617
618
  };
618
619
  }
619
- return {
620
- imports: [`import ${context.envParserImportPattern} from '${importSpecifier(fromDir, context.envParserPath)}';`, `import ${context.loggerImportPattern} from '${importSpecifier(fromDir, context.loggerPath)}';`].join("\n"),
621
- bindings: ""
622
- };
620
+ throw new Error(`No owner to take a logger and an environment parser from${owner ? ` for "${owner}"` : ""}. Build it from a \`RestApi\` or a \`Worker\`.`);
623
621
  }
624
622
  /** A relative specifier ESM will accept: always prefixed, never bare. */
625
623
  function importSpecifier(fromDir, target) {
@@ -992,6 +990,7 @@ import type { Hono as HonoType } from 'hono';
992
990
  import { setupEndpoints } from './endpoints.js';
993
991
  import { setupSubscribers } from './subscribers.js';
994
992
  import { setupQueues } from './queues.js';
993
+ import { setupCrons } from './crons.js';
995
994
  ${runtime.imports}
996
995
  ${telescopeImports}
997
996
  ${studioImports}
@@ -1079,6 +1078,12 @@ ${afterSetupCall}
1079
1078
  logger.error({ error }, 'Failed to start queue workers');
1080
1079
  });
1081
1080
 
1081
+ // Schedule this app's crons. Caught like the others: a scheduler that
1082
+ // cannot start is not a reason for the HTTP server not to.
1083
+ await setupCrons(envParser, logger).catch((error) => {
1084
+ logger.error({ error }, 'Failed to schedule crons');
1085
+ });
1086
+
1082
1087
  logger.info({ port }, 'Starting server');
1083
1088
 
1084
1089
  // Start HTTP server using provided serve function
@@ -1149,8 +1154,7 @@ export const handler = ${exportName};
1149
1154
  async generateProductionAppFile(outputDir, context) {
1150
1155
  const appFileName = "app.ts";
1151
1156
  const appPath = join(outputDir, appFileName);
1152
- const relativeLoggerPath = relative(dirname(appPath), context.loggerPath);
1153
- const relativeEnvParserPath = relative(dirname(appPath), context.envParserPath);
1157
+ const runtime = runtimeFor(context, dirname(appPath));
1154
1158
  const production = context.production;
1155
1159
  const healthCheckPath = production.healthCheck;
1156
1160
  const enableGracefulShutdown = production.gracefulShutdown;
@@ -1186,9 +1190,16 @@ export const handler = ${exportName};
1186
1190
  await setupQueues(envParser, logger).catch((error) => {
1187
1191
  logger.error({ error }, 'Failed to start queue workers');
1188
1192
  });
1193
+
1194
+ // Schedule this app's crons. Caught like the others: a scheduler that
1195
+ // cannot start is not a reason for the HTTP server not to.
1196
+ await setupCrons(envParser, logger).catch((error) => {
1197
+ logger.error({ error }, 'Failed to schedule crons');
1198
+ });
1189
1199
  ` : "";
1190
1200
  const subscriberImport = includeSubscribers ? `import { setupSubscribers } from './subscribers.js';
1191
- import { setupQueues } from './queues.js';` : "";
1201
+ import { setupQueues } from './queues.js';
1202
+ import { setupCrons } from './crons.js';` : "";
1192
1203
  const gracefulShutdownCode = enableGracefulShutdown ? `
1193
1204
  // Graceful shutdown handling
1194
1205
  let isShuttingDown = false;
@@ -1218,8 +1229,8 @@ import { Hono } from 'hono';
1218
1229
  import type { Hono as HonoType } from 'hono';
1219
1230
  import { setupEndpoints } from '${endpointsImportPath}';
1220
1231
  ${subscriberImport}
1221
- import ${context.envParserImportPattern} from '${relativeEnvParserPath}';
1222
- import ${context.loggerImportPattern} from '${relativeLoggerPath}';
1232
+ ${runtime.imports}
1233
+ ${runtime.bindings}
1223
1234
  ${hooksImports}
1224
1235
  ${cors.imports}
1225
1236
  ${context.storageDrivers?.imports ?? ""}
@@ -1827,6 +1838,16 @@ function normalizeRoutes(routes) {
1827
1838
  */
1828
1839
  const OPENAPI_OUTPUT_PATH = "./.gkm/openapi.ts";
1829
1840
  /**
1841
+ * Where a surface's generated client lands.
1842
+ *
1843
+ * Named from the surface, so `new RestApi('Webhooks')` writes
1844
+ * `.gkm/openapi/webhooks.ts` — the same kebab form that gives it its container
1845
+ * name and its `WEBHOOKS_URL`.
1846
+ */
1847
+ function openApiPathFor(surfaceId) {
1848
+ return `./.gkm/openapi/${kebabCase(surfaceId)}.ts`;
1849
+ }
1850
+ /**
1830
1851
  * Resolve OpenAPI config from GkmConfig
1831
1852
  */
1832
1853
  function resolveOpenApiConfig(config) {
@@ -1845,33 +1866,66 @@ function resolveOpenApiConfig(config) {
1845
1866
  };
1846
1867
  }
1847
1868
  /**
1848
- * Generate OpenAPI spec from endpoints
1849
- * @returns Object with output path and endpoint count, or null if disabled
1869
+ * Load endpoints from a glob, then generate.
1870
+ *
1871
+ * For the callers that have no endpoints in hand — `gkm openapi`, and `gkm dev`
1872
+ * regenerating on reload. The glob is the app's derived one, off `AppSpec.code`,
1873
+ * not a `routes` field anybody wrote.
1850
1874
  */
1851
- async function generateOpenApi(config, options = {}) {
1875
+ async function generateOpenApiFrom(routes, options = {}) {
1876
+ const loaded = await new EndpointGenerator().load(routes, void 0, options.bustCache);
1877
+ return generateOpenApi(loaded.map(({ construct }) => construct), options);
1878
+ }
1879
+ /**
1880
+ * Generate an OpenAPI spec for each surface these endpoints name.
1881
+ *
1882
+ * Takes the endpoints, not a glob to find them with. The build has already
1883
+ * loaded every one of them to generate handlers, so globbing again was a second
1884
+ * discovery pass over the same files — and the reason this function needed a
1885
+ * `routes` to be told where to look, which is how a retired config field stayed
1886
+ * load-bearing.
1887
+ *
1888
+ * @returns What was written, or null if disabled or there were none
1889
+ */
1890
+ async function generateOpenApi(endpoints, options = {}) {
1852
1891
  const logger = options.silent ? { log: () => {} } : console;
1853
- const openApiConfig = resolveOpenApiConfig(config);
1892
+ const openApiConfig = resolveOpenApiConfig({ openapi: options.openapi });
1854
1893
  if (!openApiConfig.enabled) return null;
1855
- const endpointGenerator = new EndpointGenerator();
1856
- const loadedEndpoints = await endpointGenerator.load(config.routes, void 0, options.bustCache);
1857
- if (loadedEndpoints.length === 0) {
1894
+ if (endpoints.length === 0) {
1858
1895
  logger.log("No valid endpoints found for OpenAPI generation");
1859
1896
  return null;
1860
1897
  }
1861
- const endpoints = loadedEndpoints.map(({ construct }) => construct);
1862
- const outputPath = join(process.cwd(), OPENAPI_OUTPUT_PATH);
1863
- await mkdir(dirname(outputPath), { recursive: true });
1898
+ const bySurface = /* @__PURE__ */ new Map();
1899
+ for (const endpoint of endpoints) {
1900
+ const owner = endpoint.owner ?? endpoint.surface?.id;
1901
+ if (!owner) continue;
1902
+ const existing = bySurface.get(owner);
1903
+ if (existing) existing.push(endpoint);
1904
+ else bySurface.set(owner, [endpoint]);
1905
+ }
1906
+ if (bySurface.size === 0) {
1907
+ logger.log("No endpoints named a surface, so no OpenAPI was generated");
1908
+ return null;
1909
+ }
1864
1910
  const tsGenerator = new OpenApiTsGenerator();
1865
- const tsContent = await tsGenerator.generate(endpoints, {
1866
- title: openApiConfig.title,
1867
- version: openApiConfig.version,
1868
- description: openApiConfig.description
1869
- });
1870
- await writeFile(outputPath, tsContent);
1871
- logger.log(`📄 OpenAPI client generated: ${OPENAPI_OUTPUT_PATH}`);
1911
+ const written = [];
1912
+ for (const [surfaceId, surfaceEndpoints] of bySurface) {
1913
+ const relative$1 = openApiPathFor(surfaceId);
1914
+ const outputPath = join(process.cwd(), relative$1);
1915
+ await mkdir(dirname(outputPath), { recursive: true });
1916
+ const tsContent = await tsGenerator.generate(surfaceEndpoints, {
1917
+ title: openApiConfig.title ?? surfaceId,
1918
+ version: openApiConfig.version,
1919
+ description: openApiConfig.description
1920
+ });
1921
+ await writeFile(outputPath, tsContent);
1922
+ written.push(relative$1);
1923
+ logger.log(`📄 OpenAPI client generated: ${relative$1} (${surfaceEndpoints.length} endpoints)`);
1924
+ }
1872
1925
  return {
1873
- outputPath,
1874
- endpointCount: loadedEndpoints.length
1926
+ outputPath: join(process.cwd(), written[0]),
1927
+ outputPaths: written.map((r) => join(process.cwd(), r)),
1928
+ endpointCount: endpoints.length
1875
1929
  };
1876
1930
  }
1877
1931
  async function openapiCommand(options = {}) {
@@ -1881,12 +1935,12 @@ async function openapiCommand(options = {}) {
1881
1935
  if (loadedConfig.type === "single") {
1882
1936
  const config = loadedConfig.raw;
1883
1937
  if (!config.openapi) config.openapi = { enabled: true };
1884
- const result = await generateOpenApi(config);
1938
+ const result = await generateOpenApiFrom(config.constructs, { openapi: config.openapi });
1885
1939
  if (result) logger.log(`Found ${result.endpointCount} endpoints`);
1886
1940
  } else {
1887
1941
  const { workspace } = loadedConfig;
1888
1942
  const workspaceRoot = workspace.root;
1889
- const backendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "backend" && app.routes !== void 0 && app.openapi !== false && (typeof app.openapi !== "object" || app.openapi.enabled !== false));
1943
+ const backendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "backend" && app.openapi !== false && (typeof app.openapi !== "object" || app.openapi.enabled !== false));
1890
1944
  if (backendApps.length === 0) {
1891
1945
  logger.log("No backend apps with OpenAPI enabled found");
1892
1946
  return;
@@ -1900,8 +1954,12 @@ async function openapiCommand(options = {}) {
1900
1954
  return;
1901
1955
  }
1902
1956
  for (const [appName, app] of backendApps) {
1903
- if (app.type !== "backend" || !app.routes) continue;
1904
1957
  const appPath = join(workspaceRoot, app.path);
1958
+ if (resolve(appPath) === resolve(workspaceRoot)) {
1959
+ const result = await generateOpenApiForApp(workspaceRoot, appName, app, false);
1960
+ if (result) logger.log(`Found ${result.endpointCount} endpoints`);
1961
+ continue;
1962
+ }
1905
1963
  await runOpenApiInSubprocess(appPath, appName);
1906
1964
  }
1907
1965
  }
@@ -1914,18 +1972,14 @@ async function openapiCommand(options = {}) {
1914
1972
  * Runs in-process. The caller is responsible for ensuring `process.cwd()`
1915
1973
  * is the app's directory so tsx loads the app's tsconfig path aliases.
1916
1974
  */
1917
- async function generateOpenApiForApp(workspaceRoot, _appName, app) {
1918
- if (app.type !== "backend" || !app.routes) return null;
1975
+ async function generateOpenApiForApp(workspaceRoot, _appName, app, silent = true) {
1976
+ if (app.type !== "backend") return null;
1919
1977
  const appPath = join(workspaceRoot, app.path);
1920
- const routes = normalizeRoutes(app.routes);
1921
- const routesGlob = routes.map((r) => join(appPath, r));
1922
- const gkmConfig = {
1923
- routes: routesGlob,
1924
- envParser: app.envParser || "",
1925
- logger: app.logger || "",
1926
- openapi: app.openapi
1927
- };
1928
- return generateOpenApi(gkmConfig, { silent: true });
1978
+ const globs = [join(appPath, "**/*.ts")];
1979
+ return generateOpenApiFrom(globs, {
1980
+ openapi: app.openapi ?? { enabled: true },
1981
+ silent
1982
+ });
1929
1983
  }
1930
1984
  /**
1931
1985
  * Resolve the gkm bin path. Tests can override via `GKM_BIN_PATH` env var
@@ -1963,5 +2017,5 @@ async function runOpenApiInSubprocess(appCwd, appName) {
1963
2017
  }
1964
2018
 
1965
2019
  //#endregion
1966
- export { EndpointGenerator, OPENAPI_OUTPUT_PATH, generateOpenApi, normalizeRoutes, openapiCommand, resolveOpenApiConfig };
1967
- //# sourceMappingURL=openapi-kkQbsRVj.mjs.map
2020
+ export { EndpointGenerator, OPENAPI_OUTPUT_PATH, generateOpenApi, generateOpenApiFrom, normalizeRoutes, openApiPathFor, openapiCommand, resolveOpenApiConfig, runtimeFor };
2021
+ //# sourceMappingURL=openapi-Bw8DWFWz.mjs.map