@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.
- package/dist/{config-C21ocYNd.cjs → config-CNz5KaSR.cjs} +16 -45
- package/dist/config-CNz5KaSR.cjs.map +1 -0
- package/dist/{config-WRdsQYZK.mjs → config-CxP7P-qG.mjs} +17 -46
- package/dist/config-CxP7P-qG.mjs.map +1 -0
- package/dist/config.cjs +2 -2
- package/dist/config.d.cts +2 -2
- package/dist/config.d.mts +2 -2
- package/dist/config.mjs +2 -2
- package/dist/{credentials-BBIRJVp4.mjs → credentials-BK3Z93MX.mjs} +5 -5
- package/dist/credentials-BK3Z93MX.mjs.map +1 -0
- package/dist/{credentials-G1JwBoXH.cjs → credentials-DzsXZzkx.cjs} +5 -5
- package/dist/credentials-DzsXZzkx.cjs.map +1 -0
- package/dist/discover-CAlFgYec.mjs.map +1 -1
- package/dist/discover-DTmFeloL.cjs.map +1 -1
- package/dist/{index-BThUzEIp.d.mts → index-BGGdjueF.d.cts} +2 -2
- package/dist/index-BGGdjueF.d.cts.map +1 -0
- package/dist/{index-CHB4tSzX.d.cts → index-DEbaleny.d.mts} +2 -2
- package/dist/index-DEbaleny.d.mts.map +1 -0
- package/dist/index.cjs +307 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +307 -121
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-kkQbsRVj.mjs → openapi-Bw8DWFWz.mjs} +104 -50
- package/dist/openapi-Bw8DWFWz.mjs.map +1 -0
- package/dist/{openapi-CA0yJUTN.cjs → openapi-D-dx9Akj.cjs} +122 -50
- package/dist/openapi-D-dx9Akj.cjs.map +1 -0
- package/dist/openapi.cjs +5 -3
- package/dist/openapi.d.cts +46 -10
- package/dist/openapi.d.cts.map +1 -1
- package/dist/openapi.d.mts +46 -10
- package/dist/openapi.d.mts.map +1 -1
- package/dist/openapi.mjs +4 -4
- package/dist/reconcile/public.d.cts +1 -1
- package/dist/reconcile/public.d.mts +1 -1
- package/dist/setup-8IWPsRIN.cjs +11 -0
- package/dist/{setup-DXvRLTZN.mjs → setup-BWMhbW-p.mjs} +3 -3
- package/dist/{setup-DXvRLTZN.mjs.map → setup-BWMhbW-p.mjs.map} +1 -1
- package/dist/{setup-Cm2UnAdh.cjs → setup-D7L4BgBT.cjs} +3 -3
- package/dist/{setup-Cm2UnAdh.cjs.map → setup-D7L4BgBT.cjs.map} +1 -1
- package/dist/{setup-ZijS4qeC.mjs → setup-DGIPd2fh.mjs} +4 -4
- package/dist/{types-C-arnQ1r.d.mts → types-BLSNdrk0.d.cts} +5 -13
- package/dist/{types-C-arnQ1r.d.mts.map → types-BLSNdrk0.d.cts.map} +1 -1
- package/dist/{types-abJQuk7m.d.cts → types-C3POXhw3.d.mts} +2 -10
- package/dist/{types-abJQuk7m.d.cts.map → types-C3POXhw3.d.mts.map} +1 -1
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-DdJss8I6.cjs → workspace-CGaUAw5A.cjs} +3 -3
- package/dist/workspace-CnTqIlB3.mjs +8 -0
- package/dist/{workspace-BFMrAKqI.mjs → workspace-DIuZx-cc.mjs} +1 -17
- package/dist/{workspace-BFMrAKqI.mjs.map → workspace-DIuZx-cc.mjs.map} +1 -1
- package/dist/{workspace-BcucjwvA.cjs → workspace-LKiNyaDy.cjs} +1 -17
- package/dist/{workspace-BcucjwvA.cjs.map → workspace-LKiNyaDy.cjs.map} +1 -1
- package/package.json +11 -11
- package/dist/config-C21ocYNd.cjs.map +0 -1
- package/dist/config-WRdsQYZK.mjs.map +0 -1
- package/dist/credentials-BBIRJVp4.mjs.map +0 -1
- package/dist/credentials-G1JwBoXH.cjs.map +0 -1
- package/dist/index-BThUzEIp.d.mts.map +0 -1
- package/dist/index-CHB4tSzX.d.cts.map +0 -1
- package/dist/openapi-CA0yJUTN.cjs.map +0 -1
- package/dist/openapi-kkQbsRVj.mjs.map +0 -1
- package/dist/setup-B5_oHHUj.cjs +0 -11
- 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-
|
|
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
|
|
613
|
-
"// the entry imports the
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1222
|
-
|
|
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
|
-
*
|
|
1849
|
-
*
|
|
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
|
|
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(
|
|
1892
|
+
const openApiConfig = resolveOpenApiConfig({ openapi: options.openapi });
|
|
1854
1893
|
if (!openApiConfig.enabled) return null;
|
|
1855
|
-
|
|
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
|
|
1862
|
-
const
|
|
1863
|
-
|
|
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
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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"
|
|
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
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
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-
|
|
2020
|
+
export { EndpointGenerator, OPENAPI_OUTPUT_PATH, generateOpenApi, generateOpenApiFrom, normalizeRoutes, openApiPathFor, openapiCommand, resolveOpenApiConfig, runtimeFor };
|
|
2021
|
+
//# sourceMappingURL=openapi-Bw8DWFWz.mjs.map
|