@geekmidas/cli 10.0.0-alpha.1 → 10.0.0-alpha.10

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 (76) hide show
  1. package/README.md +12 -8
  2. package/bin/adjacent-tsconfig.mjs +59 -0
  3. package/bin/gkm.mjs +7 -1
  4. package/dist/{config-DbPE1KXF.cjs → config-BZ1F0WuA.cjs} +7 -80
  5. package/dist/config-BZ1F0WuA.cjs.map +1 -0
  6. package/dist/{config-D8o6I_7Z.mjs → config-DVVph9yz.mjs} +9 -76
  7. package/dist/config-DVVph9yz.mjs.map +1 -0
  8. package/dist/config.cjs +2 -2
  9. package/dist/config.d.cts +2 -2
  10. package/dist/config.d.mts +2 -2
  11. package/dist/config.mjs +2 -2
  12. package/dist/{credentials-K961ThG3.cjs → credentials-Xamqe_NJ.cjs} +20 -19
  13. package/dist/credentials-Xamqe_NJ.cjs.map +1 -0
  14. package/dist/{credentials-Dh0Q6HzY.mjs → credentials-dP8vRVRv.mjs} +20 -19
  15. package/dist/credentials-dP8vRVRv.mjs.map +1 -0
  16. package/dist/discover-CAlFgYec.mjs.map +1 -1
  17. package/dist/discover-DTmFeloL.cjs.map +1 -1
  18. package/dist/{index-BThUzEIp.d.mts → index-C6e-ULLx.d.mts} +2 -2
  19. package/dist/index-C6e-ULLx.d.mts.map +1 -0
  20. package/dist/{index-CHB4tSzX.d.cts → index-CR5urhWX.d.cts} +2 -2
  21. package/dist/index-CR5urhWX.d.cts.map +1 -0
  22. package/dist/index.cjs +1917 -597
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.mjs +1919 -599
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/{openapi-CljONd_M.cjs → openapi-SjsgARie.cjs} +127 -50
  27. package/dist/openapi-SjsgARie.cjs.map +1 -0
  28. package/dist/{openapi-CgpMWMmM.mjs → openapi-Xx0A7Wg0.mjs} +109 -50
  29. package/dist/openapi-Xx0A7Wg0.mjs.map +1 -0
  30. package/dist/openapi.cjs +5 -3
  31. package/dist/openapi.d.cts +46 -10
  32. package/dist/openapi.d.cts.map +1 -1
  33. package/dist/openapi.d.mts +46 -10
  34. package/dist/openapi.d.mts.map +1 -1
  35. package/dist/openapi.mjs +4 -4
  36. package/dist/reconcile/public.d.cts +1 -1
  37. package/dist/reconcile/public.d.mts +1 -1
  38. package/dist/{setup-DSutDAlt.mjs → setup-BA8ciTPU.mjs} +4 -4
  39. package/dist/setup-BsZvZP6f.cjs +11 -0
  40. package/dist/{setup-DOsYueeQ.mjs → setup-C8KOx8_8.mjs} +5 -5
  41. package/dist/setup-C8KOx8_8.mjs.map +1 -0
  42. package/dist/{setup-CJiQM2pX.cjs → setup-hC6hI86f.cjs} +5 -5
  43. package/dist/setup-hC6hI86f.cjs.map +1 -0
  44. package/dist/{types-C-arnQ1r.d.mts → types-6pkp2RRj.d.mts} +46 -10
  45. package/dist/types-6pkp2RRj.d.mts.map +1 -0
  46. package/dist/{types-abJQuk7m.d.cts → types-Y3fKQSAK.d.cts} +46 -10
  47. package/dist/types-Y3fKQSAK.d.cts.map +1 -0
  48. package/dist/workspace/index.cjs +1 -1
  49. package/dist/workspace/index.d.cts +2 -2
  50. package/dist/workspace/index.d.mts +2 -2
  51. package/dist/workspace/index.mjs +1 -1
  52. package/dist/workspace-BzrG_2SI.mjs +8 -0
  53. package/dist/workspace-C6VJQVtG.cjs +6845 -0
  54. package/dist/workspace-C6VJQVtG.cjs.map +1 -0
  55. package/dist/{workspace-8acupeaI.cjs → workspace-XXtNhqJl.cjs} +3 -3
  56. package/dist/workspace-zoqRbihZ.mjs +6678 -0
  57. package/dist/workspace-zoqRbihZ.mjs.map +1 -0
  58. package/package.json +33 -32
  59. package/dist/config-D8o6I_7Z.mjs.map +0 -1
  60. package/dist/config-DbPE1KXF.cjs.map +0 -1
  61. package/dist/credentials-Dh0Q6HzY.mjs.map +0 -1
  62. package/dist/credentials-K961ThG3.cjs.map +0 -1
  63. package/dist/index-BThUzEIp.d.mts.map +0 -1
  64. package/dist/index-CHB4tSzX.d.cts.map +0 -1
  65. package/dist/openapi-CgpMWMmM.mjs.map +0 -1
  66. package/dist/openapi-CljONd_M.cjs.map +0 -1
  67. package/dist/setup-CJiQM2pX.cjs.map +0 -1
  68. package/dist/setup-DMtU3p41.cjs +0 -11
  69. package/dist/setup-DOsYueeQ.mjs.map +0 -1
  70. package/dist/types-C-arnQ1r.d.mts.map +0 -1
  71. package/dist/types-abJQuk7m.d.cts.map +0 -1
  72. package/dist/workspace-7FqprxuJ.mjs +0 -4323
  73. package/dist/workspace-7FqprxuJ.mjs.map +0 -1
  74. package/dist/workspace-XvqO9jIv.cjs +0 -4472
  75. package/dist/workspace-XvqO9jIv.cjs.map +0 -1
  76. package/dist/workspace-yP5AHnrn.mjs +0 -8
@@ -1,7 +1,8 @@
1
1
  const require_chunk = require('./chunk-CUT6urMc.cjs');
2
2
  const require_discover = require('./discover-DTmFeloL.cjs');
3
- const require_config = require('./config-DbPE1KXF.cjs');
3
+ const require_config = require('./config-BZ1F0WuA.cjs');
4
4
  const node_path = require_chunk.__toESM(require("node:path"));
5
+ const __geekmidas_manifest = require_chunk.__toESM(require("@geekmidas/manifest"));
5
6
  const node_fs_promises = require_chunk.__toESM(require("node:fs/promises"));
6
7
  const node_child_process = require_chunk.__toESM(require("node:child_process"));
7
8
  const __geekmidas_constructs_endpoints = require_chunk.__toESM(require("@geekmidas/constructs/endpoints"));
@@ -603,24 +604,21 @@ export async function setupEndpoints(
603
604
  *
604
605
  * Falls back to the configured paths for an app that declares no surface.
605
606
  */
606
- function runtimeFor(context, fromDir) {
607
- const module$1 = context.surface?.module;
607
+ function runtimeFor(context, fromDir, owner) {
608
+ const module$1 = owner ? context.owners?.[owner] ?? context.surface?.module : context.surface?.module;
608
609
  if (module$1) {
609
610
  const specifier = importSpecifier(fromDir, module$1.specifier);
610
611
  return {
611
612
  imports: `import { ${module$1.exportName} as __surface } from '${specifier}';`,
612
613
  bindings: [
613
- "// What this surface was declared with. Objects, not module paths:",
614
- "// the entry imports the surface, so there is nothing to print.",
614
+ "// What this construct was declared with. Objects, not module paths:",
615
+ "// the entry imports the owner, so there is nothing to print.",
615
616
  "const envParser = __surface.envParser;",
616
617
  "const logger = __surface.logger;"
617
618
  ].join("\n")
618
619
  };
619
620
  }
620
- return {
621
- imports: [`import ${context.envParserImportPattern} from '${importSpecifier(fromDir, context.envParserPath)}';`, `import ${context.loggerImportPattern} from '${importSpecifier(fromDir, context.loggerPath)}';`].join("\n"),
622
- bindings: ""
623
- };
621
+ 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\`.`);
624
622
  }
625
623
  /** A relative specifier ESM will accept: always prefixed, never bare. */
626
624
  function importSpecifier(fromDir, target) {
@@ -993,6 +991,7 @@ import type { Hono as HonoType } from 'hono';
993
991
  import { setupEndpoints } from './endpoints.js';
994
992
  import { setupSubscribers } from './subscribers.js';
995
993
  import { setupQueues } from './queues.js';
994
+ import { setupCrons } from './crons.js';
996
995
  ${runtime.imports}
997
996
  ${telescopeImports}
998
997
  ${studioImports}
@@ -1080,6 +1079,12 @@ ${afterSetupCall}
1080
1079
  logger.error({ error }, 'Failed to start queue workers');
1081
1080
  });
1082
1081
 
1082
+ // Schedule this app's crons. Caught like the others: a scheduler that
1083
+ // cannot start is not a reason for the HTTP server not to.
1084
+ await setupCrons(envParser, logger).catch((error) => {
1085
+ logger.error({ error }, 'Failed to schedule crons');
1086
+ });
1087
+
1083
1088
  logger.info({ port }, 'Starting server');
1084
1089
 
1085
1090
  // Start HTTP server using provided serve function
@@ -1150,8 +1155,7 @@ export const handler = ${exportName};
1150
1155
  async generateProductionAppFile(outputDir, context) {
1151
1156
  const appFileName = "app.ts";
1152
1157
  const appPath = (0, node_path.join)(outputDir, appFileName);
1153
- const relativeLoggerPath = (0, node_path.relative)((0, node_path.dirname)(appPath), context.loggerPath);
1154
- const relativeEnvParserPath = (0, node_path.relative)((0, node_path.dirname)(appPath), context.envParserPath);
1158
+ const runtime = runtimeFor(context, (0, node_path.dirname)(appPath));
1155
1159
  const production = context.production;
1156
1160
  const healthCheckPath = production.healthCheck;
1157
1161
  const enableGracefulShutdown = production.gracefulShutdown;
@@ -1187,9 +1191,16 @@ export const handler = ${exportName};
1187
1191
  await setupQueues(envParser, logger).catch((error) => {
1188
1192
  logger.error({ error }, 'Failed to start queue workers');
1189
1193
  });
1194
+
1195
+ // Schedule this app's crons. Caught like the others: a scheduler that
1196
+ // cannot start is not a reason for the HTTP server not to.
1197
+ await setupCrons(envParser, logger).catch((error) => {
1198
+ logger.error({ error }, 'Failed to schedule crons');
1199
+ });
1190
1200
  ` : "";
1191
1201
  const subscriberImport = includeSubscribers ? `import { setupSubscribers } from './subscribers.js';
1192
- import { setupQueues } from './queues.js';` : "";
1202
+ import { setupQueues } from './queues.js';
1203
+ import { setupCrons } from './crons.js';` : "";
1193
1204
  const gracefulShutdownCode = enableGracefulShutdown ? `
1194
1205
  // Graceful shutdown handling
1195
1206
  let isShuttingDown = false;
@@ -1219,8 +1230,8 @@ import { Hono } from 'hono';
1219
1230
  import type { Hono as HonoType } from 'hono';
1220
1231
  import { setupEndpoints } from '${endpointsImportPath}';
1221
1232
  ${subscriberImport}
1222
- import ${context.envParserImportPattern} from '${relativeEnvParserPath}';
1223
- import ${context.loggerImportPattern} from '${relativeLoggerPath}';
1233
+ ${runtime.imports}
1234
+ ${runtime.bindings}
1224
1235
  ${hooksImports}
1225
1236
  ${cors.imports}
1226
1237
  ${context.storageDrivers?.imports ?? ""}
@@ -1506,6 +1517,11 @@ var OpenApiTsGenerator = class {
1506
1517
  }
1507
1518
  }
1508
1519
  const { $defs,...schemaWithoutDefs } = jsonSchema;
1520
+ const ref = schemaWithoutDefs.$ref;
1521
+ if (ref && Object.keys(schemaWithoutDefs).every((k) => k === "$ref" || k === "$schema")) {
1522
+ const target = ref.split("/").pop();
1523
+ return target === name ? null : `export type ${name} = ${target};`;
1524
+ }
1509
1525
  return this.jsonSchemaToInterface(schemaWithoutDefs, name);
1510
1526
  } catch {
1511
1527
  return null;
@@ -1823,6 +1839,16 @@ function normalizeRoutes(routes) {
1823
1839
  */
1824
1840
  const OPENAPI_OUTPUT_PATH = "./.gkm/openapi.ts";
1825
1841
  /**
1842
+ * Where a surface's generated client lands.
1843
+ *
1844
+ * Named from the surface, so `new RestApi('Webhooks')` writes
1845
+ * `.gkm/openapi/webhooks.ts` — the same kebab form that gives it its container
1846
+ * name and its `WEBHOOKS_URL`.
1847
+ */
1848
+ function openApiPathFor(surfaceId) {
1849
+ return `./.gkm/openapi/${(0, __geekmidas_manifest.kebabCase)(surfaceId)}.ts`;
1850
+ }
1851
+ /**
1826
1852
  * Resolve OpenAPI config from GkmConfig
1827
1853
  */
1828
1854
  function resolveOpenApiConfig(config) {
@@ -1841,33 +1867,66 @@ function resolveOpenApiConfig(config) {
1841
1867
  };
1842
1868
  }
1843
1869
  /**
1844
- * Generate OpenAPI spec from endpoints
1845
- * @returns Object with output path and endpoint count, or null if disabled
1870
+ * Load endpoints from a glob, then generate.
1871
+ *
1872
+ * For the callers that have no endpoints in hand — `gkm openapi`, and `gkm dev`
1873
+ * regenerating on reload. The glob is the app's derived one — the conventional
1874
+ * directories under it — not a `routes` field anybody wrote.
1875
+ */
1876
+ async function generateOpenApiFrom(routes, options = {}) {
1877
+ const loaded = await new EndpointGenerator().load(routes, void 0, options.bustCache);
1878
+ return generateOpenApi(loaded.map(({ construct }) => construct), options);
1879
+ }
1880
+ /**
1881
+ * Generate an OpenAPI spec for each surface these endpoints name.
1882
+ *
1883
+ * Takes the endpoints, not a glob to find them with. The build has already
1884
+ * loaded every one of them to generate handlers, so globbing again was a second
1885
+ * discovery pass over the same files — and the reason this function needed a
1886
+ * `routes` to be told where to look, which is how a retired config field stayed
1887
+ * load-bearing.
1888
+ *
1889
+ * @returns What was written, or null if disabled or there were none
1846
1890
  */
1847
- async function generateOpenApi(config, options = {}) {
1891
+ async function generateOpenApi(endpoints, options = {}) {
1848
1892
  const logger = options.silent ? { log: () => {} } : console;
1849
- const openApiConfig = resolveOpenApiConfig(config);
1893
+ const openApiConfig = resolveOpenApiConfig({ openapi: options.openapi });
1850
1894
  if (!openApiConfig.enabled) return null;
1851
- const endpointGenerator = new EndpointGenerator();
1852
- const loadedEndpoints = await endpointGenerator.load(config.routes, void 0, options.bustCache);
1853
- if (loadedEndpoints.length === 0) {
1895
+ if (endpoints.length === 0) {
1854
1896
  logger.log("No valid endpoints found for OpenAPI generation");
1855
1897
  return null;
1856
1898
  }
1857
- const endpoints = loadedEndpoints.map(({ construct }) => construct);
1858
- const outputPath = (0, node_path.join)(process.cwd(), OPENAPI_OUTPUT_PATH);
1859
- await (0, node_fs_promises.mkdir)((0, node_path.dirname)(outputPath), { recursive: true });
1899
+ const bySurface = /* @__PURE__ */ new Map();
1900
+ for (const endpoint of endpoints) {
1901
+ const owner = endpoint.owner ?? endpoint.surface?.id;
1902
+ if (!owner) continue;
1903
+ const existing = bySurface.get(owner);
1904
+ if (existing) existing.push(endpoint);
1905
+ else bySurface.set(owner, [endpoint]);
1906
+ }
1907
+ if (bySurface.size === 0) {
1908
+ logger.log("No endpoints named a surface, so no OpenAPI was generated");
1909
+ return null;
1910
+ }
1860
1911
  const tsGenerator = new OpenApiTsGenerator();
1861
- const tsContent = await tsGenerator.generate(endpoints, {
1862
- title: openApiConfig.title,
1863
- version: openApiConfig.version,
1864
- description: openApiConfig.description
1865
- });
1866
- await (0, node_fs_promises.writeFile)(outputPath, tsContent);
1867
- logger.log(`📄 OpenAPI client generated: ${OPENAPI_OUTPUT_PATH}`);
1912
+ const written = [];
1913
+ for (const [surfaceId, surfaceEndpoints] of bySurface) {
1914
+ const relative$1 = openApiPathFor(surfaceId);
1915
+ const outputPath = (0, node_path.join)(process.cwd(), relative$1);
1916
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(outputPath), { recursive: true });
1917
+ const tsContent = await tsGenerator.generate(surfaceEndpoints, {
1918
+ title: openApiConfig.title ?? surfaceId,
1919
+ version: openApiConfig.version,
1920
+ description: openApiConfig.description
1921
+ });
1922
+ await (0, node_fs_promises.writeFile)(outputPath, tsContent);
1923
+ written.push(relative$1);
1924
+ logger.log(`📄 OpenAPI client generated: ${relative$1} (${surfaceEndpoints.length} endpoints)`);
1925
+ }
1868
1926
  return {
1869
- outputPath,
1870
- endpointCount: loadedEndpoints.length
1927
+ outputPath: (0, node_path.join)(process.cwd(), written[0]),
1928
+ outputPaths: written.map((r) => (0, node_path.join)(process.cwd(), r)),
1929
+ endpointCount: endpoints.length
1871
1930
  };
1872
1931
  }
1873
1932
  async function openapiCommand(options = {}) {
@@ -1877,12 +1936,12 @@ async function openapiCommand(options = {}) {
1877
1936
  if (loadedConfig.type === "single") {
1878
1937
  const config = loadedConfig.raw;
1879
1938
  if (!config.openapi) config.openapi = { enabled: true };
1880
- const result = await generateOpenApi(config);
1939
+ const result = await generateOpenApiFrom(config.constructs, { openapi: config.openapi });
1881
1940
  if (result) logger.log(`Found ${result.endpointCount} endpoints`);
1882
1941
  } else {
1883
1942
  const { workspace } = loadedConfig;
1884
1943
  const workspaceRoot = workspace.root;
1885
- 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));
1944
+ const backendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "backend" && app.openapi !== false && (typeof app.openapi !== "object" || app.openapi.enabled !== false));
1886
1945
  if (backendApps.length === 0) {
1887
1946
  logger.log("No backend apps with OpenAPI enabled found");
1888
1947
  return;
@@ -1896,8 +1955,12 @@ async function openapiCommand(options = {}) {
1896
1955
  return;
1897
1956
  }
1898
1957
  for (const [appName, app] of backendApps) {
1899
- if (app.type !== "backend" || !app.routes) continue;
1900
1958
  const appPath = (0, node_path.join)(workspaceRoot, app.path);
1959
+ if ((0, node_path.resolve)(appPath) === (0, node_path.resolve)(workspaceRoot)) {
1960
+ const result = await generateOpenApiForApp(workspaceRoot, appName, app, false);
1961
+ if (result) logger.log(`Found ${result.endpointCount} endpoints`);
1962
+ continue;
1963
+ }
1901
1964
  await runOpenApiInSubprocess(appPath, appName);
1902
1965
  }
1903
1966
  }
@@ -1910,18 +1973,14 @@ async function openapiCommand(options = {}) {
1910
1973
  * Runs in-process. The caller is responsible for ensuring `process.cwd()`
1911
1974
  * is the app's directory so tsx loads the app's tsconfig path aliases.
1912
1975
  */
1913
- async function generateOpenApiForApp(workspaceRoot, _appName, app) {
1914
- if (app.type !== "backend" || !app.routes) return null;
1976
+ async function generateOpenApiForApp(workspaceRoot, _appName, app, silent = true) {
1977
+ if (app.type !== "backend") return null;
1915
1978
  const appPath = (0, node_path.join)(workspaceRoot, app.path);
1916
- const routes = normalizeRoutes(app.routes);
1917
- const routesGlob = routes.map((r) => (0, node_path.join)(appPath, r));
1918
- const gkmConfig = {
1919
- routes: routesGlob,
1920
- envParser: app.envParser || "",
1921
- logger: app.logger || "",
1922
- openapi: app.openapi
1923
- };
1924
- return generateOpenApi(gkmConfig, { silent: true });
1979
+ const globs = [(0, node_path.join)(appPath, "**/*.ts")];
1980
+ return generateOpenApiFrom(globs, {
1981
+ openapi: app.openapi ?? { enabled: true },
1982
+ silent
1983
+ });
1925
1984
  }
1926
1985
  /**
1927
1986
  * Resolve the gkm bin path. Tests can override via `GKM_BIN_PATH` env var
@@ -1939,7 +1998,7 @@ function resolveGkmBinPath() {
1939
1998
  */
1940
1999
  async function runOpenApiInSubprocess(appCwd, appName) {
1941
2000
  const binPath = resolveGkmBinPath();
1942
- await new Promise((resolve, reject) => {
2001
+ await new Promise((resolve$1, reject) => {
1943
2002
  const child = (0, node_child_process.spawn)(process.execPath, [
1944
2003
  binPath,
1945
2004
  "openapi",
@@ -1952,7 +2011,7 @@ async function runOpenApiInSubprocess(appCwd, appName) {
1952
2011
  });
1953
2012
  child.on("error", reject);
1954
2013
  child.on("close", (code) => {
1955
- if (code === 0) resolve();
2014
+ if (code === 0) resolve$1();
1956
2015
  else reject(new Error(`OpenAPI generation for app "${appName}" exited with code ${code}`));
1957
2016
  });
1958
2017
  });
@@ -1977,12 +2036,24 @@ Object.defineProperty(exports, 'generateOpenApi', {
1977
2036
  return generateOpenApi;
1978
2037
  }
1979
2038
  });
2039
+ Object.defineProperty(exports, 'generateOpenApiFrom', {
2040
+ enumerable: true,
2041
+ get: function () {
2042
+ return generateOpenApiFrom;
2043
+ }
2044
+ });
1980
2045
  Object.defineProperty(exports, 'normalizeRoutes', {
1981
2046
  enumerable: true,
1982
2047
  get: function () {
1983
2048
  return normalizeRoutes;
1984
2049
  }
1985
2050
  });
2051
+ Object.defineProperty(exports, 'openApiPathFor', {
2052
+ enumerable: true,
2053
+ get: function () {
2054
+ return openApiPathFor;
2055
+ }
2056
+ });
1986
2057
  Object.defineProperty(exports, 'openapiCommand', {
1987
2058
  enumerable: true,
1988
2059
  get: function () {
@@ -1995,4 +2066,10 @@ Object.defineProperty(exports, 'resolveOpenApiConfig', {
1995
2066
  return resolveOpenApiConfig;
1996
2067
  }
1997
2068
  });
1998
- //# sourceMappingURL=openapi-CljONd_M.cjs.map
2069
+ Object.defineProperty(exports, 'runtimeFor', {
2070
+ enumerable: true,
2071
+ get: function () {
2072
+ return runtimeFor;
2073
+ }
2074
+ });
2075
+ //# sourceMappingURL=openapi-SjsgARie.cjs.map