@geekmidas/cli 10.0.0-alpha.0 → 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.
- package/README.md +12 -15
- package/bin/adjacent-tsconfig.mjs +59 -0
- package/bin/gkm.mjs +7 -1
- package/dist/{config-DbPE1KXF.cjs → config-BZ1F0WuA.cjs} +7 -80
- package/dist/config-BZ1F0WuA.cjs.map +1 -0
- package/dist/{config-D8o6I_7Z.mjs → config-DVVph9yz.mjs} +9 -76
- package/dist/config-DVVph9yz.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-K961ThG3.cjs → credentials-Xamqe_NJ.cjs} +20 -19
- package/dist/credentials-Xamqe_NJ.cjs.map +1 -0
- package/dist/{credentials-Dh0Q6HzY.mjs → credentials-dP8vRVRv.mjs} +20 -19
- package/dist/credentials-dP8vRVRv.mjs.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-C6e-ULLx.d.mts} +2 -2
- package/dist/index-C6e-ULLx.d.mts.map +1 -0
- package/dist/{index-CHB4tSzX.d.cts → index-CR5urhWX.d.cts} +2 -2
- package/dist/index-CR5urhWX.d.cts.map +1 -0
- package/dist/index.cjs +2154 -859
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +2156 -861
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-CljONd_M.cjs → openapi-SjsgARie.cjs} +127 -50
- package/dist/openapi-SjsgARie.cjs.map +1 -0
- package/dist/{openapi-CgpMWMmM.mjs → openapi-Xx0A7Wg0.mjs} +109 -50
- package/dist/openapi-Xx0A7Wg0.mjs.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-DSutDAlt.mjs → setup-BA8ciTPU.mjs} +4 -4
- package/dist/setup-BsZvZP6f.cjs +11 -0
- package/dist/{setup-DOsYueeQ.mjs → setup-C8KOx8_8.mjs} +5 -5
- package/dist/setup-C8KOx8_8.mjs.map +1 -0
- package/dist/{setup-CJiQM2pX.cjs → setup-hC6hI86f.cjs} +5 -5
- package/dist/setup-hC6hI86f.cjs.map +1 -0
- package/dist/{types-C-arnQ1r.d.mts → types-6pkp2RRj.d.mts} +46 -10
- package/dist/types-6pkp2RRj.d.mts.map +1 -0
- package/dist/{types-abJQuk7m.d.cts → types-Y3fKQSAK.d.cts} +46 -10
- package/dist/types-Y3fKQSAK.d.cts.map +1 -0
- 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-BzrG_2SI.mjs +8 -0
- package/dist/workspace-C6VJQVtG.cjs +6845 -0
- package/dist/workspace-C6VJQVtG.cjs.map +1 -0
- package/dist/{workspace-8acupeaI.cjs → workspace-XXtNhqJl.cjs} +3 -3
- package/dist/workspace-zoqRbihZ.mjs +6678 -0
- package/dist/workspace-zoqRbihZ.mjs.map +1 -0
- package/package.json +33 -32
- package/dist/config-D8o6I_7Z.mjs.map +0 -1
- package/dist/config-DbPE1KXF.cjs.map +0 -1
- package/dist/credentials-Dh0Q6HzY.mjs.map +0 -1
- package/dist/credentials-K961ThG3.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-CgpMWMmM.mjs.map +0 -1
- package/dist/openapi-CljONd_M.cjs.map +0 -1
- package/dist/setup-CJiQM2pX.cjs.map +0 -1
- package/dist/setup-DMtU3p41.cjs +0 -11
- package/dist/setup-DOsYueeQ.mjs.map +0 -1
- package/dist/types-C-arnQ1r.d.mts.map +0 -1
- package/dist/types-abJQuk7m.d.cts.map +0 -1
- package/dist/workspace-7FqprxuJ.mjs +0 -4323
- package/dist/workspace-7FqprxuJ.mjs.map +0 -1
- package/dist/workspace-XvqO9jIv.cjs +0 -4472
- package/dist/workspace-XvqO9jIv.cjs.map +0 -1
- package/dist/workspace-yP5AHnrn.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-DVVph9yz.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 ?? ""}
|
|
@@ -1505,6 +1516,11 @@ var OpenApiTsGenerator = class {
|
|
|
1505
1516
|
}
|
|
1506
1517
|
}
|
|
1507
1518
|
const { $defs,...schemaWithoutDefs } = jsonSchema;
|
|
1519
|
+
const ref = schemaWithoutDefs.$ref;
|
|
1520
|
+
if (ref && Object.keys(schemaWithoutDefs).every((k) => k === "$ref" || k === "$schema")) {
|
|
1521
|
+
const target = ref.split("/").pop();
|
|
1522
|
+
return target === name ? null : `export type ${name} = ${target};`;
|
|
1523
|
+
}
|
|
1508
1524
|
return this.jsonSchemaToInterface(schemaWithoutDefs, name);
|
|
1509
1525
|
} catch {
|
|
1510
1526
|
return null;
|
|
@@ -1822,6 +1838,16 @@ function normalizeRoutes(routes) {
|
|
|
1822
1838
|
*/
|
|
1823
1839
|
const OPENAPI_OUTPUT_PATH = "./.gkm/openapi.ts";
|
|
1824
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
|
+
/**
|
|
1825
1851
|
* Resolve OpenAPI config from GkmConfig
|
|
1826
1852
|
*/
|
|
1827
1853
|
function resolveOpenApiConfig(config) {
|
|
@@ -1840,33 +1866,66 @@ function resolveOpenApiConfig(config) {
|
|
|
1840
1866
|
};
|
|
1841
1867
|
}
|
|
1842
1868
|
/**
|
|
1843
|
-
*
|
|
1844
|
-
*
|
|
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 — the conventional
|
|
1873
|
+
* directories under it — not a `routes` field anybody wrote.
|
|
1874
|
+
*/
|
|
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
|
|
1845
1889
|
*/
|
|
1846
|
-
async function generateOpenApi(
|
|
1890
|
+
async function generateOpenApi(endpoints, options = {}) {
|
|
1847
1891
|
const logger = options.silent ? { log: () => {} } : console;
|
|
1848
|
-
const openApiConfig = resolveOpenApiConfig(
|
|
1892
|
+
const openApiConfig = resolveOpenApiConfig({ openapi: options.openapi });
|
|
1849
1893
|
if (!openApiConfig.enabled) return null;
|
|
1850
|
-
|
|
1851
|
-
const loadedEndpoints = await endpointGenerator.load(config.routes, void 0, options.bustCache);
|
|
1852
|
-
if (loadedEndpoints.length === 0) {
|
|
1894
|
+
if (endpoints.length === 0) {
|
|
1853
1895
|
logger.log("No valid endpoints found for OpenAPI generation");
|
|
1854
1896
|
return null;
|
|
1855
1897
|
}
|
|
1856
|
-
const
|
|
1857
|
-
const
|
|
1858
|
-
|
|
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
|
+
}
|
|
1859
1910
|
const tsGenerator = new OpenApiTsGenerator();
|
|
1860
|
-
const
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
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
|
+
}
|
|
1867
1925
|
return {
|
|
1868
|
-
outputPath,
|
|
1869
|
-
|
|
1926
|
+
outputPath: join(process.cwd(), written[0]),
|
|
1927
|
+
outputPaths: written.map((r) => join(process.cwd(), r)),
|
|
1928
|
+
endpointCount: endpoints.length
|
|
1870
1929
|
};
|
|
1871
1930
|
}
|
|
1872
1931
|
async function openapiCommand(options = {}) {
|
|
@@ -1876,12 +1935,12 @@ async function openapiCommand(options = {}) {
|
|
|
1876
1935
|
if (loadedConfig.type === "single") {
|
|
1877
1936
|
const config = loadedConfig.raw;
|
|
1878
1937
|
if (!config.openapi) config.openapi = { enabled: true };
|
|
1879
|
-
const result = await
|
|
1938
|
+
const result = await generateOpenApiFrom(config.constructs, { openapi: config.openapi });
|
|
1880
1939
|
if (result) logger.log(`Found ${result.endpointCount} endpoints`);
|
|
1881
1940
|
} else {
|
|
1882
1941
|
const { workspace } = loadedConfig;
|
|
1883
1942
|
const workspaceRoot = workspace.root;
|
|
1884
|
-
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));
|
|
1885
1944
|
if (backendApps.length === 0) {
|
|
1886
1945
|
logger.log("No backend apps with OpenAPI enabled found");
|
|
1887
1946
|
return;
|
|
@@ -1895,8 +1954,12 @@ async function openapiCommand(options = {}) {
|
|
|
1895
1954
|
return;
|
|
1896
1955
|
}
|
|
1897
1956
|
for (const [appName, app] of backendApps) {
|
|
1898
|
-
if (app.type !== "backend" || !app.routes) continue;
|
|
1899
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
|
+
}
|
|
1900
1963
|
await runOpenApiInSubprocess(appPath, appName);
|
|
1901
1964
|
}
|
|
1902
1965
|
}
|
|
@@ -1909,18 +1972,14 @@ async function openapiCommand(options = {}) {
|
|
|
1909
1972
|
* Runs in-process. The caller is responsible for ensuring `process.cwd()`
|
|
1910
1973
|
* is the app's directory so tsx loads the app's tsconfig path aliases.
|
|
1911
1974
|
*/
|
|
1912
|
-
async function generateOpenApiForApp(workspaceRoot, _appName, app) {
|
|
1913
|
-
if (app.type !== "backend"
|
|
1975
|
+
async function generateOpenApiForApp(workspaceRoot, _appName, app, silent = true) {
|
|
1976
|
+
if (app.type !== "backend") return null;
|
|
1914
1977
|
const appPath = join(workspaceRoot, app.path);
|
|
1915
|
-
const
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
logger: app.logger || "",
|
|
1921
|
-
openapi: app.openapi
|
|
1922
|
-
};
|
|
1923
|
-
return generateOpenApi(gkmConfig, { silent: true });
|
|
1978
|
+
const globs = [join(appPath, "**/*.ts")];
|
|
1979
|
+
return generateOpenApiFrom(globs, {
|
|
1980
|
+
openapi: app.openapi ?? { enabled: true },
|
|
1981
|
+
silent
|
|
1982
|
+
});
|
|
1924
1983
|
}
|
|
1925
1984
|
/**
|
|
1926
1985
|
* Resolve the gkm bin path. Tests can override via `GKM_BIN_PATH` env var
|
|
@@ -1958,5 +2017,5 @@ async function runOpenApiInSubprocess(appCwd, appName) {
|
|
|
1958
2017
|
}
|
|
1959
2018
|
|
|
1960
2019
|
//#endregion
|
|
1961
|
-
export { EndpointGenerator, OPENAPI_OUTPUT_PATH, generateOpenApi, normalizeRoutes, openapiCommand, resolveOpenApiConfig };
|
|
1962
|
-
//# sourceMappingURL=openapi-
|
|
2020
|
+
export { EndpointGenerator, OPENAPI_OUTPUT_PATH, generateOpenApi, generateOpenApiFrom, normalizeRoutes, openApiPathFor, openapiCommand, resolveOpenApiConfig, runtimeFor };
|
|
2021
|
+
//# sourceMappingURL=openapi-Xx0A7Wg0.mjs.map
|