@geekmidas/cli 10.0.0-alpha.45 → 10.0.0-alpha.47
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 +144 -183
- package/dist/{OpenApiTsGenerator-B4Kj_qNU.cjs → OpenApiTsGenerator-BN0PIZxd.cjs} +17 -71
- package/dist/OpenApiTsGenerator-BN0PIZxd.cjs.map +1 -0
- package/dist/{OpenApiTsGenerator-CEh3EKN3.mjs → OpenApiTsGenerator-BzIGy8KE.mjs} +17 -71
- package/dist/OpenApiTsGenerator-BzIGy8KE.mjs.map +1 -0
- package/dist/backends-B9V2XD2M.cjs +82 -0
- package/dist/backends-B9V2XD2M.cjs.map +1 -0
- package/dist/backends-CSuLl0YP.mjs +59 -0
- package/dist/backends-CSuLl0YP.mjs.map +1 -0
- package/dist/{config-CJ-PAb7M.cjs → config-CsYSyUya.cjs} +3 -3
- package/dist/{config-CJ-PAb7M.cjs.map → config-CsYSyUya.cjs.map} +1 -1
- package/dist/{config-5watB1D0.mjs → config-D8J5rrTb.mjs} +3 -3
- package/dist/{config-5watB1D0.mjs.map → config-D8J5rrTb.mjs.map} +1 -1
- 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/{discover-BLhf7V_J.mjs → discover-Cf2jAcgF.mjs} +4 -56
- package/dist/discover-Cf2jAcgF.mjs.map +1 -0
- package/dist/{discover-B0f_kuNN.cjs → discover-DXNviAh-.cjs} +3 -79
- package/dist/discover-DXNviAh-.cjs.map +1 -0
- package/dist/{emit-Bzq7JsMP.mjs → emit-C1e8qD08.mjs} +12 -43
- package/dist/emit-C1e8qD08.mjs.map +1 -0
- package/dist/{emit-BdoPFjZf.cjs → emit-Dmc1nvdS.cjs} +11 -54
- package/dist/emit-Dmc1nvdS.cjs.map +1 -0
- package/dist/encryption-C7OGMve_.cjs +91 -0
- package/dist/encryption-C7OGMve_.cjs.map +1 -0
- package/dist/encryption-DyQnPsZN.mjs +90 -0
- package/dist/encryption-DyQnPsZN.mjs.map +1 -0
- package/dist/{github-D5MthUlR.mjs → github-BfpadQWQ.mjs} +3 -3
- package/dist/{github-D5MthUlR.mjs.map → github-BfpadQWQ.mjs.map} +1 -1
- package/dist/{github-BAr8WGkR.cjs → github-BrI1tv6u.cjs} +3 -3
- package/dist/{github-BAr8WGkR.cjs.map → github-BrI1tv6u.cjs.map} +1 -1
- package/dist/{index-B3PeGOQp.d.cts → index-BQhJXpaQ.d.mts} +2 -2
- package/dist/{index-B3PeGOQp.d.cts.map → index-BQhJXpaQ.d.mts.map} +1 -1
- package/dist/{index-BOyaGcCm.d.mts → index-DHJePE0y.d.cts} +2 -2
- package/dist/{index-BOyaGcCm.d.mts.map → index-DHJePE0y.d.cts.map} +1 -1
- package/dist/index.cjs +444 -670
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +442 -668
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-PJi4b1ga.cjs → migrate-BgEY9pdP.cjs} +5 -5
- package/dist/{migrate-PJi4b1ga.cjs.map → migrate-BgEY9pdP.cjs.map} +1 -1
- package/dist/{migrate-BRklCm7b.mjs → migrate-DhxTIA0z.mjs} +5 -5
- package/dist/{migrate-BRklCm7b.mjs.map → migrate-DhxTIA0z.mjs.map} +1 -1
- package/dist/openapi.cjs +2 -2
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +2 -2
- package/dist/{ready-ZqOuNvKD.mjs → ready-WfT-eWlm.mjs} +50 -8
- package/dist/ready-WfT-eWlm.mjs.map +1 -0
- package/dist/{ready-3RfHxdA0.cjs → ready-lr2Fm-ep.cjs} +68 -8
- package/dist/ready-lr2Fm-ep.cjs.map +1 -0
- package/dist/reconcile/public.cjs +2 -4
- package/dist/reconcile/public.d.cts +6 -10
- package/dist/reconcile/public.d.cts.map +1 -1
- package/dist/reconcile/public.d.mts +6 -10
- package/dist/reconcile/public.d.mts.map +1 -1
- package/dist/reconcile/public.mjs +3 -3
- package/dist/{types-0R124hrY.d.mts → types-BbrNu83u.d.mts} +14 -109
- package/dist/{types-0R124hrY.d.mts.map → types-BbrNu83u.d.mts.map} +1 -1
- package/dist/{types-DfdYe31u.d.cts → types-DNqEsctD.d.cts} +14 -109
- package/dist/{types-DfdYe31u.d.cts.map → types-DNqEsctD.d.cts.map} +1 -1
- package/dist/vitest.cjs +1 -1
- package/dist/vitest.mjs +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-RYSYtHP7.cjs → workspace-DEibcoBe.cjs} +11 -25
- package/dist/{workspace-RYSYtHP7.cjs.map → workspace-DEibcoBe.cjs.map} +1 -1
- package/dist/{workspace-DOmIhiau.mjs → workspace-DNzWgM1J.mjs} +11 -25
- package/dist/{workspace-DOmIhiau.mjs.map → workspace-DNzWgM1J.mjs.map} +1 -1
- package/dist/{workspace-BKytVCzb.mjs → workspace-cb6U29EL.mjs} +59 -81
- package/dist/workspace-cb6U29EL.mjs.map +1 -0
- package/dist/{workspace-dV6f-3vv.cjs → workspace-vafrzZTC.cjs} +65 -105
- package/dist/workspace-vafrzZTC.cjs.map +1 -0
- package/package.json +14 -14
- package/dist/OpenApiTsGenerator-B4Kj_qNU.cjs.map +0 -1
- package/dist/OpenApiTsGenerator-CEh3EKN3.mjs.map +0 -1
- package/dist/discover-B0f_kuNN.cjs.map +0 -1
- package/dist/discover-BLhf7V_J.mjs.map +0 -1
- package/dist/emit-BdoPFjZf.cjs.map +0 -1
- package/dist/emit-Bzq7JsMP.mjs.map +0 -1
- package/dist/ready-3RfHxdA0.cjs.map +0 -1
- package/dist/ready-ZqOuNvKD.mjs.map +0 -1
- package/dist/workspace-BKytVCzb.mjs.map +0 -1
- package/dist/workspace-dV6f-3vv.cjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
import { n as __require, t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
3
|
-
import {
|
|
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-
|
|
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-
|
|
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
|
|
9
|
-
import { n as EndpointGenerator, r as runtimeFor } from "./OpenApiTsGenerator-
|
|
8
|
+
import { C as loadEnvFiles, S as isPortAvailable, T as prepareEntryCredentials, _ as fakeKey, a as reconcileWorkspace, b as createEntryWrapper, c as LOCAL_CA_PATH, d as postgresDatabaseNames, f as quoteIdentifier, g as describeServices, 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 primaryPortKey, w as loadSecretsForApp, x as findAvailablePort, y as createCredentialsPreload } from "./workspace-cb6U29EL.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-
|
|
12
|
-
import { i as
|
|
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-WfT-eWlm.mjs";
|
|
12
|
+
import { i as storageBackendFor, n as eventsBackendFor, r as providerOf, t as cacheBackendFor } from "./backends-CSuLl0YP.mjs";
|
|
13
|
+
import { n as withCompute, r as withRoutes, t as manifestModule } from "./emit-C1e8qD08.mjs";
|
|
13
14
|
import { createRequire } from "node:module";
|
|
14
15
|
import { Command } from "commander";
|
|
15
16
|
import { stdin, stdout } from "node:process";
|
|
16
17
|
import * as readline from "node:readline/promises";
|
|
17
18
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
18
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
19
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
19
20
|
import { homedir } from "node:os";
|
|
20
21
|
import { basename, dirname, isAbsolute, join, parse, relative, resolve } from "node:path";
|
|
21
22
|
import { execFile, execSync, spawn } from "node:child_process";
|
|
@@ -24,6 +25,7 @@ import fg from "fast-glob";
|
|
|
24
25
|
import chokidar from "chokidar";
|
|
25
26
|
import { createCipheriv, createHash, randomBytes } from "node:crypto";
|
|
26
27
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
28
|
+
import { generateKeyring } from "@geekmidas/constructs/encryption";
|
|
27
29
|
import { ownerRole, readerRole, roleStatements } from "@geekmidas/db/pg/roles";
|
|
28
30
|
import { Client } from "pg";
|
|
29
31
|
import { parse as parse$1, parseDocument } from "yaml";
|
|
@@ -40,7 +42,7 @@ import { Queue } from "@geekmidas/constructs/queue";
|
|
|
40
42
|
import { Topic } from "@geekmidas/constructs/topic";
|
|
41
43
|
import { lookup } from "node:dns/promises";
|
|
42
44
|
//#region package.json
|
|
43
|
-
var version = "10.0.0-alpha.
|
|
45
|
+
var version = "10.0.0-alpha.47";
|
|
44
46
|
//#endregion
|
|
45
47
|
//#region src/auth/credentials.ts
|
|
46
48
|
/**
|
|
@@ -174,7 +176,7 @@ async function getHostingerToken(options) {
|
|
|
174
176
|
}
|
|
175
177
|
//#endregion
|
|
176
178
|
//#region src/auth/index.ts
|
|
177
|
-
const logger$
|
|
179
|
+
const logger$12 = console;
|
|
178
180
|
/**
|
|
179
181
|
* Validate Dokploy token by making a test API call
|
|
180
182
|
*/
|
|
@@ -241,52 +243,52 @@ async function prompt$1(message, hidden = false) {
|
|
|
241
243
|
async function loginCommand(options) {
|
|
242
244
|
const { provider, token: providedToken, endpoint: providedEndpoint } = options;
|
|
243
245
|
if (provider === "dokploy") {
|
|
244
|
-
logger$
|
|
246
|
+
logger$12.log("\n🔐 Logging in to Dokploy...\n");
|
|
245
247
|
let endpoint = providedEndpoint;
|
|
246
248
|
if (!endpoint) endpoint = await prompt$1("Dokploy URL (e.g., https://dokploy.example.com): ");
|
|
247
249
|
endpoint = endpoint.replace(/\/$/, "");
|
|
248
250
|
try {
|
|
249
251
|
new URL(endpoint);
|
|
250
252
|
} catch {
|
|
251
|
-
logger$
|
|
253
|
+
logger$12.error("Invalid URL format");
|
|
252
254
|
process.exit(1);
|
|
253
255
|
}
|
|
254
256
|
let token = providedToken;
|
|
255
257
|
if (!token) {
|
|
256
|
-
logger$
|
|
258
|
+
logger$12.log(`\nGenerate a token at: ${endpoint}/settings/profile\n`);
|
|
257
259
|
token = await prompt$1("API Token: ", true);
|
|
258
260
|
}
|
|
259
261
|
if (!token) {
|
|
260
|
-
logger$
|
|
262
|
+
logger$12.error("Token is required");
|
|
261
263
|
process.exit(1);
|
|
262
264
|
}
|
|
263
|
-
logger$
|
|
265
|
+
logger$12.log("\nValidating credentials...");
|
|
264
266
|
if (!await validateDokployToken(endpoint, token)) {
|
|
265
|
-
logger$
|
|
267
|
+
logger$12.error("\n✗ Invalid credentials. Please check your token and try again.");
|
|
266
268
|
process.exit(1);
|
|
267
269
|
}
|
|
268
270
|
await storeDokployCredentials(token, endpoint);
|
|
269
|
-
logger$
|
|
270
|
-
logger$
|
|
271
|
-
logger$
|
|
272
|
-
logger$
|
|
271
|
+
logger$12.log("\n✓ Successfully logged in to Dokploy!");
|
|
272
|
+
logger$12.log(` Endpoint: ${endpoint}`);
|
|
273
|
+
logger$12.log(` Credentials stored in: ${getCredentialsPath()}`);
|
|
274
|
+
logger$12.log("\nYou can now use deploy commands without setting DOKPLOY_API_TOKEN.");
|
|
273
275
|
}
|
|
274
276
|
if (provider === "hostinger") {
|
|
275
|
-
logger$
|
|
277
|
+
logger$12.log("\n🔐 Logging in to Hostinger...\n");
|
|
276
278
|
let token = providedToken;
|
|
277
279
|
if (!token) {
|
|
278
|
-
logger$
|
|
280
|
+
logger$12.log("\nGenerate a token at: https://hpanel.hostinger.com/profile/api\n");
|
|
279
281
|
token = await prompt$1("API Token: ", true);
|
|
280
282
|
}
|
|
281
283
|
if (!token) {
|
|
282
|
-
logger$
|
|
284
|
+
logger$12.error("Token is required");
|
|
283
285
|
process.exit(1);
|
|
284
286
|
}
|
|
285
287
|
await storeHostingerToken(token);
|
|
286
|
-
logger$
|
|
287
|
-
logger$
|
|
288
|
-
logger$
|
|
289
|
-
logger$
|
|
288
|
+
logger$12.log("\n✓ Hostinger token stored.");
|
|
289
|
+
logger$12.log(` Credentials stored in: ${getCredentialsPath()}`);
|
|
290
|
+
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.");
|
|
291
|
+
logger$12.log("\nYou can now use it as a DNS provider without setting HOSTINGER_API_TOKEN.");
|
|
290
292
|
}
|
|
291
293
|
}
|
|
292
294
|
/**
|
|
@@ -295,27 +297,27 @@ async function loginCommand(options) {
|
|
|
295
297
|
async function logoutCommand(options) {
|
|
296
298
|
const { provider = "dokploy" } = options;
|
|
297
299
|
if (provider === "all") {
|
|
298
|
-
if (await removeDokployCredentials()) logger$
|
|
299
|
-
else logger$
|
|
300
|
+
if (await removeDokployCredentials()) logger$12.log("\n✓ Logged out from all services");
|
|
301
|
+
else logger$12.log("\nNo stored credentials found");
|
|
300
302
|
return;
|
|
301
303
|
}
|
|
302
304
|
if (provider === "dokploy") {
|
|
303
|
-
if (await removeDokployCredentials()) logger$
|
|
304
|
-
else logger$
|
|
305
|
+
if (await removeDokployCredentials()) logger$12.log("\n✓ Logged out from Dokploy");
|
|
306
|
+
else logger$12.log("\nNo Dokploy credentials found");
|
|
305
307
|
}
|
|
306
308
|
}
|
|
307
309
|
/**
|
|
308
310
|
* Show current login status
|
|
309
311
|
*/
|
|
310
312
|
async function whoamiCommand() {
|
|
311
|
-
logger$
|
|
313
|
+
logger$12.log("\n📋 Current credentials:\n");
|
|
312
314
|
const dokploy = await getDokployCredentials();
|
|
313
315
|
if (dokploy) {
|
|
314
|
-
logger$
|
|
315
|
-
logger$
|
|
316
|
-
logger$
|
|
317
|
-
} else logger$
|
|
318
|
-
logger$
|
|
316
|
+
logger$12.log(" Dokploy:");
|
|
317
|
+
logger$12.log(` Endpoint: ${dokploy.endpoint}`);
|
|
318
|
+
logger$12.log(` Token: ${maskToken(dokploy.token)}`);
|
|
319
|
+
} else logger$12.log(" Dokploy: Not logged in");
|
|
320
|
+
logger$12.log(`\n Credentials file: ${getCredentialsPath()}`);
|
|
319
321
|
}
|
|
320
322
|
/**
|
|
321
323
|
* Mask a token for display
|
|
@@ -325,85 +327,14 @@ function maskToken(token) {
|
|
|
325
327
|
return `${token.slice(0, 4)}...${token.slice(-4)}`;
|
|
326
328
|
}
|
|
327
329
|
//#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
330
|
//#region src/workspace/client-generator.ts
|
|
399
331
|
/**
|
|
400
332
|
* Normalize routes to an array of patterns.
|
|
401
|
-
* Handles
|
|
333
|
+
* Handles a glob or a list of them.
|
|
402
334
|
* @internal Exported for use in dev command
|
|
403
335
|
*/
|
|
404
336
|
function normalizeRoutes(routes) {
|
|
405
337
|
if (!routes) return [];
|
|
406
|
-
if (isPartitionedRoutes(routes)) return Array.isArray(routes.paths) ? routes.paths : [routes.paths];
|
|
407
338
|
return Array.isArray(routes) ? routes : [routes];
|
|
408
339
|
}
|
|
409
340
|
//#endregion
|
|
@@ -422,7 +353,7 @@ function normalizeRoutes(routes) {
|
|
|
422
353
|
* trade than one that tells you what to type. Caddy's own `caddy trust` draws
|
|
423
354
|
* the line in the same place.
|
|
424
355
|
*/
|
|
425
|
-
const logger$
|
|
356
|
+
const logger$11 = console;
|
|
426
357
|
const run = promisify(execFile);
|
|
427
358
|
/**
|
|
428
359
|
* How each platform is told about a new authority.
|
|
@@ -474,15 +405,15 @@ async function trustCommand(options = {}) {
|
|
|
474
405
|
const certificate = join(workspace.root, LOCAL_CA_PATH);
|
|
475
406
|
if (!existsSync(certificate)) throw new NoLocalAuthority(certificate);
|
|
476
407
|
const { describe, steps } = installation(process.platform, certificate);
|
|
477
|
-
logger$
|
|
478
|
-
logger$
|
|
408
|
+
logger$11.log(`\n🔐 Trusting the local authority in ${describe}`);
|
|
409
|
+
logger$11.log(` ${certificate}\n`);
|
|
479
410
|
if (options.dryRun) {
|
|
480
|
-
for (const [command, args] of steps) logger$
|
|
411
|
+
for (const [command, args] of steps) logger$11.log(` ${command} ${args.join(" ")}`);
|
|
481
412
|
return;
|
|
482
413
|
}
|
|
483
|
-
logger$
|
|
414
|
+
logger$11.log(" This needs an admin password.\n");
|
|
484
415
|
for (const [command, args] of steps) await run(command, args, { stdio: "inherit" });
|
|
485
|
-
logger$
|
|
416
|
+
logger$11.log("\n✅ Local https addresses are trusted. Restart your browser.\n");
|
|
486
417
|
}
|
|
487
418
|
/**
|
|
488
419
|
* Trust the authority if this machine lacks it and its owner agrees.
|
|
@@ -499,7 +430,7 @@ async function ensureTrusted(root, url, options = {}) {
|
|
|
499
430
|
if (await isTrusted(url)) return;
|
|
500
431
|
if (!options.assumeYes) {
|
|
501
432
|
if (!process.stdin.isTTY) {
|
|
502
|
-
logger$
|
|
433
|
+
logger$11.log(" Local https addresses are not trusted here. Run \"gkm trust\", or \"gkm setup --yes\".");
|
|
503
434
|
return;
|
|
504
435
|
}
|
|
505
436
|
if (await declined(root)) return;
|
|
@@ -511,7 +442,7 @@ async function ensureTrusted(root, url, options = {}) {
|
|
|
511
442
|
});
|
|
512
443
|
if (!install) {
|
|
513
444
|
await recordDeclined(root);
|
|
514
|
-
logger$
|
|
445
|
+
logger$11.log(" Skipped. Run \"gkm trust\" later.");
|
|
515
446
|
return;
|
|
516
447
|
}
|
|
517
448
|
}
|
|
@@ -1061,7 +992,7 @@ function closeFakes(served) {
|
|
|
1061
992
|
}
|
|
1062
993
|
//#endregion
|
|
1063
994
|
//#region src/exec/index.ts
|
|
1064
|
-
const logger$
|
|
995
|
+
const logger$10 = console;
|
|
1065
996
|
/** `gkm exec` was given nothing to run. */
|
|
1066
997
|
var NoCommandSpecified = class extends Error {
|
|
1067
998
|
constructor() {
|
|
@@ -1084,17 +1015,17 @@ async function execCommand(commandArgs, options = {}) {
|
|
|
1084
1015
|
const cwd = options.cwd ?? process.cwd();
|
|
1085
1016
|
if (!commandArgs[0]) throw new NoCommandSpecified();
|
|
1086
1017
|
const defaultEnv = loadEnvFiles(".env");
|
|
1087
|
-
if (defaultEnv.loaded.length > 0) logger$
|
|
1018
|
+
if (defaultEnv.loaded.length > 0) logger$10.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
|
|
1088
1019
|
const { credentials, secretsJsonPath, appName } = await prepareEntryCredentials({ cwd });
|
|
1089
|
-
if (appName) logger$
|
|
1020
|
+
if (appName) logger$10.log(`📦 App: ${appName}`);
|
|
1090
1021
|
const secretCount = Object.keys(credentials).filter((k) => k !== "PORT").length;
|
|
1091
|
-
if (secretCount > 0) logger$
|
|
1022
|
+
if (secretCount > 0) logger$10.log(`🔐 Loaded ${secretCount} secret(s)`);
|
|
1092
1023
|
const preloadDir = join(cwd, ".gkm");
|
|
1093
1024
|
await mkdir(preloadDir, { recursive: true });
|
|
1094
1025
|
const preloadPath = join(preloadDir, "credentials-preload.mjs");
|
|
1095
1026
|
await createCredentialsPreload(preloadPath, secretsJsonPath);
|
|
1096
1027
|
const [cmd, ...args] = commandArgs;
|
|
1097
|
-
logger$
|
|
1028
|
+
logger$10.log(`🚀 Running: ${[cmd, ...args].join(" ")}`);
|
|
1098
1029
|
const nodeOptions = [(process.env.NODE_OPTIONS ?? "").replace(/--import[= ]\S+/g, "").trim(), `--import=${preloadPath}`].filter(Boolean).join(" ");
|
|
1099
1030
|
const child = spawn(cmd, args, {
|
|
1100
1031
|
cwd,
|
|
@@ -1108,7 +1039,7 @@ async function execCommand(commandArgs, options = {}) {
|
|
|
1108
1039
|
const exitCode = await new Promise((resolve) => {
|
|
1109
1040
|
child.on("close", (code) => resolve(code ?? 0));
|
|
1110
1041
|
child.on("error", (error) => {
|
|
1111
|
-
logger$
|
|
1042
|
+
logger$10.error(`Failed to run command: ${error.message}`);
|
|
1112
1043
|
resolve(1);
|
|
1113
1044
|
});
|
|
1114
1045
|
});
|
|
@@ -1116,7 +1047,7 @@ async function execCommand(commandArgs, options = {}) {
|
|
|
1116
1047
|
}
|
|
1117
1048
|
//#endregion
|
|
1118
1049
|
//#region src/dev/index.ts
|
|
1119
|
-
const logger$
|
|
1050
|
+
const logger$9 = console;
|
|
1120
1051
|
/**
|
|
1121
1052
|
* Normalize telescope configuration
|
|
1122
1053
|
* @internal Exported for testing
|
|
@@ -1181,39 +1112,36 @@ function normalizeHooksConfig(config, cwd = process.cwd()) {
|
|
|
1181
1112
|
return { serverHooksPath: resolve(cwd, serverPath) };
|
|
1182
1113
|
}
|
|
1183
1114
|
/**
|
|
1184
|
-
*
|
|
1115
|
+
* What a `--production` build is.
|
|
1116
|
+
*
|
|
1117
|
+
* A server runs its worker's background work itself — queues polled, crons
|
|
1118
|
+
* scheduled, subscribers drained — so the production entry wires all three.
|
|
1119
|
+
* The build writes their files on every server build, empty when there is
|
|
1120
|
+
* nothing, so including them costs an app without any nothing.
|
|
1121
|
+
*
|
|
1185
1122
|
* @internal Exported for testing
|
|
1186
1123
|
*/
|
|
1187
|
-
function normalizeProductionConfig(cliProduction
|
|
1124
|
+
function normalizeProductionConfig(cliProduction) {
|
|
1188
1125
|
if (!cliProduction) return;
|
|
1189
|
-
const config = configProduction ?? {};
|
|
1190
1126
|
return {
|
|
1191
1127
|
enabled: true,
|
|
1192
|
-
bundle:
|
|
1193
|
-
minify:
|
|
1194
|
-
healthCheck:
|
|
1195
|
-
gracefulShutdown:
|
|
1196
|
-
external:
|
|
1197
|
-
subscribers:
|
|
1198
|
-
openapi:
|
|
1199
|
-
optimizedHandlers:
|
|
1128
|
+
bundle: true,
|
|
1129
|
+
minify: true,
|
|
1130
|
+
healthCheck: "/health",
|
|
1131
|
+
gracefulShutdown: true,
|
|
1132
|
+
external: [],
|
|
1133
|
+
subscribers: "include",
|
|
1134
|
+
openapi: false,
|
|
1135
|
+
optimizedHandlers: true
|
|
1200
1136
|
};
|
|
1201
1137
|
}
|
|
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
1138
|
/** Read by the generated server: no subscriber is subscribed or polled. */
|
|
1211
1139
|
const SUBSCRIBERS_ENV = "GKM_SUBSCRIBERS";
|
|
1212
1140
|
async function devCommand(options) {
|
|
1213
1141
|
if (options.subscribers === false) process.env[SUBSCRIBERS_ENV] = "off";
|
|
1214
1142
|
if (options.entry) return entryDevCommand(options);
|
|
1215
1143
|
const defaultEnv = loadEnvFiles(".env");
|
|
1216
|
-
if (defaultEnv.loaded.length > 0) logger$
|
|
1144
|
+
if (defaultEnv.loaded.length > 0) logger$9.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
|
|
1217
1145
|
const appConfig = await loadAppConfig().catch((error) => {
|
|
1218
1146
|
if (error instanceof NotInAnApp || error instanceof NotABackendApp) return;
|
|
1219
1147
|
throw error;
|
|
@@ -1229,7 +1157,7 @@ async function devCommand(options) {
|
|
|
1229
1157
|
const workspaceAppPort = appConfig.app.port;
|
|
1230
1158
|
const workspace = appConfig.workspace;
|
|
1231
1159
|
if (appConfig.app.entry) {
|
|
1232
|
-
logger$
|
|
1160
|
+
logger$9.log(`📄 Using entry point: ${appConfig.app.entry}`);
|
|
1233
1161
|
return entryDevCommand({
|
|
1234
1162
|
...options,
|
|
1235
1163
|
entry: appConfig.app.entry,
|
|
@@ -1239,19 +1167,18 @@ async function devCommand(options) {
|
|
|
1239
1167
|
}
|
|
1240
1168
|
if (config.env) {
|
|
1241
1169
|
const { loaded, missing } = loadEnvFiles(config.env, appRoot);
|
|
1242
|
-
if (loaded.length > 0) logger$
|
|
1243
|
-
if (missing.length > 0) logger$
|
|
1170
|
+
if (loaded.length > 0) logger$9.log(`📦 Loaded env: ${loaded.join(", ")}`);
|
|
1171
|
+
if (missing.length > 0) logger$9.warn(`⚠️ Missing env files: ${missing.join(", ")}`);
|
|
1244
1172
|
}
|
|
1245
|
-
const resolved = resolveProviders(config, { provider: "server" });
|
|
1246
1173
|
const telescope = normalizeTelescopeConfig(config.telescope);
|
|
1247
1174
|
const studio = normalizeStudioConfig(config.studio);
|
|
1248
1175
|
const hooks = normalizeHooksConfig(config.hooks, appRoot);
|
|
1249
|
-
const enableOpenApi = resolveOpenApiConfig(config).enabled
|
|
1176
|
+
const enableOpenApi = resolveOpenApiConfig(config).enabled;
|
|
1250
1177
|
const build = (bustCache = false) => quietly(() => buildApp({
|
|
1251
1178
|
config,
|
|
1252
1179
|
workspaceRoot: workspace?.root ?? secretsRoot,
|
|
1253
1180
|
appRoot,
|
|
1254
|
-
|
|
1181
|
+
target: "server",
|
|
1255
1182
|
enableOpenApi,
|
|
1256
1183
|
cacheBackend: cacheBackendFor(providerOf(workspace ?? config)),
|
|
1257
1184
|
telescope,
|
|
@@ -1271,9 +1198,9 @@ async function devCommand(options) {
|
|
|
1271
1198
|
stage: workspace.stages.local,
|
|
1272
1199
|
...options.fake ? { fake: true } : {}
|
|
1273
1200
|
});
|
|
1274
|
-
if (reconciled.
|
|
1275
|
-
logger$
|
|
1276
|
-
for (const
|
|
1201
|
+
if (reconciled.services.length > 0) {
|
|
1202
|
+
logger$9.log("🐳 Services");
|
|
1203
|
+
for (const line of describeServices(reconciled.services)) logger$9.log(line);
|
|
1277
1204
|
}
|
|
1278
1205
|
Object.assign(appSecrets, reconciled.env);
|
|
1279
1206
|
const own = reconciled.plan.resources.find((r) => (r.kind === "rest-api" || r.kind === "site") && appKey(r.id) === workspaceAppName);
|
|
@@ -1286,7 +1213,7 @@ async function devCommand(options) {
|
|
|
1286
1213
|
secretsJsonPath = join(secretsDir, secretsFileName);
|
|
1287
1214
|
await writeFile(secretsJsonPath, JSON.stringify(appSecrets, null, 2));
|
|
1288
1215
|
}
|
|
1289
|
-
const devServer = new DevServer(
|
|
1216
|
+
const devServer = new DevServer(options.port ?? workspaceAppPort ?? 3e3, options.portExplicit ?? workspaceAppPort !== void 0, enableOpenApi, telescope, studio, runtime, appRoot, secretsJsonPath, initial.selfServing !== void 0);
|
|
1290
1217
|
devServer.label = workspaceAppName ?? "server";
|
|
1291
1218
|
devServer.publicUrl = publicUrl;
|
|
1292
1219
|
await devServer.start();
|
|
@@ -1307,11 +1234,11 @@ async function devCommand(options) {
|
|
|
1307
1234
|
cwd: appRoot
|
|
1308
1235
|
});
|
|
1309
1236
|
watcher.on("error", (error) => {
|
|
1310
|
-
logger$
|
|
1237
|
+
logger$9.error("❌ Watcher error:", error);
|
|
1311
1238
|
});
|
|
1312
1239
|
let rebuildTimeout = null;
|
|
1313
1240
|
watcher.on("change", async (path) => {
|
|
1314
|
-
logger$
|
|
1241
|
+
logger$9.log(`🔄 ${path} changed — rebuilding`);
|
|
1315
1242
|
if (rebuildTimeout) clearTimeout(rebuildTimeout);
|
|
1316
1243
|
rebuildTimeout = setTimeout(async () => {
|
|
1317
1244
|
try {
|
|
@@ -1320,8 +1247,8 @@ async function devCommand(options) {
|
|
|
1320
1247
|
await devServer.restart();
|
|
1321
1248
|
} catch (error) {
|
|
1322
1249
|
const err = error;
|
|
1323
|
-
logger$
|
|
1324
|
-
if (err.stack) logger$
|
|
1250
|
+
logger$9.error(`❌ Rebuild failed: ${err.message}`);
|
|
1251
|
+
if (err.stack) logger$9.error(err.stack);
|
|
1325
1252
|
}
|
|
1326
1253
|
}, 300);
|
|
1327
1254
|
});
|
|
@@ -1329,9 +1256,9 @@ async function devCommand(options) {
|
|
|
1329
1256
|
const shutdown = () => {
|
|
1330
1257
|
if (isShuttingDown) return;
|
|
1331
1258
|
isShuttingDown = true;
|
|
1332
|
-
logger$
|
|
1259
|
+
logger$9.log("\n🛑 Shutting down...");
|
|
1333
1260
|
Promise.all([watcher.close(), devServer.stop()]).catch((err) => {
|
|
1334
|
-
logger$
|
|
1261
|
+
logger$9.error("Error during shutdown:", err);
|
|
1335
1262
|
}).finally(() => {
|
|
1336
1263
|
process.exit(0);
|
|
1337
1264
|
});
|
|
@@ -1533,7 +1460,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1533
1460
|
const frontendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "web");
|
|
1534
1461
|
const conflicts = checkPortConflicts(workspace);
|
|
1535
1462
|
if (conflicts.length > 0) {
|
|
1536
|
-
for (const conflict of conflicts) logger$
|
|
1463
|
+
for (const conflict of conflicts) logger$9.error(`❌ Port conflict: Apps "${conflict.app1}" and "${conflict.app2}" both use port ${conflict.port}`);
|
|
1537
1464
|
throw new Error("Port conflicts detected. Please assign unique ports to each app.");
|
|
1538
1465
|
}
|
|
1539
1466
|
if (frontendApps.length > 0) {
|
|
@@ -1542,10 +1469,10 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1542
1469
|
for (const result of validationResults) {
|
|
1543
1470
|
if (!result.valid) {
|
|
1544
1471
|
hasErrors = true;
|
|
1545
|
-
logger$
|
|
1546
|
-
for (const error of result.errors) logger$
|
|
1472
|
+
logger$9.error(`\n❌ Frontend app "${result.appName}" validation failed:`);
|
|
1473
|
+
for (const error of result.errors) logger$9.error(` • ${error}`);
|
|
1547
1474
|
}
|
|
1548
|
-
for (const warning of result.warnings) logger$
|
|
1475
|
+
for (const warning of result.warnings) logger$9.warn(` ⚠️ ${result.appName}: ${warning}`);
|
|
1549
1476
|
}
|
|
1550
1477
|
if (hasErrors) throw new Error("Frontend app validation failed. Fix the issues above and try again.");
|
|
1551
1478
|
}
|
|
@@ -1554,21 +1481,21 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1554
1481
|
stage: workspace.stages.local,
|
|
1555
1482
|
...options.fake ? { fake: true } : {}
|
|
1556
1483
|
});
|
|
1557
|
-
if (reconciled.
|
|
1558
|
-
logger$
|
|
1559
|
-
for (const
|
|
1484
|
+
if (reconciled.services.length > 0) {
|
|
1485
|
+
logger$9.log("🐳 Services");
|
|
1486
|
+
for (const line of describeServices(reconciled.services)) logger$9.log(line);
|
|
1560
1487
|
}
|
|
1561
1488
|
const secretsEnv = {
|
|
1562
1489
|
...rawSecrets,
|
|
1563
1490
|
...reconciled.env
|
|
1564
1491
|
};
|
|
1565
1492
|
const fakes = options.fake ? await serveFakes(reconciled.fakes, reconciled.ports) : [];
|
|
1566
|
-
for (const { id, port } of fakes) logger$
|
|
1493
|
+
for (const { id, port } of fakes) logger$9.log(`🎭 ${id} fake: http://localhost:${port}`);
|
|
1567
1494
|
if (options.migrate || options.seed) {
|
|
1568
|
-
const { prepareDevDatabases } = await import("./migrate-
|
|
1495
|
+
const { prepareDevDatabases } = await import("./migrate-DhxTIA0z.mjs");
|
|
1569
1496
|
await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
|
|
1570
|
-
} else await import("./migrate-
|
|
1571
|
-
logger$
|
|
1497
|
+
} else await import("./migrate-DhxTIA0z.mjs").then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
|
|
1498
|
+
logger$9.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
|
|
1572
1499
|
});
|
|
1573
1500
|
const appUrls = {};
|
|
1574
1501
|
for (const resource of reconciled.plan.resources) {
|
|
@@ -1589,11 +1516,11 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1589
1516
|
else {
|
|
1590
1517
|
const { filters, unpackaged } = turboFilters(workspace);
|
|
1591
1518
|
turboFilter = filters.flatMap((name) => ["--filter", name]);
|
|
1592
|
-
if (unpackaged.length > 0) logger$
|
|
1519
|
+
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
1520
|
}
|
|
1594
1521
|
const buildOrder = getAppBuildOrder(workspace);
|
|
1595
1522
|
const width = Math.max(...buildOrder.map((name) => name.length));
|
|
1596
|
-
logger$
|
|
1523
|
+
logger$9.log(`\n${workspace.name}: ${appCount} app(s)`);
|
|
1597
1524
|
for (const appName of buildOrder) {
|
|
1598
1525
|
const app = workspace.apps[appName];
|
|
1599
1526
|
if (!app) continue;
|
|
@@ -1601,14 +1528,14 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1601
1528
|
const own = reconciled.plan.resources.find((r) => r.kind === "mobile-app" && appKey(r.id) === appName);
|
|
1602
1529
|
const scheme = own ? reconciled.env[own.envKey] : void 0;
|
|
1603
1530
|
const device = Object.entries(reconciled.env).find(([key]) => key.endsWith("_DEVICE_URL"))?.[1];
|
|
1604
|
-
logger$
|
|
1531
|
+
logger$9.log(` ${appName.padEnd(width)} ${scheme ? `${scheme}://` : "mobile"}${device ? ` — devices reach ${new URL(device).hostname}` : ""}`);
|
|
1605
1532
|
continue;
|
|
1606
1533
|
}
|
|
1607
1534
|
const local = `http://localhost:${app.port}`;
|
|
1608
1535
|
const url = Object.entries(appUrls).find(([id]) => appKey(id) === appName)?.[1];
|
|
1609
|
-
logger$
|
|
1536
|
+
logger$9.log(` ${appName.padEnd(width)} ${url ? `${url} -> ${local}` : local}`);
|
|
1610
1537
|
}
|
|
1611
|
-
logger$
|
|
1538
|
+
logger$9.log("");
|
|
1612
1539
|
const configFiles = [
|
|
1613
1540
|
"gkm.config.ts",
|
|
1614
1541
|
"gkm.config.js",
|
|
@@ -1655,7 +1582,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1655
1582
|
const shutdown = () => {
|
|
1656
1583
|
if (isShuttingDown) return;
|
|
1657
1584
|
isShuttingDown = true;
|
|
1658
|
-
logger$
|
|
1585
|
+
logger$9.log("\n🛑 Shutting down workspace...");
|
|
1659
1586
|
closeFakes(fakes);
|
|
1660
1587
|
const pid = turboProcess.pid;
|
|
1661
1588
|
if (pid) try {
|
|
@@ -1680,7 +1607,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1680
1607
|
process.on("SIGTERM", shutdown);
|
|
1681
1608
|
return new Promise((resolve, reject) => {
|
|
1682
1609
|
turboProcess.on("error", (error) => {
|
|
1683
|
-
logger$
|
|
1610
|
+
logger$9.error("❌ Turbo error:", error);
|
|
1684
1611
|
reject(error);
|
|
1685
1612
|
});
|
|
1686
1613
|
turboProcess.on("exit", (code) => {
|
|
@@ -1699,11 +1626,11 @@ async function entryDevCommand(options) {
|
|
|
1699
1626
|
const entryPath = resolve(process.cwd(), entry);
|
|
1700
1627
|
if (!existsSync(entryPath)) throw new Error(`Entry file not found: ${entryPath}`);
|
|
1701
1628
|
const defaultEnv = loadEnvFiles(".env");
|
|
1702
|
-
if (defaultEnv.loaded.length > 0) logger$
|
|
1629
|
+
if (defaultEnv.loaded.length > 0) logger$9.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
|
|
1703
1630
|
const { credentials, resolvedPort, secretsJsonPath, appName } = await prepareEntryCredentials({ explicitPort: options.portExplicit ? options.port : void 0 });
|
|
1704
|
-
if (appName) logger$
|
|
1705
|
-
logger$
|
|
1706
|
-
if (Object.keys(credentials).length > 1) logger$
|
|
1631
|
+
if (appName) logger$9.log(`📦 App: ${appName} (port ${resolvedPort})`);
|
|
1632
|
+
logger$9.log(`🚀 Starting entry file: ${entry} on port ${resolvedPort}`);
|
|
1633
|
+
if (Object.keys(credentials).length > 1) logger$9.log(`🔐 Loaded ${Object.keys(credentials).length - 1} secret(s) + PORT`);
|
|
1707
1634
|
const wrapperDir = join(process.cwd(), ".gkm");
|
|
1708
1635
|
await mkdir(wrapperDir, { recursive: true });
|
|
1709
1636
|
const wrapperPath = join(wrapperDir, "entry-wrapper.ts");
|
|
@@ -1714,7 +1641,7 @@ async function entryDevCommand(options) {
|
|
|
1714
1641
|
const shutdown = () => {
|
|
1715
1642
|
if (isShuttingDown) return;
|
|
1716
1643
|
isShuttingDown = true;
|
|
1717
|
-
logger$
|
|
1644
|
+
logger$9.log("\n🛑 Shutting down...");
|
|
1718
1645
|
runner.stop();
|
|
1719
1646
|
process.exit(0);
|
|
1720
1647
|
};
|
|
@@ -1750,14 +1677,14 @@ var EntryRunner = class {
|
|
|
1750
1677
|
});
|
|
1751
1678
|
let restartTimeout = null;
|
|
1752
1679
|
this.watcher.on("change", (path) => {
|
|
1753
|
-
logger$
|
|
1680
|
+
logger$9.log(`📝 File changed: ${path}`);
|
|
1754
1681
|
if (restartTimeout) clearTimeout(restartTimeout);
|
|
1755
1682
|
restartTimeout = setTimeout(async () => {
|
|
1756
|
-
logger$
|
|
1683
|
+
logger$9.log("🔄 Restarting...");
|
|
1757
1684
|
await this.restart();
|
|
1758
1685
|
}, 300);
|
|
1759
1686
|
});
|
|
1760
|
-
logger$
|
|
1687
|
+
logger$9.log(`👀 Watching for changes in: ${watchDir}`);
|
|
1761
1688
|
}
|
|
1762
1689
|
}
|
|
1763
1690
|
async runProcess() {
|
|
@@ -1772,17 +1699,17 @@ var EntryRunner = class {
|
|
|
1772
1699
|
});
|
|
1773
1700
|
this.isRunning = true;
|
|
1774
1701
|
this.childProcess.on("error", (error) => {
|
|
1775
|
-
logger$
|
|
1702
|
+
logger$9.error("❌ Process error:", error);
|
|
1776
1703
|
});
|
|
1777
1704
|
this.childProcess.on("exit", (code) => {
|
|
1778
|
-
if (code !== null && code !== 0 && code !== 143) logger$
|
|
1705
|
+
if (code !== null && code !== 0 && code !== 143) logger$9.error(`❌ Process exited with code ${code}`);
|
|
1779
1706
|
this.isRunning = false;
|
|
1780
1707
|
});
|
|
1781
1708
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1782
1709
|
if (this.isRunning) {
|
|
1783
|
-
logger$
|
|
1784
|
-
logger$
|
|
1785
|
-
logger$
|
|
1710
|
+
logger$9.log("");
|
|
1711
|
+
logger$9.log(` \x1b[32m✓ Ready\x1b[0m at \x1b[36mhttp://localhost:${this.port}\x1b[0m`);
|
|
1712
|
+
logger$9.log("");
|
|
1786
1713
|
}
|
|
1787
1714
|
}
|
|
1788
1715
|
async restart() {
|
|
@@ -1960,7 +1887,6 @@ function listenerOn(port) {
|
|
|
1960
1887
|
}
|
|
1961
1888
|
}
|
|
1962
1889
|
var DevServer = class {
|
|
1963
|
-
provider;
|
|
1964
1890
|
requestedPort;
|
|
1965
1891
|
portExplicit;
|
|
1966
1892
|
enableOpenApi;
|
|
@@ -1974,8 +1900,7 @@ var DevServer = class {
|
|
|
1974
1900
|
isRunning = false;
|
|
1975
1901
|
actualPort;
|
|
1976
1902
|
startTime = Date.now();
|
|
1977
|
-
constructor(
|
|
1978
|
-
this.provider = provider;
|
|
1903
|
+
constructor(requestedPort, portExplicit, enableOpenApi, telescope, studio, runtime = "node", appRoot = process.cwd(), secretsJsonPath, selfServing = false) {
|
|
1979
1904
|
this.requestedPort = requestedPort;
|
|
1980
1905
|
this.portExplicit = portExplicit;
|
|
1981
1906
|
this.enableOpenApi = enableOpenApi;
|
|
@@ -2002,9 +1927,9 @@ var DevServer = class {
|
|
|
2002
1927
|
this.actualPort = this.requestedPort;
|
|
2003
1928
|
} else {
|
|
2004
1929
|
this.actualPort = await findAvailablePort(this.requestedPort);
|
|
2005
|
-
if (this.actualPort !== this.requestedPort) logger$
|
|
1930
|
+
if (this.actualPort !== this.requestedPort) logger$9.log(`ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`);
|
|
2006
1931
|
}
|
|
2007
|
-
const serverEntryPath = join(this.appRoot, ".gkm",
|
|
1932
|
+
const serverEntryPath = join(this.appRoot, ".gkm", "server", "server.ts");
|
|
2008
1933
|
await this.createServerEntry();
|
|
2009
1934
|
this.serverProcess = spawn(...tsxCommand(this.appRoot, [
|
|
2010
1935
|
serverEntryPath,
|
|
@@ -2021,10 +1946,10 @@ var DevServer = class {
|
|
|
2021
1946
|
});
|
|
2022
1947
|
this.isRunning = true;
|
|
2023
1948
|
this.serverProcess.on("error", (error) => {
|
|
2024
|
-
logger$
|
|
1949
|
+
logger$9.error("❌ Server error:", error);
|
|
2025
1950
|
});
|
|
2026
1951
|
this.serverProcess.on("exit", (code, signal) => {
|
|
2027
|
-
if (code !== null && code !== 0 && signal !== "SIGTERM") logger$
|
|
1952
|
+
if (code !== null && code !== 0 && signal !== "SIGTERM") logger$9.error(`❌ Server exited with code ${code}`);
|
|
2028
1953
|
this.isRunning = false;
|
|
2029
1954
|
});
|
|
2030
1955
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
@@ -2032,13 +1957,13 @@ var DevServer = class {
|
|
|
2032
1957
|
const local = `http://localhost:${this.actualPort}`;
|
|
2033
1958
|
const address = this.publicUrl ? `${this.publicUrl} -> ${local}` : local;
|
|
2034
1959
|
const seconds = ((Date.now() - this.startTime) / 1e3).toFixed(1);
|
|
2035
|
-
logger$
|
|
1960
|
+
logger$9.log(`\x1b[32m✓\x1b[0m ${this.label} ready in ${seconds}s ${address}`);
|
|
2036
1961
|
const tools = this.selfServing ? [] : [
|
|
2037
1962
|
...this.enableOpenApi ? ["docs /__docs"] : [],
|
|
2038
1963
|
...this.telescope ? [`telescope ${this.telescope.path}`] : [],
|
|
2039
1964
|
...this.studio ? [`studio ${this.studio.path}`] : []
|
|
2040
1965
|
];
|
|
2041
|
-
if (tools.length > 0) logger$
|
|
1966
|
+
if (tools.length > 0) logger$9.log(` ${tools.join(" · ")}`);
|
|
2042
1967
|
}
|
|
2043
1968
|
}
|
|
2044
1969
|
async stop() {
|
|
@@ -2076,7 +2001,7 @@ var DevServer = class {
|
|
|
2076
2001
|
}
|
|
2077
2002
|
async createServerEntry() {
|
|
2078
2003
|
const { writeFile: fsWriteFile } = await import("node:fs/promises");
|
|
2079
|
-
await fsWriteFile(join(this.appRoot, ".gkm",
|
|
2004
|
+
await fsWriteFile(join(this.appRoot, ".gkm", "server", "server.ts"), generateServerEntryContent({
|
|
2080
2005
|
secretsJsonPath: this.secretsJsonPath,
|
|
2081
2006
|
runtime: this.runtime,
|
|
2082
2007
|
enableOpenApi: this.enableOpenApi,
|
|
@@ -2088,22 +2013,22 @@ var DevServer = class {
|
|
|
2088
2013
|
//#region src/generators/CronGenerator.ts
|
|
2089
2014
|
var CronGenerator = class extends ConstructGenerator {
|
|
2090
2015
|
async build(context, constructs, outputDir, options) {
|
|
2091
|
-
const
|
|
2016
|
+
const target = options?.target ?? "aws";
|
|
2092
2017
|
const logger = console;
|
|
2093
2018
|
const cronInfos = [];
|
|
2094
|
-
if (
|
|
2019
|
+
if (target === "server") {
|
|
2095
2020
|
await this.generateServerCronsFile(outputDir, constructs);
|
|
2096
2021
|
logger.log(`Generated server crons file with ${constructs.length} crons`);
|
|
2097
2022
|
return cronInfos;
|
|
2098
2023
|
}
|
|
2099
|
-
if (constructs.length === 0
|
|
2024
|
+
if (constructs.length === 0) return cronInfos;
|
|
2100
2025
|
const cronsDir = join(outputDir, "crons");
|
|
2101
2026
|
await mkdir(cronsDir, { recursive: true });
|
|
2102
2027
|
for (const { key, construct, path } of constructs) {
|
|
2103
2028
|
const handlerFile = await this.generateCronHandler(cronsDir, path.relative, key, context, construct.owner);
|
|
2104
2029
|
cronInfos.push({
|
|
2105
2030
|
name: key,
|
|
2106
|
-
handler: relative(process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
|
|
2031
|
+
handler: relative(options?.root ?? process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
|
|
2107
2032
|
schedule: construct.schedule || "rate(1 hour)",
|
|
2108
2033
|
timeout: construct.timeout,
|
|
2109
2034
|
memorySize: construct.memorySize,
|
|
@@ -2293,17 +2218,17 @@ var FunctionGenerator = class extends ConstructGenerator {
|
|
|
2293
2218
|
return Function.isFunction(value);
|
|
2294
2219
|
}
|
|
2295
2220
|
async build(context, constructs, outputDir, options) {
|
|
2296
|
-
const
|
|
2221
|
+
const target = options?.target ?? "aws";
|
|
2297
2222
|
const logger = console;
|
|
2298
2223
|
const functionInfos = [];
|
|
2299
|
-
if (constructs.length === 0 ||
|
|
2224
|
+
if (constructs.length === 0 || target === "server") return functionInfos;
|
|
2300
2225
|
const functionsDir = join(outputDir, "functions");
|
|
2301
2226
|
await mkdir(functionsDir, { recursive: true });
|
|
2302
2227
|
for (const { key, construct, path } of constructs) {
|
|
2303
2228
|
const handlerFile = await this.generateFunctionHandler(functionsDir, path.relative, key, context, construct.owner);
|
|
2304
2229
|
functionInfos.push({
|
|
2305
2230
|
name: key,
|
|
2306
|
-
handler: relative(process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
|
|
2231
|
+
handler: relative(options?.root ?? process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
|
|
2307
2232
|
timeout: construct.timeout,
|
|
2308
2233
|
memorySize: construct.memorySize,
|
|
2309
2234
|
environment: await construct.getEnvironment({ markOptional: context.markOptional }),
|
|
@@ -2347,22 +2272,22 @@ var QueueGenerator = class extends ConstructGenerator {
|
|
|
2347
2272
|
return Queue.isQueue(value);
|
|
2348
2273
|
}
|
|
2349
2274
|
async build(context, constructs, outputDir, options) {
|
|
2350
|
-
const
|
|
2275
|
+
const target = options?.target ?? "aws";
|
|
2351
2276
|
const logger = console;
|
|
2352
2277
|
const queueInfos = [];
|
|
2353
|
-
if (
|
|
2278
|
+
if (target === "server") {
|
|
2354
2279
|
await this.generateServerQueuesFile(outputDir, constructs);
|
|
2355
2280
|
logger.log(`Generated server queues file with ${constructs.length} queues (polling mode)`);
|
|
2356
2281
|
return queueInfos;
|
|
2357
2282
|
}
|
|
2358
|
-
if (constructs.length === 0
|
|
2283
|
+
if (constructs.length === 0) return queueInfos;
|
|
2359
2284
|
const queuesDir = join(outputDir, "queues");
|
|
2360
2285
|
await mkdir(queuesDir, { recursive: true });
|
|
2361
2286
|
for (const { key, construct, path } of constructs) {
|
|
2362
2287
|
const handlerFile = await this.generateQueueHandler(queuesDir, path.relative, key, context, construct.owner);
|
|
2363
2288
|
queueInfos.push({
|
|
2364
2289
|
name: construct.name,
|
|
2365
|
-
handler: relative(process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
|
|
2290
|
+
handler: relative(options?.root ?? process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
|
|
2366
2291
|
batchSize: construct.batchSize,
|
|
2367
2292
|
fifo: construct.fifo,
|
|
2368
2293
|
timeout: construct.timeout,
|
|
@@ -2528,217 +2453,6 @@ var TopicGenerator = class extends ConstructGenerator {
|
|
|
2528
2453
|
}
|
|
2529
2454
|
};
|
|
2530
2455
|
//#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
2456
|
//#region src/build/owners.ts
|
|
2743
2457
|
/**
|
|
2744
2458
|
* What a generated entry imports its runtime from, read off the discovered
|
|
@@ -2808,27 +2522,6 @@ function restApis(declared) {
|
|
|
2808
2522
|
function servedSurface(declared, workspaceRoot, appRoot) {
|
|
2809
2523
|
return restApis(declared).find((d) => resolve(workspaceRoot, d.path) === resolve(appRoot));
|
|
2810
2524
|
}
|
|
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
2525
|
//#endregion
|
|
2833
2526
|
//#region src/build/surfaceEntry.ts
|
|
2834
2527
|
/** Whether a construct serves itself, asked of the construct. */
|
|
@@ -2905,6 +2598,24 @@ function rootGkmConfig(workspace) {
|
|
|
2905
2598
|
constructs: allConstructGlobs(workspace)
|
|
2906
2599
|
};
|
|
2907
2600
|
}
|
|
2601
|
+
/** Directories under `.gkm/` that the removed per-provider builds wrote. */
|
|
2602
|
+
const SUPERSEDED_OUTPUT = [
|
|
2603
|
+
"aws-apigatewayv1",
|
|
2604
|
+
"aws-apigatewayv2",
|
|
2605
|
+
"aws-lambda"
|
|
2606
|
+
];
|
|
2607
|
+
/** Whether `--provider` named something a build can target. */
|
|
2608
|
+
function isMainProvider(value) {
|
|
2609
|
+
return value === "aws" || value === "server";
|
|
2610
|
+
}
|
|
2611
|
+
var UnknownBuildProvider = class extends Error {
|
|
2612
|
+
provider;
|
|
2613
|
+
constructor(provider) {
|
|
2614
|
+
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.`);
|
|
2615
|
+
this.provider = provider;
|
|
2616
|
+
this.name = "UnknownBuildProvider";
|
|
2617
|
+
}
|
|
2618
|
+
};
|
|
2908
2619
|
async function buildCommand(options) {
|
|
2909
2620
|
const loadedConfig = await loadWorkspaceConfig();
|
|
2910
2621
|
if (loadedConfig.type === "workspace") {
|
|
@@ -2918,25 +2629,62 @@ async function buildCommand(options) {
|
|
|
2918
2629
|
}
|
|
2919
2630
|
}
|
|
2920
2631
|
const config = loadedConfig.type !== "workspace" ? await loadConfig() : resolve(process.cwd()) === resolve(loadedConfig.workspace.root) ? rootGkmConfig(loadedConfig.workspace) : (await loadAppConfig()).gkmConfig;
|
|
2921
|
-
const
|
|
2922
|
-
const
|
|
2923
|
-
const
|
|
2924
|
-
const
|
|
2632
|
+
const target = options.provider ?? providerOf(loadedConfig.workspace);
|
|
2633
|
+
const workspaceRoot = loadedConfig.workspace.root;
|
|
2634
|
+
const appRoot = process.cwd();
|
|
2635
|
+
const output = await buildOneApp({
|
|
2636
|
+
config,
|
|
2637
|
+
options,
|
|
2638
|
+
workspace: loadedConfig.workspace,
|
|
2639
|
+
appRoot,
|
|
2640
|
+
target
|
|
2641
|
+
});
|
|
2642
|
+
if (resolve(appRoot) === resolve(workspaceRoot)) await writeManifest({
|
|
2643
|
+
workspaceRoot,
|
|
2644
|
+
target,
|
|
2645
|
+
builds: output.built ? [output.built] : [],
|
|
2646
|
+
constructs: await declaredIn(config, workspaceRoot),
|
|
2647
|
+
backends: backendsOf(loadedConfig.workspace)
|
|
2648
|
+
});
|
|
2649
|
+
return output;
|
|
2650
|
+
}
|
|
2651
|
+
/** What the workspace declares, read through a config's globs. */
|
|
2652
|
+
async function declaredIn(config, cwd) {
|
|
2653
|
+
return discover({
|
|
2654
|
+
patterns: config.constructs,
|
|
2655
|
+
cwd
|
|
2656
|
+
});
|
|
2657
|
+
}
|
|
2658
|
+
/**
|
|
2659
|
+
* The backend choices the manifest records — answered by the deploy target,
|
|
2660
|
+
* the same place reconcile and the build's drivers read them.
|
|
2661
|
+
*/
|
|
2662
|
+
function backendsOf(workspace) {
|
|
2663
|
+
return {
|
|
2664
|
+
cache: cacheBackendFor(providerOf(workspace)),
|
|
2665
|
+
email: "ses"
|
|
2666
|
+
};
|
|
2667
|
+
}
|
|
2668
|
+
/** One app's build, from its config and the command's options. */
|
|
2669
|
+
async function buildOneApp(input) {
|
|
2670
|
+
const { config, options, workspace, appRoot, target } = input;
|
|
2671
|
+
const cacheBackend = cacheBackendFor(providerOf(workspace));
|
|
2672
|
+
const production = normalizeProductionConfig(options.production ?? false);
|
|
2925
2673
|
if (production) logger$8.log(`🏭 Building for PRODUCTION`);
|
|
2926
|
-
logger$8.log(`Building
|
|
2674
|
+
logger$8.log(`Building for ${target}`);
|
|
2927
2675
|
logger$8.log(`Loading constructs from: ${formatRoutes(config.constructs)}`);
|
|
2928
2676
|
const telescope = production ? void 0 : normalizeTelescopeConfig(config.telescope);
|
|
2929
2677
|
if (telescope) logger$8.log(`🔭 Telescope enabled at ${telescope.path}`);
|
|
2930
2678
|
const studio = production ? void 0 : normalizeStudioConfig(config.studio);
|
|
2931
2679
|
if (studio) logger$8.log(`🗄️ Studio enabled at ${studio.path}`);
|
|
2932
|
-
const hooks = normalizeHooksConfig(config.hooks);
|
|
2680
|
+
const hooks = normalizeHooksConfig(config.hooks, appRoot);
|
|
2933
2681
|
if (hooks) logger$8.log(`🪝 Server hooks enabled`);
|
|
2934
2682
|
return buildApp({
|
|
2935
2683
|
config,
|
|
2936
|
-
workspaceRoot:
|
|
2937
|
-
appRoot
|
|
2938
|
-
|
|
2939
|
-
enableOpenApi:
|
|
2684
|
+
workspaceRoot: workspace.root,
|
|
2685
|
+
appRoot,
|
|
2686
|
+
target,
|
|
2687
|
+
enableOpenApi: options.enableOpenApi ?? false,
|
|
2940
2688
|
cacheBackend,
|
|
2941
2689
|
production,
|
|
2942
2690
|
telescope,
|
|
@@ -2948,6 +2696,33 @@ async function buildCommand(options) {
|
|
|
2948
2696
|
});
|
|
2949
2697
|
}
|
|
2950
2698
|
/**
|
|
2699
|
+
* Write the application's manifest: every declared construct, with what each
|
|
2700
|
+
* app generated folded into it.
|
|
2701
|
+
*
|
|
2702
|
+
* The manifest belongs to the whole application, so only the root writes it —
|
|
2703
|
+
* once, after every app has built, from the constructs the workspace declares.
|
|
2704
|
+
* An app's own build generates its handlers and nothing else.
|
|
2705
|
+
*/
|
|
2706
|
+
async function writeManifest(input) {
|
|
2707
|
+
const { workspaceRoot, target, builds, constructs, backends } = input;
|
|
2708
|
+
const manifestDir = join(workspaceRoot, ".gkm", "manifest");
|
|
2709
|
+
await mkdir(manifestDir, { recursive: true });
|
|
2710
|
+
await rm(join(manifestDir, `${target === "aws" ? "server" : "aws"}.ts`), { force: true });
|
|
2711
|
+
const withServed = builds.reduce((manifest, build) => withRoutes(manifest, build.routes.filter((route) => route.method !== "ALL"), {
|
|
2712
|
+
perRoute: true,
|
|
2713
|
+
...build.surface ? { surface: build.surface } : {}
|
|
2714
|
+
}), constructs);
|
|
2715
|
+
const folded = withCompute(withServed, {
|
|
2716
|
+
functions: builds.flatMap((build) => build.functions),
|
|
2717
|
+
crons: builds.flatMap((build) => build.crons),
|
|
2718
|
+
queues: builds.flatMap((build) => build.queues),
|
|
2719
|
+
subscribers: builds.flatMap((build) => build.subscribers)
|
|
2720
|
+
});
|
|
2721
|
+
const path = join(manifestDir, `${target}.ts`);
|
|
2722
|
+
await writeFile(path, manifestModule(folded, backends));
|
|
2723
|
+
logger$8.log(`Manifest: ${relative(process.cwd(), path)}`);
|
|
2724
|
+
}
|
|
2725
|
+
/**
|
|
2951
2726
|
* Turn one app's constructs into its generated entry.
|
|
2952
2727
|
*
|
|
2953
2728
|
* `gkm build` and `gkm dev` both run this. They used to be two copies of it,
|
|
@@ -2956,8 +2731,8 @@ async function buildCommand(options) {
|
|
|
2956
2731
|
* dev did not, and failed on exactly the apps the build had learned to handle.
|
|
2957
2732
|
*/
|
|
2958
2733
|
async function buildApp(input) {
|
|
2959
|
-
const { config, workspaceRoot, appRoot,
|
|
2960
|
-
const constructGlobs =
|
|
2734
|
+
const { config, workspaceRoot, appRoot, target, enableOpenApi, cacheBackend, production, telescope, studio, hooks, bustCache = false } = input;
|
|
2735
|
+
const constructGlobs = config.constructs;
|
|
2961
2736
|
const constructSources = {};
|
|
2962
2737
|
const declared = constructGlobs ? await discover({
|
|
2963
2738
|
patterns: constructGlobs,
|
|
@@ -3023,50 +2798,58 @@ async function buildApp(input) {
|
|
|
3023
2798
|
logger$8.log("No endpoints, functions, crons, subscribers, queues, or topics found to process");
|
|
3024
2799
|
return {};
|
|
3025
2800
|
}
|
|
3026
|
-
const
|
|
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
|
-
}
|
|
2801
|
+
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
2802
|
await generateOpenApi(allEndpoints.map(({ construct }) => construct), { openapi: config.openapi });
|
|
3034
2803
|
return result;
|
|
3035
2804
|
}
|
|
3036
|
-
async function
|
|
3037
|
-
const
|
|
3038
|
-
|
|
2805
|
+
async function buildForTarget(target, context, appRoot, workspaceRoot, endpointGenerator, functionGenerator, cronGenerator, subscriberGenerator, queueGenerator, topicGenerator, endpoints, functions, crons, subscribers, queues, topics, enableOpenApi, skipBundle, stage) {
|
|
2806
|
+
const outputDir = join(appRoot, ".gkm", target);
|
|
2807
|
+
await Promise.all([...SUPERSEDED_OUTPUT.map((dir) => join(appRoot, ".gkm", dir)), ...appRoot === workspaceRoot ? [] : [join(appRoot, ".gkm", "manifest")]].map((dir) => rm(dir, {
|
|
2808
|
+
recursive: true,
|
|
2809
|
+
force: true
|
|
2810
|
+
})));
|
|
2811
|
+
if (target === "aws") await rm(outputDir, {
|
|
2812
|
+
recursive: true,
|
|
2813
|
+
force: true
|
|
2814
|
+
});
|
|
3039
2815
|
await mkdir(outputDir, { recursive: true });
|
|
3040
|
-
logger$8.log(`\nGenerating handlers for
|
|
2816
|
+
logger$8.log(`\nGenerating handlers for ${target}`);
|
|
2817
|
+
const options = {
|
|
2818
|
+
target,
|
|
2819
|
+
root: workspaceRoot
|
|
2820
|
+
};
|
|
3041
2821
|
const [routes, functionInfos, cronInfos, subscriberInfos, queueInfos, topicInfos] = await Promise.all([
|
|
3042
2822
|
endpointGenerator.build(context, endpoints, outputDir, {
|
|
3043
|
-
|
|
2823
|
+
...options,
|
|
3044
2824
|
enableOpenApi
|
|
3045
2825
|
}),
|
|
3046
|
-
functionGenerator.build(context, functions, outputDir,
|
|
3047
|
-
cronGenerator.build(context, crons, outputDir,
|
|
3048
|
-
subscriberGenerator.build(context, subscribers, outputDir,
|
|
3049
|
-
queueGenerator.build(context, queues, outputDir,
|
|
3050
|
-
topicGenerator.build(context, topics, outputDir,
|
|
2826
|
+
functionGenerator.build(context, functions, outputDir, options),
|
|
2827
|
+
cronGenerator.build(context, crons, outputDir, options),
|
|
2828
|
+
subscriberGenerator.build(context, subscribers, outputDir, options),
|
|
2829
|
+
queueGenerator.build(context, queues, outputDir, options),
|
|
2830
|
+
topicGenerator.build(context, topics, outputDir, options)
|
|
3051
2831
|
]);
|
|
3052
|
-
logger$8.log(`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers, ${queueInfos.length} queues, ${topicInfos.length} topics for ${
|
|
3053
|
-
const
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
2832
|
+
logger$8.log(`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers, ${queueInfos.length} queues, ${topicInfos.length} topics for ${target}`);
|
|
2833
|
+
const fields = {
|
|
2834
|
+
...context.surface ? { surface: context.surface.id } : {},
|
|
2835
|
+
functions: functionInfos,
|
|
2836
|
+
crons: cronInfos,
|
|
2837
|
+
queues: queueInfos,
|
|
2838
|
+
subscribers: subscriberInfos
|
|
2839
|
+
};
|
|
2840
|
+
if (target === "server") {
|
|
2841
|
+
const handler = relative(workspaceRoot, join(outputDir, "app.ts"));
|
|
2842
|
+
const built = {
|
|
2843
|
+
...fields,
|
|
2844
|
+
routes: await Promise.all(endpoints.map(async ({ construct }) => ({
|
|
2845
|
+
path: construct._path,
|
|
2846
|
+
method: construct.method,
|
|
2847
|
+
handler,
|
|
2848
|
+
environment: await construct.getEnvironment({ markOptional: context.markOptional }),
|
|
2849
|
+
dependencies: construct.constructs,
|
|
2850
|
+
authorizer: construct.authorizer?.name ?? "none"
|
|
2851
|
+
})))
|
|
2852
|
+
};
|
|
3070
2853
|
let masterKey;
|
|
3071
2854
|
if (context.production?.bundle && !skipBundle) {
|
|
3072
2855
|
logger$8.log(`\n📦 Bundling production server...`);
|
|
@@ -3093,12 +2876,15 @@ async function buildForProvider(provider, context, appRoot, discovered, endpoint
|
|
|
3093
2876
|
logger$8.log(` Deploy with: GKM_MASTER_KEY=${masterKey}`);
|
|
3094
2877
|
}
|
|
3095
2878
|
}
|
|
3096
|
-
return {
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
}
|
|
3101
|
-
return {
|
|
2879
|
+
return {
|
|
2880
|
+
masterKey,
|
|
2881
|
+
built
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
return { built: {
|
|
2885
|
+
...fields,
|
|
2886
|
+
routes
|
|
2887
|
+
} };
|
|
3102
2888
|
}
|
|
3103
2889
|
/**
|
|
3104
2890
|
* Detect available package manager.
|
|
@@ -3120,8 +2906,8 @@ function detectPackageManager$2() {
|
|
|
3120
2906
|
*
|
|
3121
2907
|
* @internal Exported for testing
|
|
3122
2908
|
*/
|
|
3123
|
-
function getTurboCommand(pm, filters = []) {
|
|
3124
|
-
const filterArgs = (typeof filters === "string" ? [filters] : filters).map((f) => ` --filter=${f}`).join("");
|
|
2909
|
+
function getTurboCommand(pm, filters = [], { only = false } = {}) {
|
|
2910
|
+
const filterArgs = (typeof filters === "string" ? [filters] : filters).map((f) => ` --filter=${f}`).join("") + (only ? " --only" : "");
|
|
3125
2911
|
switch (pm) {
|
|
3126
2912
|
case "pnpm": return `pnpm exec turbo run build${filterArgs}`;
|
|
3127
2913
|
case "yarn": return `yarn turbo run build${filterArgs}`;
|
|
@@ -3141,10 +2927,11 @@ function getTurboCommand(pm, filters = []) {
|
|
|
3141
2927
|
*
|
|
3142
2928
|
* @internal Exported for testing
|
|
3143
2929
|
*/
|
|
3144
|
-
function turboFilters(workspace) {
|
|
2930
|
+
function turboFilters(workspace, { exclude } = {}) {
|
|
3145
2931
|
const filters = [];
|
|
3146
2932
|
const unpackaged = [];
|
|
3147
|
-
for (const appName of Object.
|
|
2933
|
+
for (const [appName, app] of Object.entries(workspace.apps)) {
|
|
2934
|
+
if (app.type === exclude) continue;
|
|
3148
2935
|
const name = appPackageName(workspace, appName);
|
|
3149
2936
|
if (name) filters.push(name);
|
|
3150
2937
|
else unpackaged.push(appName);
|
|
@@ -3183,31 +2970,59 @@ async function workspaceBuildCommand(workspace, options) {
|
|
|
3183
2970
|
if (options.production) logger$8.log(` 🏭 Production mode enabled`);
|
|
3184
2971
|
const buildOrder = getAppBuildOrder(workspace);
|
|
3185
2972
|
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
2973
|
try {
|
|
3189
|
-
const
|
|
3190
|
-
|
|
3191
|
-
const
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
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);
|
|
2974
|
+
const target = options.provider ?? providerOf(workspace);
|
|
2975
|
+
const builds = [];
|
|
2976
|
+
for (const appName of buildOrder) {
|
|
2977
|
+
const app = workspace.apps[appName];
|
|
2978
|
+
const config = getAppGkmConfig(workspace, appName);
|
|
2979
|
+
if (!app || app.type !== "backend" || !config) continue;
|
|
2980
|
+
logger$8.log(`\n⚙️ ${appName}`);
|
|
2981
|
+
const output = await buildOneApp({
|
|
2982
|
+
config,
|
|
2983
|
+
options,
|
|
2984
|
+
workspace,
|
|
2985
|
+
appRoot: join(workspace.root, app.path),
|
|
2986
|
+
target
|
|
3209
2987
|
});
|
|
2988
|
+
if (output.built) builds.push(output.built);
|
|
2989
|
+
}
|
|
2990
|
+
await writeManifest({
|
|
2991
|
+
workspaceRoot: workspace.root,
|
|
2992
|
+
target,
|
|
2993
|
+
builds,
|
|
2994
|
+
constructs: await discover({
|
|
2995
|
+
patterns: allConstructGlobs(workspace),
|
|
2996
|
+
cwd: workspace.root
|
|
2997
|
+
}),
|
|
2998
|
+
backends: backendsOf(workspace)
|
|
3210
2999
|
});
|
|
3000
|
+
const { filters, unpackaged } = turboFilters(workspace, { exclude: "backend" });
|
|
3001
|
+
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.`);
|
|
3002
|
+
const pm = detectPackageManager$2();
|
|
3003
|
+
const turboCommand = getTurboCommand(pm, filters, { only: true });
|
|
3004
|
+
if (filters.length > 0) {
|
|
3005
|
+
logger$8.log(`\n📦 Using ${pm} with Turbo for the other apps...\n`);
|
|
3006
|
+
logger$8.log(`Running: ${turboCommand}`);
|
|
3007
|
+
await new Promise((resolve, reject) => {
|
|
3008
|
+
const child = spawn(turboCommand, {
|
|
3009
|
+
shell: true,
|
|
3010
|
+
cwd: workspace.root,
|
|
3011
|
+
stdio: "inherit",
|
|
3012
|
+
env: {
|
|
3013
|
+
...process.env,
|
|
3014
|
+
NODE_ENV: options.production ? "production" : "development"
|
|
3015
|
+
}
|
|
3016
|
+
});
|
|
3017
|
+
child.on("close", (code) => {
|
|
3018
|
+
if (code === 0) resolve();
|
|
3019
|
+
else reject(/* @__PURE__ */ new Error(`Turbo build failed with exit code ${code}`));
|
|
3020
|
+
});
|
|
3021
|
+
child.on("error", (err) => {
|
|
3022
|
+
reject(err);
|
|
3023
|
+
});
|
|
3024
|
+
});
|
|
3025
|
+
}
|
|
3211
3026
|
for (const [appName, app] of apps) {
|
|
3212
3027
|
const outputPath = getAppOutputPath(workspace, appName, app);
|
|
3213
3028
|
results.push({
|
|
@@ -3250,23 +3065,11 @@ function getAppOutputPath(workspace, _appName, app) {
|
|
|
3250
3065
|
}
|
|
3251
3066
|
return join(appPath, ".gkm");
|
|
3252
3067
|
}
|
|
3253
|
-
/**
|
|
3254
|
-
* Format routes for logging, handling PartitionedRoutes.
|
|
3255
|
-
*/
|
|
3068
|
+
/** Format routes for logging. */
|
|
3256
3069
|
function formatRoutes(routes) {
|
|
3257
|
-
if (isPartitionedRoutes(routes)) return `${Array.isArray(routes.paths) ? routes.paths.join(", ") : routes.paths} (partitioned)`;
|
|
3258
3070
|
return Array.isArray(routes) ? routes.join(", ") : routes;
|
|
3259
3071
|
}
|
|
3260
3072
|
/**
|
|
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
3073
|
* Format a fatal error for display.
|
|
3271
3074
|
* Always includes the full stack trace since these are process-ending errors.
|
|
3272
3075
|
*/
|
|
@@ -3569,6 +3372,19 @@ const PROVISIONERS = {
|
|
|
3569
3372
|
return { provides: { [key]: supplied(declaration.id, key, context) } };
|
|
3570
3373
|
},
|
|
3571
3374
|
/**
|
|
3375
|
+
* The keyring the stage keeps in its secrets.
|
|
3376
|
+
*
|
|
3377
|
+
* Generated on the first deploy (`withGeneratedSecrets`) and rotated only
|
|
3378
|
+
* by `gkm encryption:rotate`, so a redeploy never changes a key and nothing
|
|
3379
|
+
* written becomes unreadable. Nothing is created in Dokploy: the process
|
|
3380
|
+
* holds the keyring.
|
|
3381
|
+
*/
|
|
3382
|
+
encryption: async (declaration, context) => {
|
|
3383
|
+
if (declaration.kind !== "encryption") throw new WrongKind(declaration.kind);
|
|
3384
|
+
const key = provideKey(declaration.id, "url");
|
|
3385
|
+
return { provides: { [key]: supplied(declaration.id, key, context) } };
|
|
3386
|
+
},
|
|
3387
|
+
/**
|
|
3572
3388
|
* A third party's credentials, as the stage was given them.
|
|
3573
3389
|
*
|
|
3574
3390
|
* Nothing is created and nothing derived: the value was issued by somebody
|
|
@@ -5412,9 +5228,8 @@ async function dockerCommand(options) {
|
|
|
5412
5228
|
logger$6.log("📦 Detected workspace configuration");
|
|
5413
5229
|
return workspaceDockerCommand(loadedConfig.workspace);
|
|
5414
5230
|
}
|
|
5415
|
-
const
|
|
5416
|
-
const
|
|
5417
|
-
const healthCheckPath = (typeof config.providers?.server === "object" ? config.providers.server : void 0)?.production?.healthCheck ?? "/health";
|
|
5231
|
+
const dockerConfig = resolveDockerConfig(await loadConfig());
|
|
5232
|
+
const healthCheckPath = "/health";
|
|
5418
5233
|
const useSlim = options.slim === true;
|
|
5419
5234
|
if (useSlim) {
|
|
5420
5235
|
const distDir = join(process.cwd(), ".gkm", "server", "dist");
|
|
@@ -6573,6 +6388,8 @@ function validateEnvVars(requiredVars, context) {
|
|
|
6573
6388
|
* who could read the repo.
|
|
6574
6389
|
* - **Each `secret` construct's value** — an auth server's signing secret —
|
|
6575
6390
|
* random, rather than derived.
|
|
6391
|
+
* - **Each `encryption` construct's keyring** — one random key and an index
|
|
6392
|
+
* key. Later keys are added by `gkm encryption:rotate`, never by a deploy.
|
|
6576
6393
|
*
|
|
6577
6394
|
* Both are generated on the first deploy that needs them and written to the
|
|
6578
6395
|
* stage's store, so every later deploy reads the same values: sessions survive
|
|
@@ -6588,10 +6405,10 @@ function withGeneratedSecrets(secrets, manifest) {
|
|
|
6588
6405
|
const generated = [];
|
|
6589
6406
|
const custom = { ...secrets.custom };
|
|
6590
6407
|
for (const declaration of Object.values(manifest)) {
|
|
6591
|
-
if (declaration?.kind !== "secret") continue;
|
|
6408
|
+
if (declaration?.kind !== "secret" && declaration?.kind !== "encryption") continue;
|
|
6592
6409
|
const key = declaration.provides?.[0];
|
|
6593
6410
|
if (!key || custom[key]) continue;
|
|
6594
|
-
custom[key] = random();
|
|
6411
|
+
custom[key] = declaration.kind === "encryption" ? generateKeyring() : random();
|
|
6595
6412
|
generated.push(key);
|
|
6596
6413
|
}
|
|
6597
6414
|
const seed = secrets.seed ?? random();
|
|
@@ -7420,6 +7237,10 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
7420
7237
|
} else logger$4.log(` Deploying all apps: ${appsToDeployNames.join(", ")}`);
|
|
7421
7238
|
const dokployApps = appsToDeployNames.filter((name) => {
|
|
7422
7239
|
const target = workspace.apps[name].resolvedDeployTarget;
|
|
7240
|
+
if (target === "sst") {
|
|
7241
|
+
logger$4.log(` ⚠️ Skipping ${name}: it deploys with SST — run \`gkm build && sst deploy --stage ${stage}\``);
|
|
7242
|
+
return false;
|
|
7243
|
+
}
|
|
7423
7244
|
if (!isDeployTargetSupported(target)) {
|
|
7424
7245
|
logger$4.log(` ⚠️ Skipping ${name}: ${getDeployTargetError(target, name)}`);
|
|
7425
7246
|
return false;
|
|
@@ -7940,66 +7761,6 @@ async function createApi(endpoint) {
|
|
|
7940
7761
|
});
|
|
7941
7762
|
}
|
|
7942
7763
|
/**
|
|
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
7764
|
* Initialize Dokploy deployment configuration
|
|
8004
7765
|
*/
|
|
8005
7766
|
async function deployInitCommand(options) {
|
|
@@ -8052,7 +7813,6 @@ async function deployInitCommand(options) {
|
|
|
8052
7813
|
projectId,
|
|
8053
7814
|
applicationId: application.applicationId
|
|
8054
7815
|
};
|
|
8055
|
-
await updateConfig(config);
|
|
8056
7816
|
logger$3.log(`\n✅ Dokploy deployment initialized!`);
|
|
8057
7817
|
logger$3.log(`\n📋 Configuration:`);
|
|
8058
7818
|
logger$3.log(` Project ID: ${projectId}`);
|
|
@@ -9322,26 +9082,26 @@ function loadPackageJson() {
|
|
|
9322
9082
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
9323
9083
|
*/
|
|
9324
9084
|
const GEEKMIDAS_VERSIONS = {
|
|
9325
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
9326
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
9327
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
9328
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
9329
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
9330
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
9331
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
9332
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
9333
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
9334
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
9335
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
9336
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
9337
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
9338
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
9339
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
9340
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
9341
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
9342
|
-
"@geekmidas/studio": "~10.0.0-alpha.
|
|
9343
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
9344
|
-
"@geekmidas/testkit": "~10.0.0-alpha.
|
|
9085
|
+
"@geekmidas/audit": "~10.0.0-alpha.47",
|
|
9086
|
+
"@geekmidas/auth": "~10.0.0-alpha.47",
|
|
9087
|
+
"@geekmidas/cache": "~10.0.0-alpha.47",
|
|
9088
|
+
"@geekmidas/client": "~10.0.0-alpha.47",
|
|
9089
|
+
"@geekmidas/cloud": "~10.0.0-alpha.47",
|
|
9090
|
+
"@geekmidas/constructs": "~10.0.0-alpha.47",
|
|
9091
|
+
"@geekmidas/db": "~10.0.0-alpha.47",
|
|
9092
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.47",
|
|
9093
|
+
"@geekmidas/envkit": "~10.0.0-alpha.47",
|
|
9094
|
+
"@geekmidas/errors": "~10.0.0-alpha.47",
|
|
9095
|
+
"@geekmidas/events": "~10.0.0-alpha.47",
|
|
9096
|
+
"@geekmidas/logger": "~10.0.0-alpha.47",
|
|
9097
|
+
"@geekmidas/manifest": "~10.0.0-alpha.47",
|
|
9098
|
+
"@geekmidas/rate-limit": "~10.0.0-alpha.47",
|
|
9099
|
+
"@geekmidas/schema": "~10.0.0-alpha.47",
|
|
9100
|
+
"@geekmidas/services": "~10.0.0-alpha.47",
|
|
9101
|
+
"@geekmidas/storage": "~10.0.0-alpha.47",
|
|
9102
|
+
"@geekmidas/studio": "~10.0.0-alpha.47",
|
|
9103
|
+
"@geekmidas/telescope": "~10.0.0-alpha.47",
|
|
9104
|
+
"@geekmidas/testkit": "~10.0.0-alpha.47",
|
|
9345
9105
|
"@geekmidas/cli": `~${loadPackageJson().version}`
|
|
9346
9106
|
};
|
|
9347
9107
|
//#endregion
|
|
@@ -10868,7 +10628,7 @@ function deployPackage(options) {
|
|
|
10868
10628
|
case "sst": {
|
|
10869
10629
|
const v = GEEKMIDAS_VERSIONS;
|
|
10870
10630
|
return {
|
|
10871
|
-
scripts: perStage(options, (stage) => `gkm build
|
|
10631
|
+
scripts: perStage(options, (stage) => `gkm build && sst deploy --stage ${stage}`),
|
|
10872
10632
|
dependencies: {
|
|
10873
10633
|
"@geekmidas/cloud": v["@geekmidas/cloud"],
|
|
10874
10634
|
"@geekmidas/db": v["@geekmidas/db"],
|
|
@@ -10933,7 +10693,7 @@ export default $config({
|
|
|
10933
10693
|
|
|
10934
10694
|
async run() {
|
|
10935
10695
|
const { App, fromManifest, Stack } = await import('@geekmidas/cloud/sst');
|
|
10936
|
-
// Written by \`gkm build
|
|
10696
|
+
// Written by \`gkm build\`. Imported rather than discovered,
|
|
10937
10697
|
// so SST's toolchain never evaluates the application's own modules.
|
|
10938
10698
|
const { backends, constructs } = await import('./.gkm/manifest/aws.js');
|
|
10939
10699
|
|
|
@@ -11171,8 +10931,11 @@ coverage/
|
|
|
11171
10931
|
${isSst ? "\n# SST\n.sst/\n" : ""}`;
|
|
11172
10932
|
const tsConfig = {
|
|
11173
10933
|
compilerOptions: {
|
|
10934
|
+
paths: {
|
|
10935
|
+
...isFullstack ? { [`@${options.name}/constructs/*`]: ["./constructs/*"] } : {},
|
|
10936
|
+
[`@${options.name}/manifest`]: [`./.gkm/manifest/${isSst ? "aws" : "server"}.ts`]
|
|
10937
|
+
},
|
|
11174
10938
|
...isFullstack ? {
|
|
11175
|
-
paths: { [`@${options.name}/constructs/*`]: ["./constructs/*"] },
|
|
11176
10939
|
allowImportingTsExtensions: true,
|
|
11177
10940
|
noEmit: true
|
|
11178
10941
|
} : {},
|
|
@@ -11313,7 +11076,7 @@ export default defineWorkspace({
|
|
|
11313
11076
|
// \`production-${options.name}-database\` on Dokploy and on AWS alike.
|
|
11314
11077
|
name: '${options.name}',
|
|
11315
11078
|
${stagesBlock(options.stages)}
|
|
11316
|
-
|
|
11079
|
+
${deployBlock(options)}
|
|
11317
11080
|
// Every kind, in every app. A declared database is why a Postgres exists, a
|
|
11318
11081
|
// declared bucket is why MinIO does, a declared topic is why a broker does —
|
|
11319
11082
|
// none of it listed here. It is also where the apps come from: a
|
|
@@ -11331,6 +11094,18 @@ ${workspaceConstructsGlobs(options.routesStructure, dirname(options.apiPath)).ma
|
|
|
11331
11094
|
`;
|
|
11332
11095
|
}
|
|
11333
11096
|
/**
|
|
11097
|
+
* Where it deploys, when that is not the default Dokploy: what a bare
|
|
11098
|
+
* `gkm build` builds for, and which backends a cache or a broker resolves to.
|
|
11099
|
+
*/
|
|
11100
|
+
function deployBlock(options) {
|
|
11101
|
+
if (options.deployTarget !== "sst") return "";
|
|
11102
|
+
return `
|
|
11103
|
+
// Deployed with \`sst deploy\`, from the manifest \`gkm build\` writes to
|
|
11104
|
+
// \`.gkm/manifest/aws.ts\`. Each construct becomes a managed AWS resource.
|
|
11105
|
+
deploy: { default: 'sst' },
|
|
11106
|
+
`;
|
|
11107
|
+
}
|
|
11108
|
+
/**
|
|
11334
11109
|
* Where a deployed stage's secrets live. On AWS that is SSM in the stage's own
|
|
11335
11110
|
* account, which the deploy job reaches with the role it already assumed; a
|
|
11336
11111
|
* server target keeps the encrypted local file, the default.
|
|
@@ -11356,7 +11131,7 @@ function secretsStore(options) {
|
|
|
11356
11131
|
*/
|
|
11357
11132
|
function generateRootConstructs(options) {
|
|
11358
11133
|
if (!options.monorepo || options.template !== "fullstack") return [];
|
|
11359
|
-
const {
|
|
11134
|
+
const { frontendFramework } = options;
|
|
11360
11135
|
const { cache, uploads, mail } = options.constructs;
|
|
11361
11136
|
const db = databaseFor();
|
|
11362
11137
|
const files = [];
|
|
@@ -12116,7 +11891,7 @@ const serverlessTemplate = {
|
|
|
12116
11891
|
},
|
|
12117
11892
|
scripts: {
|
|
12118
11893
|
dev: "gkm dev",
|
|
12119
|
-
build: "gkm build --provider aws
|
|
11894
|
+
build: "gkm build --provider aws",
|
|
12120
11895
|
test: "vitest",
|
|
12121
11896
|
"test:once": "vitest run",
|
|
12122
11897
|
typecheck: "tsc --noEmit",
|
|
@@ -12125,7 +11900,7 @@ const serverlessTemplate = {
|
|
|
12125
11900
|
"fmt:check": "biome format ."
|
|
12126
11901
|
},
|
|
12127
11902
|
files: (options) => {
|
|
12128
|
-
const { loggerType, routesStructure
|
|
11903
|
+
const { loggerType, routesStructure } = options;
|
|
12129
11904
|
const loggerContent = `import { createLogger } from '@geekmidas/logger/${loggerType}';
|
|
12130
11905
|
|
|
12131
11906
|
export const logger = createLogger();
|
|
@@ -12410,10 +12185,6 @@ export const telescope = new Telescope({
|
|
|
12410
12185
|
*/
|
|
12411
12186
|
const OPENAPI_OUTPUT_PATH = "./.gkm/openapi/api.ts";
|
|
12412
12187
|
/**
|
|
12413
|
-
* Endpoints output path (generated by gkm dev/build)
|
|
12414
|
-
*/
|
|
12415
|
-
const ENDPOINTS_OUTPUT_PATH = "./.gkm/server/endpoints.ts";
|
|
12416
|
-
/**
|
|
12417
12188
|
* All available templates
|
|
12418
12189
|
*/
|
|
12419
12190
|
const templates = {
|
|
@@ -12626,10 +12397,7 @@ function generatePackageJson(options, template) {
|
|
|
12626
12397
|
private: true,
|
|
12627
12398
|
type: "module",
|
|
12628
12399
|
...!monorepo && options.packageManager === "pnpm" ? { packageManager: PNPM_VERSION } : {},
|
|
12629
|
-
exports: {
|
|
12630
|
-
"./client": OPENAPI_OUTPUT_PATH,
|
|
12631
|
-
"./endpoints": ENDPOINTS_OUTPUT_PATH
|
|
12632
|
-
},
|
|
12400
|
+
exports: { "./client": OPENAPI_OUTPUT_PATH },
|
|
12633
12401
|
scripts,
|
|
12634
12402
|
dependencies: sortObject(dependencies),
|
|
12635
12403
|
devDependencies: sortObject(devDependencies)
|
|
@@ -15355,7 +15123,7 @@ function printNextSteps(projectName, options, pkgManager) {
|
|
|
15355
15123
|
}
|
|
15356
15124
|
if (options.deployTarget === "sst") {
|
|
15357
15125
|
console.log("🚀 Deployment (AWS, through SST):");
|
|
15358
|
-
for (const stage of deployed) console.log(` ${getRunCommand(pkgManager, `deploy:${stage}`)} # gkm build
|
|
15126
|
+
for (const stage of deployed) console.log(` ${getRunCommand(pkgManager, `deploy:${stage}`)} # gkm build, then sst deploy`);
|
|
15359
15127
|
console.log(` Uses your AWS credentials, in ${options.region}.`);
|
|
15360
15128
|
if (options.constructs.mail) console.log(" Set MAIL_FROM to a sender verified in SES first.");
|
|
15361
15129
|
console.log("");
|
|
@@ -15742,8 +15510,8 @@ async function reconcileLocal(workspace, stage, options) {
|
|
|
15742
15510
|
logger$1.log("🐳 Services already up to date");
|
|
15743
15511
|
return;
|
|
15744
15512
|
}
|
|
15745
|
-
logger$1.log(
|
|
15746
|
-
for (const
|
|
15513
|
+
logger$1.log("🐳 Services");
|
|
15514
|
+
for (const line of describeServices(result.services)) logger$1.log(line);
|
|
15747
15515
|
const url = result.plan.resources.filter((r) => r.kind === "rest-api" || r.kind === "site" || r.kind === "file-server").map((r) => result.env[r.envKey]).find((address) => address?.startsWith("https://"));
|
|
15748
15516
|
if (url) await ensureTrusted(workspace.root, url, { ...options.yes ? { assumeYes: true } : {} });
|
|
15749
15517
|
}
|
|
@@ -16015,7 +15783,7 @@ async function testCommand(options = {}) {
|
|
|
16015
15783
|
root: workspace.root,
|
|
16016
15784
|
targets: [workspace.root, ...Object.values(workspace.apps).map((app) => isAbsolute(app.path) ? app.path : join(workspace.root, app.path))],
|
|
16017
15785
|
patterns: constructGlobs(workspace),
|
|
16018
|
-
cacheBackend: backendsOf(workspace).cache,
|
|
15786
|
+
cacheBackend: backendsOf$1(workspace).cache,
|
|
16019
15787
|
stage: TEST_STAGE,
|
|
16020
15788
|
env: finalCredentials,
|
|
16021
15789
|
factories: join(workspace.root, workspace.test.factories ?? "test/factories")
|
|
@@ -16090,8 +15858,8 @@ function testDatabases(reconciled) {
|
|
|
16090
15858
|
* starts without the reference data the app needs.
|
|
16091
15859
|
*/
|
|
16092
15860
|
async function migrateTestStage(workspace, env) {
|
|
16093
|
-
const { migrateAndSeed } = await import("./migrate-
|
|
16094
|
-
const { discover } = await import("./discover-
|
|
15861
|
+
const { migrateAndSeed } = await import("./migrate-DhxTIA0z.mjs");
|
|
15862
|
+
const { discover } = await import("./discover-Cf2jAcgF.mjs").then((n) => n.n);
|
|
16095
15863
|
const sources = {};
|
|
16096
15864
|
const manifest = await discover({
|
|
16097
15865
|
patterns: constructGlobs(workspace),
|
|
@@ -16456,29 +16224,13 @@ program.command("setup").description("Reconcile declared constructs — containe
|
|
|
16456
16224
|
process.exit(1);
|
|
16457
16225
|
}
|
|
16458
16226
|
});
|
|
16459
|
-
program.command("build").description("Build handlers and the manifest from declared constructs").option("--provider <provider>", "
|
|
16227
|
+
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
16228
|
try {
|
|
16461
16229
|
const globalOptions = program.opts();
|
|
16462
16230
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16463
|
-
if (options.provider)
|
|
16464
|
-
|
|
16465
|
-
|
|
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({
|
|
16231
|
+
if (options.provider !== void 0 && !isMainProvider(options.provider)) throw new UnknownBuildProvider(options.provider);
|
|
16232
|
+
await buildCommand({
|
|
16233
|
+
...options.provider ? { provider: options.provider } : {},
|
|
16482
16234
|
enableOpenApi: options.enableOpenapi || false,
|
|
16483
16235
|
production: options.production || false,
|
|
16484
16236
|
skipBundle: options.skipBundle || false,
|
|
@@ -16537,7 +16289,7 @@ program.command("migrate").description("Apply each database construct's migratio
|
|
|
16537
16289
|
try {
|
|
16538
16290
|
const globalOptions = program.opts();
|
|
16539
16291
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16540
|
-
const { migrateCommand } = await import("./migrate-
|
|
16292
|
+
const { migrateCommand } = await import("./migrate-DhxTIA0z.mjs");
|
|
16541
16293
|
await migrateCommand({
|
|
16542
16294
|
...options,
|
|
16543
16295
|
...construct ? { construct } : {}
|
|
@@ -16551,7 +16303,7 @@ program.command("seed").description("Migrate, then run each database construct's
|
|
|
16551
16303
|
try {
|
|
16552
16304
|
const globalOptions = program.opts();
|
|
16553
16305
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16554
|
-
const { seedCommand } = await import("./migrate-
|
|
16306
|
+
const { seedCommand } = await import("./migrate-DhxTIA0z.mjs");
|
|
16555
16307
|
await seedCommand({
|
|
16556
16308
|
...options,
|
|
16557
16309
|
...construct ? { construct } : {}
|
|
@@ -16565,7 +16317,7 @@ program.command("migration").description("Write a construct's next migration: an
|
|
|
16565
16317
|
try {
|
|
16566
16318
|
const globalOptions = program.opts();
|
|
16567
16319
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16568
|
-
const { migrationCommand } = await import("./migrate-
|
|
16320
|
+
const { migrationCommand } = await import("./migrate-DhxTIA0z.mjs");
|
|
16569
16321
|
await migrationCommand({
|
|
16570
16322
|
construct,
|
|
16571
16323
|
...name ? { name } : {}
|
|
@@ -16659,6 +16411,28 @@ program.command("secrets:set").description("Set a custom secret for a stage").ar
|
|
|
16659
16411
|
process.exit(1);
|
|
16660
16412
|
}
|
|
16661
16413
|
});
|
|
16414
|
+
program.command("encryption:rotate").description("Add a new current key to an Encryption construct's keyring on a server stage").argument("<construct>", "The Encryption construct (e.g. Pii)").requiredOption("--stage <stage>", "A deployed stage").action(async (construct, options) => {
|
|
16415
|
+
try {
|
|
16416
|
+
const globalOptions = program.opts();
|
|
16417
|
+
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16418
|
+
const { encryptionRotateCommand } = await import("./encryption-DyQnPsZN.mjs");
|
|
16419
|
+
await encryptionRotateCommand(construct, options);
|
|
16420
|
+
} catch (error) {
|
|
16421
|
+
console.error(formatError(error));
|
|
16422
|
+
process.exit(1);
|
|
16423
|
+
}
|
|
16424
|
+
});
|
|
16425
|
+
program.command("encryption:retire").description("Remove an old key from an Encryption construct's keyring, once nothing is under it").argument("<construct>", "The Encryption construct (e.g. Pii)").argument("<key>", "The key to retire (e.g. k1)").requiredOption("--stage <stage>", "A deployed stage").action(async (construct, key, options) => {
|
|
16426
|
+
try {
|
|
16427
|
+
const globalOptions = program.opts();
|
|
16428
|
+
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16429
|
+
const { encryptionRetireCommand } = await import("./encryption-DyQnPsZN.mjs");
|
|
16430
|
+
await encryptionRetireCommand(construct, key, options);
|
|
16431
|
+
} catch (error) {
|
|
16432
|
+
console.error(formatError(error));
|
|
16433
|
+
process.exit(1);
|
|
16434
|
+
}
|
|
16435
|
+
});
|
|
16662
16436
|
program.command("secrets:show").description("Show secrets for a stage").requiredOption("--stage <stage>", "Stage name").option("--reveal", "Show actual secret values (not masked)").action(async (options) => {
|
|
16663
16437
|
try {
|
|
16664
16438
|
const globalOptions = program.opts();
|
|
@@ -16704,7 +16478,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
16704
16478
|
console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
16705
16479
|
process.exit(1);
|
|
16706
16480
|
}
|
|
16707
|
-
const { derivedContainers } = await import("./workspace-
|
|
16481
|
+
const { derivedContainers } = await import("./workspace-cb6U29EL.mjs").then((n) => n.o);
|
|
16708
16482
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
16709
16483
|
if (!result) {
|
|
16710
16484
|
console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
|
|
@@ -16763,7 +16537,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
|
|
|
16763
16537
|
try {
|
|
16764
16538
|
const globalOptions = program.opts();
|
|
16765
16539
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16766
|
-
const { deployGithubCommand } = await import("./github-
|
|
16540
|
+
const { deployGithubCommand } = await import("./github-BfpadQWQ.mjs");
|
|
16767
16541
|
await deployGithubCommand(options);
|
|
16768
16542
|
} catch (error) {
|
|
16769
16543
|
console.error(formatError(error));
|