@geekmidas/cli 10.0.0-alpha.5 → 10.0.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{config-C21ocYNd.cjs → config-CNz5KaSR.cjs} +16 -45
- package/dist/config-CNz5KaSR.cjs.map +1 -0
- package/dist/{config-WRdsQYZK.mjs → config-CxP7P-qG.mjs} +17 -46
- package/dist/config-CxP7P-qG.mjs.map +1 -0
- package/dist/config.cjs +2 -2
- package/dist/config.d.cts +2 -2
- package/dist/config.d.mts +2 -2
- package/dist/config.mjs +2 -2
- package/dist/{credentials-BBIRJVp4.mjs → credentials-BK3Z93MX.mjs} +5 -5
- package/dist/credentials-BK3Z93MX.mjs.map +1 -0
- package/dist/{credentials-G1JwBoXH.cjs → credentials-DzsXZzkx.cjs} +5 -5
- package/dist/credentials-DzsXZzkx.cjs.map +1 -0
- package/dist/discover-CAlFgYec.mjs.map +1 -1
- package/dist/discover-DTmFeloL.cjs.map +1 -1
- package/dist/{index-BThUzEIp.d.mts → index-BGGdjueF.d.cts} +2 -2
- package/dist/index-BGGdjueF.d.cts.map +1 -0
- package/dist/{index-CHB4tSzX.d.cts → index-DEbaleny.d.mts} +2 -2
- package/dist/index-DEbaleny.d.mts.map +1 -0
- package/dist/index.cjs +307 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +307 -121
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-kkQbsRVj.mjs → openapi-Bw8DWFWz.mjs} +104 -50
- package/dist/openapi-Bw8DWFWz.mjs.map +1 -0
- package/dist/{openapi-CA0yJUTN.cjs → openapi-D-dx9Akj.cjs} +122 -50
- package/dist/openapi-D-dx9Akj.cjs.map +1 -0
- package/dist/openapi.cjs +5 -3
- package/dist/openapi.d.cts +46 -10
- package/dist/openapi.d.cts.map +1 -1
- package/dist/openapi.d.mts +46 -10
- package/dist/openapi.d.mts.map +1 -1
- package/dist/openapi.mjs +4 -4
- package/dist/reconcile/public.d.cts +1 -1
- package/dist/reconcile/public.d.mts +1 -1
- package/dist/setup-8IWPsRIN.cjs +11 -0
- package/dist/{setup-DXvRLTZN.mjs → setup-BWMhbW-p.mjs} +3 -3
- package/dist/{setup-DXvRLTZN.mjs.map → setup-BWMhbW-p.mjs.map} +1 -1
- package/dist/{setup-Cm2UnAdh.cjs → setup-D7L4BgBT.cjs} +3 -3
- package/dist/{setup-Cm2UnAdh.cjs.map → setup-D7L4BgBT.cjs.map} +1 -1
- package/dist/{setup-ZijS4qeC.mjs → setup-DGIPd2fh.mjs} +4 -4
- package/dist/{types-C-arnQ1r.d.mts → types-BLSNdrk0.d.cts} +5 -13
- package/dist/{types-C-arnQ1r.d.mts.map → types-BLSNdrk0.d.cts.map} +1 -1
- package/dist/{types-abJQuk7m.d.cts → types-C3POXhw3.d.mts} +2 -10
- package/dist/{types-abJQuk7m.d.cts.map → types-C3POXhw3.d.mts.map} +1 -1
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-DdJss8I6.cjs → workspace-CGaUAw5A.cjs} +3 -3
- package/dist/workspace-CnTqIlB3.mjs +8 -0
- package/dist/{workspace-BFMrAKqI.mjs → workspace-DIuZx-cc.mjs} +1 -17
- package/dist/{workspace-BFMrAKqI.mjs.map → workspace-DIuZx-cc.mjs.map} +1 -1
- package/dist/{workspace-BcucjwvA.cjs → workspace-LKiNyaDy.cjs} +1 -17
- package/dist/{workspace-BcucjwvA.cjs.map → workspace-LKiNyaDy.cjs.map} +1 -1
- package/package.json +11 -11
- package/dist/config-C21ocYNd.cjs.map +0 -1
- package/dist/config-WRdsQYZK.mjs.map +0 -1
- package/dist/credentials-BBIRJVp4.mjs.map +0 -1
- package/dist/credentials-G1JwBoXH.cjs.map +0 -1
- package/dist/index-BThUzEIp.d.mts.map +0 -1
- package/dist/index-CHB4tSzX.d.cts.map +0 -1
- package/dist/openapi-CA0yJUTN.cjs.map +0 -1
- package/dist/openapi-kkQbsRVj.mjs.map +0 -1
- package/dist/setup-B5_oHHUj.cjs +0 -11
- package/dist/workspace-k4l3rsFh.mjs +0 -8
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
import { __require } from "./chunk-Duj1WY3L.mjs";
|
|
3
3
|
import { ConstructGenerator, DEFAULT_CACHE, discover, isPartitionedRoutes } from "./discover-CAlFgYec.mjs";
|
|
4
|
-
import { derivedApps, getAppNameFromCwd, loadAppConfig, loadConfig, loadWorkspaceConfig, parseModuleConfig, resolveAppSpec } from "./config-
|
|
5
|
-
import { allConstructGlobs, getAppBuildOrder, getDependencyEnvVars, getDeployTargetError, getPublicEnvPrefix, isDeployTargetSupported, stripPublicPrefix } from "./workspace-
|
|
4
|
+
import { derivedApps, getAppNameFromCwd, loadAppConfig, loadConfig, loadWorkspaceConfig, parseModuleConfig, resolveAppSpec } from "./config-CxP7P-qG.mjs";
|
|
5
|
+
import { allConstructGlobs, getAppBuildOrder, getAppGkmConfig, getDependencyEnvVars, getDeployTargetError, getPublicEnvPrefix, isDeployTargetSupported, stripPublicPrefix } from "./workspace-DIuZx-cc.mjs";
|
|
6
6
|
import { getCredentialsPath, getDokployCredentials, getDokployRegistryId, getDokployToken, removeDokployCredentials, storeDokployCredentials, storeDokployRegistryId, storeHostingerToken } from "./credentials-B5zRHdAl.mjs";
|
|
7
|
-
import { applyPostgres, cacheBackendOf, constructGlobs, createCredentialsPreload, createEntryWrapper, derivedContainers, emailBackendOf, findAvailablePort, isPortAvailable, loadEnvFiles, loadSecretsForApp, prepareEntryCredentials, providerOf, reconcileWorkspace, resolveServicePorts, resourceName, rewriteUrlsWithPorts, startWorkspaceServices, storageBackendOf, usesConstructs } from "./credentials-
|
|
7
|
+
import { applyPostgres, cacheBackendOf, constructGlobs, createCredentialsPreload, createEntryWrapper, derivedContainers, emailBackendOf, findAvailablePort, isPortAvailable, loadEnvFiles, loadSecretsForApp, prepareEntryCredentials, providerOf, reconcileWorkspace, resolveServicePorts, resourceName, rewriteUrlsWithPorts, startWorkspaceServices, storageBackendOf, usesConstructs } from "./credentials-BK3Z93MX.mjs";
|
|
8
8
|
import { createStageSecrets, rotateServicePassword } from "./generator-VE2JrFlY.mjs";
|
|
9
9
|
import { getKeyPath, maskPassword, readStageSecrets, secretsExist, setCustomSecret, toEmbeddableSecrets, writeStageSecrets } from "./storage-Bi24WfoW.mjs";
|
|
10
|
-
import { EndpointGenerator, OPENAPI_OUTPUT_PATH, generateOpenApi, normalizeRoutes, openapiCommand, resolveOpenApiConfig } from "./openapi-
|
|
10
|
+
import { EndpointGenerator, OPENAPI_OUTPUT_PATH, generateOpenApi, generateOpenApiFrom, normalizeRoutes, openapiCommand, resolveOpenApiConfig, runtimeFor } from "./openapi-Bw8DWFWz.mjs";
|
|
11
11
|
import { withCompute, withRoutes } from "./emit-CiZS_5Ub.mjs";
|
|
12
12
|
import { DokployApi } from "./dokploy-api-aTFPz7_4.mjs";
|
|
13
13
|
import { encryptSecrets } from "./encryption-D-abyhZx.mjs";
|
|
@@ -15,7 +15,7 @@ import { CachedStateProvider } from "./CachedStateProvider-DTniehMP.mjs";
|
|
|
15
15
|
import { generateDbPassword, generateDbUrl, generateFullstackCustomSecrets } from "./fullstack-secrets-D1o9-G5l.mjs";
|
|
16
16
|
import { generateReactQueryCommand } from "./openapi-react-query-Byltvm_7.mjs";
|
|
17
17
|
import "./sync-CmEuNZsB.mjs";
|
|
18
|
-
import { setupCommand, trustCommand } from "./setup-
|
|
18
|
+
import { setupCommand, trustCommand } from "./setup-BWMhbW-p.mjs";
|
|
19
19
|
import { createRequire } from "node:module";
|
|
20
20
|
import { copyFileSync, existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
21
21
|
import { basename, dirname, isAbsolute, join, parse, relative, resolve } from "node:path";
|
|
@@ -43,7 +43,7 @@ import prompts from "prompts";
|
|
|
43
43
|
|
|
44
44
|
//#region package.json
|
|
45
45
|
var name = "@geekmidas/cli";
|
|
46
|
-
var version = "10.0.0-alpha.
|
|
46
|
+
var version = "10.0.0-alpha.5";
|
|
47
47
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
48
48
|
var private$1 = false;
|
|
49
49
|
var type = "module";
|
|
@@ -415,11 +415,16 @@ var CronGenerator = class extends ConstructGenerator {
|
|
|
415
415
|
const provider = options?.provider || "aws-lambda";
|
|
416
416
|
const logger$12 = console;
|
|
417
417
|
const cronInfos = [];
|
|
418
|
+
if (provider === "server") {
|
|
419
|
+
await this.generateServerCronsFile(outputDir, constructs);
|
|
420
|
+
logger$12.log(`Generated server crons file with ${constructs.length} crons`);
|
|
421
|
+
return cronInfos;
|
|
422
|
+
}
|
|
418
423
|
if (constructs.length === 0 || provider !== "aws-lambda") return cronInfos;
|
|
419
424
|
const cronsDir = join(outputDir, "crons");
|
|
420
425
|
await mkdir(cronsDir, { recursive: true });
|
|
421
426
|
for (const { key, construct, path } of constructs) {
|
|
422
|
-
const handlerFile = await this.generateCronHandler(cronsDir, path.relative, key, context);
|
|
427
|
+
const handlerFile = await this.generateCronHandler(cronsDir, path.relative, key, context, construct.owner);
|
|
423
428
|
cronInfos.push({
|
|
424
429
|
name: key,
|
|
425
430
|
handler: relative(process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
|
|
@@ -436,17 +441,173 @@ var CronGenerator = class extends ConstructGenerator {
|
|
|
436
441
|
isConstruct(value) {
|
|
437
442
|
return Cron.isCron(value);
|
|
438
443
|
}
|
|
439
|
-
|
|
444
|
+
/**
|
|
445
|
+
* The crons file a server entry imports.
|
|
446
|
+
*
|
|
447
|
+
* Shaped after `generateServerSubscribersFile`: one module, every cron, a
|
|
448
|
+
* `setupCrons` the generated entry calls beside `setupSubscribers`.
|
|
449
|
+
*
|
|
450
|
+
* It picks its scheduler from what the process can reach. With a Postgres it
|
|
451
|
+
* uses pg-boss, whose schedules live in the database — so a deployment
|
|
452
|
+
* running four replicas fires each job once, which is the thing a timer in
|
|
453
|
+
* every process gets wrong and does not report. With no database it falls
|
|
454
|
+
* back to timers and says plainly that it is doing so.
|
|
455
|
+
*/
|
|
456
|
+
async generateServerCronsFile(outputDir, crons) {
|
|
457
|
+
await mkdir(outputDir, { recursive: true });
|
|
458
|
+
const cronsPath = join(outputDir, "crons.ts");
|
|
459
|
+
const importsByFile = /* @__PURE__ */ new Map();
|
|
460
|
+
for (const { path, key } of crons) {
|
|
461
|
+
const relativePath = relative(dirname(cronsPath), path.relative);
|
|
462
|
+
const importPath = relativePath.replace(/\.ts$/, ".js");
|
|
463
|
+
if (!importsByFile.has(importPath)) importsByFile.set(importPath, []);
|
|
464
|
+
importsByFile.get(importPath)?.push(key);
|
|
465
|
+
}
|
|
466
|
+
const imports = Array.from(importsByFile.entries()).map(([importPath, exports$1]) => `import { ${exports$1.join(", ")} } from '${importPath}';`).join("\n");
|
|
467
|
+
const entries = crons.map(({ key }) => ` { name: '${key}', cron: ${key} }`).join(",\n");
|
|
468
|
+
const content = `/**
|
|
469
|
+
* Generated cron setup.
|
|
470
|
+
*
|
|
471
|
+
* Schedules every cron this app declares, in the process that serves it.
|
|
472
|
+
*
|
|
473
|
+
* The schedule lives in Postgres, through pg-boss, in the database the worker
|
|
474
|
+
* named with \`.database(db)\`. That is what makes running several replicas
|
|
475
|
+
* safe: each job fires once, where a timer in every process fires it once per
|
|
476
|
+
* replica and reports nothing.
|
|
477
|
+
*
|
|
478
|
+
* No connection string appears here. The construct that owns the database is
|
|
479
|
+
* the only thing that knows its key, and it is resolved through service
|
|
480
|
+
* discovery like any other dependency.
|
|
481
|
+
*/
|
|
482
|
+
import type { EnvironmentParser } from '@geekmidas/envkit';
|
|
483
|
+
import type { Logger } from '@geekmidas/logger';
|
|
484
|
+
import { toCronExpression } from '@geekmidas/constructs/crons';
|
|
485
|
+
import { ServiceDiscovery } from '@geekmidas/services';
|
|
486
|
+
${imports}
|
|
487
|
+
|
|
488
|
+
const crons = [
|
|
489
|
+
${entries}
|
|
490
|
+
];
|
|
491
|
+
|
|
492
|
+
type Stoppable = { stop: () => Promise<void> | void };
|
|
493
|
+
const running: Stoppable[] = [];
|
|
494
|
+
|
|
495
|
+
export async function setupCrons(
|
|
496
|
+
envParser: EnvironmentParser<any>,
|
|
497
|
+
logger: Logger,
|
|
498
|
+
): Promise<void> {
|
|
499
|
+
if (crons.length === 0) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const serviceDiscovery = ServiceDiscovery.getInstance(envParser);
|
|
504
|
+
|
|
505
|
+
// Resolved once per cron rather than per firing: registering services on
|
|
506
|
+
// every tick would reconnect a database every minute.
|
|
507
|
+
const prepared = [];
|
|
508
|
+
for (const { name, cron } of crons) {
|
|
509
|
+
try {
|
|
510
|
+
const schedule = toCronExpression(cron.schedule);
|
|
511
|
+
const services = cron.services.length > 0
|
|
512
|
+
? await serviceDiscovery.register(cron.services)
|
|
513
|
+
: {};
|
|
514
|
+
|
|
515
|
+
prepared.push({ name, cron, schedule, services });
|
|
516
|
+
} catch (error) {
|
|
517
|
+
// An unrepresentable schedule is a build-time mistake, and failing the
|
|
518
|
+
// process for it would take the HTTP server down with it.
|
|
519
|
+
logger.error({ error, cron: name }, 'Cron has no runnable schedule, skipping');
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const run = async (entry: (typeof prepared)[number]) => {
|
|
524
|
+
try {
|
|
525
|
+
await entry.cron.handler({
|
|
526
|
+
input: undefined,
|
|
527
|
+
services: entry.services,
|
|
528
|
+
logger: entry.cron.logger,
|
|
529
|
+
});
|
|
530
|
+
} catch (error) {
|
|
531
|
+
logger.error({ error, cron: entry.name }, 'Cron failed');
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// Declared once on the worker, carried onto every cron it built — so this
|
|
536
|
+
// reads it from any of them rather than from config or the environment.
|
|
537
|
+
const scheduleStore = prepared.find(({ cron }) => cron.scheduleStore)?.cron
|
|
538
|
+
.scheduleStore;
|
|
539
|
+
|
|
540
|
+
if (!scheduleStore) {
|
|
541
|
+
// Declared, not discovered. A worker says where its schedules live with
|
|
542
|
+
// \`.database(db)\`; inferring one from whatever database happened to be
|
|
543
|
+
// around would move the schedules the day an app declared a second.
|
|
544
|
+
logger.error(
|
|
545
|
+
{ crons: prepared.map(({ name }) => name) },
|
|
546
|
+
'These crons have nowhere to keep their schedule. A server fires its own ' +
|
|
547
|
+
'crons, and a process scheduling in memory fires each job once per ' +
|
|
548
|
+
'replica — so the schedule lives in Postgres. Say where with ' +
|
|
549
|
+
'\`new Worker(…).database(db)\`.',
|
|
550
|
+
);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const [store] = await serviceDiscovery
|
|
555
|
+
.register([scheduleStore])
|
|
556
|
+
.then((s) => Object.values(s));
|
|
557
|
+
|
|
558
|
+
const { PgBoss } = await import('pg-boss');
|
|
559
|
+
// The pool the construct already opened — no connection string is read here,
|
|
560
|
+
// and none is named anywhere outside the construct that owns it.
|
|
561
|
+
const boss = new PgBoss({ db: store as never });
|
|
562
|
+
await boss.start();
|
|
563
|
+
|
|
564
|
+
// What this app declares now. Anything else under the prefix belonged to a
|
|
565
|
+
// cron since renamed or deleted, and would keep firing at a handler that has
|
|
566
|
+
// gone.
|
|
567
|
+
//
|
|
568
|
+
// The prefix is \`cron.\` rather than \`cron:\`: pg-boss accepts
|
|
569
|
+
// alphanumerics, underscores, hyphens, periods and slashes in a queue name,
|
|
570
|
+
// and rejects a colon outright.
|
|
571
|
+
const declared = new Set(prepared.map(({ name }) => \`cron.\${name}\`));
|
|
572
|
+
for (const existing of await boss.getSchedules()) {
|
|
573
|
+
if (existing.name.startsWith('cron.') && !declared.has(existing.name)) {
|
|
574
|
+
await boss.unschedule(existing.name);
|
|
575
|
+
logger.info({ cron: existing.name }, 'Removed a schedule nothing declares');
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
for (const entry of prepared) {
|
|
580
|
+
const queue = \`cron.\${entry.name}\`;
|
|
581
|
+
await boss.createQueue(queue);
|
|
582
|
+
// UTC, so this agrees with the schedule a deploy target would have made.
|
|
583
|
+
await boss.schedule(queue, entry.schedule, undefined, { tz: 'UTC' });
|
|
584
|
+
await boss.work(queue, () => run(entry));
|
|
585
|
+
|
|
586
|
+
logger.info({ cron: entry.name, schedule: entry.schedule }, 'Cron scheduled');
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
running.push({ stop: () => boss.stop() });
|
|
590
|
+
logger.info({ count: prepared.length }, 'Crons scheduled');
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
export async function stopCrons(): Promise<void> {
|
|
594
|
+
await Promise.all(running.map((r) => r.stop()));
|
|
595
|
+
running.length = 0;
|
|
596
|
+
}
|
|
597
|
+
`;
|
|
598
|
+
await writeFile(cronsPath, content);
|
|
599
|
+
return cronsPath;
|
|
600
|
+
}
|
|
601
|
+
async generateCronHandler(outputDir, sourceFile, exportName, context, owner) {
|
|
440
602
|
const handlerFileName = `${exportName}.ts`;
|
|
441
603
|
const handlerPath = join(outputDir, handlerFileName);
|
|
442
604
|
const relativePath = relative(dirname(handlerPath), sourceFile);
|
|
443
605
|
const importPath = relativePath.replace(/\.ts$/, ".js");
|
|
444
|
-
const
|
|
445
|
-
const relativeLoggerPath = relative(dirname(handlerPath), context.loggerPath);
|
|
606
|
+
const runtime = runtimeFor(context, dirname(handlerPath), owner);
|
|
446
607
|
const content = `import { AWSScheduledFunction } from '@geekmidas/constructs/aws';
|
|
447
608
|
import { ${exportName} } from '${importPath}';
|
|
448
|
-
|
|
449
|
-
|
|
609
|
+
${runtime.imports}
|
|
610
|
+
${runtime.bindings}
|
|
450
611
|
|
|
451
612
|
const adapter = new AWSScheduledFunction(envParser, ${exportName});
|
|
452
613
|
|
|
@@ -562,7 +723,7 @@ var FunctionGenerator = class extends ConstructGenerator {
|
|
|
562
723
|
const functionsDir = join(outputDir, "functions");
|
|
563
724
|
await mkdir(functionsDir, { recursive: true });
|
|
564
725
|
for (const { key, construct, path } of constructs) {
|
|
565
|
-
const handlerFile = await this.generateFunctionHandler(functionsDir, path.relative, key, context);
|
|
726
|
+
const handlerFile = await this.generateFunctionHandler(functionsDir, path.relative, key, context, construct.owner);
|
|
566
727
|
functionInfos.push({
|
|
567
728
|
name: key,
|
|
568
729
|
handler: relative(process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
|
|
@@ -575,17 +736,16 @@ var FunctionGenerator = class extends ConstructGenerator {
|
|
|
575
736
|
}
|
|
576
737
|
return functionInfos;
|
|
577
738
|
}
|
|
578
|
-
async generateFunctionHandler(outputDir, sourceFile, exportName, context) {
|
|
739
|
+
async generateFunctionHandler(outputDir, sourceFile, exportName, context, owner) {
|
|
579
740
|
const handlerFileName = `${exportName}.ts`;
|
|
580
741
|
const handlerPath = join(outputDir, handlerFileName);
|
|
581
742
|
const relativePath = relative(dirname(handlerPath), sourceFile);
|
|
582
743
|
const importPath = relativePath.replace(/\.ts$/, ".js");
|
|
583
|
-
const
|
|
584
|
-
const relativeLoggerPath = relative(dirname(handlerPath), context.loggerPath);
|
|
744
|
+
const runtime = runtimeFor(context, dirname(handlerPath), owner);
|
|
585
745
|
const content = `import { AWSLambdaFunction } from '@geekmidas/constructs/aws';
|
|
586
746
|
import { ${exportName} } from '${importPath}';
|
|
587
|
-
|
|
588
|
-
|
|
747
|
+
${runtime.imports}
|
|
748
|
+
${runtime.bindings}
|
|
589
749
|
|
|
590
750
|
const adapter = new AWSLambdaFunction(envParser, ${exportName});
|
|
591
751
|
|
|
@@ -624,7 +784,7 @@ var QueueGenerator = class extends ConstructGenerator {
|
|
|
624
784
|
const queuesDir = join(outputDir, "queues");
|
|
625
785
|
await mkdir(queuesDir, { recursive: true });
|
|
626
786
|
for (const { key, construct, path } of constructs) {
|
|
627
|
-
const handlerFile = await this.generateQueueHandler(queuesDir, path.relative, key, context);
|
|
787
|
+
const handlerFile = await this.generateQueueHandler(queuesDir, path.relative, key, context, construct.owner);
|
|
628
788
|
queueInfos.push({
|
|
629
789
|
name: construct.name,
|
|
630
790
|
handler: relative(process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
|
|
@@ -638,13 +798,14 @@ var QueueGenerator = class extends ConstructGenerator {
|
|
|
638
798
|
}
|
|
639
799
|
return queueInfos;
|
|
640
800
|
}
|
|
641
|
-
async generateQueueHandler(outputDir, sourceFile, exportName, context) {
|
|
801
|
+
async generateQueueHandler(outputDir, sourceFile, exportName, context, owner) {
|
|
642
802
|
const handlerPath = join(outputDir, `${exportName}.ts`);
|
|
643
803
|
const importPath = relative(dirname(handlerPath), sourceFile).replace(/\.ts$/, ".js");
|
|
644
|
-
const
|
|
804
|
+
const runtime = runtimeFor(context, dirname(handlerPath), owner);
|
|
645
805
|
const content = `import { AWSLambdaQueue } from '@geekmidas/constructs/aws';
|
|
646
806
|
import { ${exportName} } from '${importPath}';
|
|
647
|
-
|
|
807
|
+
${runtime.imports}
|
|
808
|
+
${runtime.bindings}
|
|
648
809
|
|
|
649
810
|
const adapter = new AWSLambdaQueue(envParser, ${exportName});
|
|
650
811
|
|
|
@@ -787,7 +948,7 @@ var SubscriberGenerator = class extends ConstructGenerator {
|
|
|
787
948
|
const subscribersDir = join(outputDir, "subscribers");
|
|
788
949
|
await mkdir(subscribersDir, { recursive: true });
|
|
789
950
|
for (const { key, construct, path } of constructs) {
|
|
790
|
-
const handlerFile = await this.generateSubscriberHandler(subscribersDir, path.relative, key, construct, context);
|
|
951
|
+
const handlerFile = await this.generateSubscriberHandler(subscribersDir, path.relative, key, construct, context, construct.owner);
|
|
791
952
|
subscriberInfos.push({
|
|
792
953
|
name: key,
|
|
793
954
|
handler: relative(process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
|
|
@@ -805,15 +966,16 @@ var SubscriberGenerator = class extends ConstructGenerator {
|
|
|
805
966
|
}
|
|
806
967
|
return subscriberInfos;
|
|
807
968
|
}
|
|
808
|
-
async generateSubscriberHandler(outputDir, sourceFile, exportName, _subscriber, context) {
|
|
969
|
+
async generateSubscriberHandler(outputDir, sourceFile, exportName, _subscriber, context, owner) {
|
|
809
970
|
const handlerFileName = `${exportName}.ts`;
|
|
810
971
|
const handlerPath = join(outputDir, handlerFileName);
|
|
811
972
|
const relativePath = relative(dirname(handlerPath), sourceFile);
|
|
812
973
|
const importPath = relativePath.replace(/\.ts$/, ".js");
|
|
813
|
-
const
|
|
974
|
+
const runtime = runtimeFor(context, dirname(handlerPath), owner);
|
|
814
975
|
const content = `import { AWSLambdaSubscriber } from '@geekmidas/constructs/aws';
|
|
815
976
|
import { ${exportName} } from '${importPath}';
|
|
816
|
-
|
|
977
|
+
${runtime.imports}
|
|
978
|
+
${runtime.bindings}
|
|
817
979
|
|
|
818
980
|
const adapter = new AWSLambdaSubscriber(envParser, ${exportName});
|
|
819
981
|
|
|
@@ -1196,13 +1358,7 @@ async function devCommand(options) {
|
|
|
1196
1358
|
}
|
|
1197
1359
|
const resolved = resolveProviders(config, { provider: "server" });
|
|
1198
1360
|
logger$9.log("🚀 Starting development server...");
|
|
1199
|
-
logger$9.log(`Loading
|
|
1200
|
-
if (config.functions) logger$9.log(`Loading functions from: ${config.functions}`);
|
|
1201
|
-
if (config.crons) logger$9.log(`Loading crons from: ${config.crons}`);
|
|
1202
|
-
if (config.subscribers) logger$9.log(`Loading subscribers from: ${config.subscribers}`);
|
|
1203
|
-
logger$9.log(`Using envParser: ${config.envParser}`);
|
|
1204
|
-
const { path: envParserPath, importPattern: envParserImportPattern } = parseModuleConfig(config.envParser, "envParser");
|
|
1205
|
-
const { path: loggerPath, importPattern: loggerImportPattern } = parseModuleConfig(config.logger, "logger");
|
|
1361
|
+
logger$9.log(`Loading constructs from: ${config.constructs}`);
|
|
1206
1362
|
const telescope = normalizeTelescopeConfig(config.telescope);
|
|
1207
1363
|
if (telescope) logger$9.log(`🔭 Telescope enabled at ${telescope.path}`);
|
|
1208
1364
|
const studio = normalizeStudioConfig(config.studio);
|
|
@@ -1213,16 +1369,12 @@ async function devCommand(options) {
|
|
|
1213
1369
|
const enableOpenApi = openApiConfig.enabled || resolved.enableOpenApi;
|
|
1214
1370
|
if (enableOpenApi) logger$9.log(`📄 OpenAPI output: ${OPENAPI_OUTPUT_PATH}`);
|
|
1215
1371
|
const buildContext = {
|
|
1216
|
-
envParserPath,
|
|
1217
|
-
envParserImportPattern,
|
|
1218
|
-
loggerPath,
|
|
1219
|
-
loggerImportPattern,
|
|
1220
1372
|
telescope,
|
|
1221
1373
|
studio,
|
|
1222
1374
|
hooks
|
|
1223
1375
|
};
|
|
1224
1376
|
await buildServer(config, buildContext, resolved.providers[0], enableOpenApi, appRoot);
|
|
1225
|
-
if (enableOpenApi) await
|
|
1377
|
+
if (enableOpenApi) await generateOpenApiFrom(config.constructs, { openapi: config.openapi });
|
|
1226
1378
|
const runtime = config.runtime ?? "node";
|
|
1227
1379
|
let secretsJsonPath;
|
|
1228
1380
|
const appSecrets = await loadSecretsForApp(secretsRoot, workspaceAppName);
|
|
@@ -1255,19 +1407,9 @@ async function devCommand(options) {
|
|
|
1255
1407
|
}
|
|
1256
1408
|
const devServer = new DevServer(resolved.providers[0], options.port ?? workspaceAppPort ?? 3e3, options.portExplicit ?? false, enableOpenApi, telescope, studio, runtime, appRoot, secretsJsonPath);
|
|
1257
1409
|
await devServer.start();
|
|
1258
|
-
const envParserFile = config.envParser.split("#")[0] ?? config.envParser;
|
|
1259
|
-
const loggerFile = config.logger.split("#")[0] ?? config.logger;
|
|
1260
1410
|
const hooksFileParts = config.hooks?.server?.split("#");
|
|
1261
1411
|
const hooksFile = hooksFileParts?.[0];
|
|
1262
|
-
const watchPatterns = [
|
|
1263
|
-
config.routes,
|
|
1264
|
-
...config.functions ? [config.functions] : [],
|
|
1265
|
-
...config.crons ? [config.crons] : [],
|
|
1266
|
-
...config.subscribers ? [config.subscribers] : [],
|
|
1267
|
-
envParserFile.endsWith(".ts") ? envParserFile : `${envParserFile}.ts`,
|
|
1268
|
-
loggerFile.endsWith(".ts") ? loggerFile : `${loggerFile}.ts`,
|
|
1269
|
-
...hooksFile ? [hooksFile.endsWith(".ts") ? hooksFile : `${hooksFile}.ts`] : []
|
|
1270
|
-
].flat().filter((p) => typeof p === "string");
|
|
1412
|
+
const watchPatterns = [config.constructs, ...hooksFile ? [hooksFile.endsWith(".ts") ? hooksFile : `${hooksFile}.ts`] : []].flat().filter((p) => typeof p === "string");
|
|
1271
1413
|
const normalizedPatterns = watchPatterns.map((p) => p.startsWith("./") ? p.slice(2) : p);
|
|
1272
1414
|
logger$9.log(`👀 Watching for changes in: ${normalizedPatterns.join(", ")}`);
|
|
1273
1415
|
const resolvedFiles = await fg(normalizedPatterns, {
|
|
@@ -1300,7 +1442,8 @@ async function devCommand(options) {
|
|
|
1300
1442
|
try {
|
|
1301
1443
|
logger$9.log("🔄 Rebuilding...");
|
|
1302
1444
|
await buildServer(config, buildContext, resolved.providers[0], enableOpenApi, appRoot, true);
|
|
1303
|
-
if (enableOpenApi) await
|
|
1445
|
+
if (enableOpenApi) await generateOpenApiFrom(config.constructs, {
|
|
1446
|
+
openapi: config.openapi,
|
|
1304
1447
|
silent: true,
|
|
1305
1448
|
bustCache: true
|
|
1306
1449
|
});
|
|
@@ -1714,7 +1857,7 @@ async function buildServer(config, context, provider, enableOpenApi, appRoot = p
|
|
|
1714
1857
|
const queueGenerator = new QueueGenerator();
|
|
1715
1858
|
const topicGenerator = new TopicGenerator();
|
|
1716
1859
|
const [allEndpoints, allFunctions, allCrons, allSubscribers, allQueues, allTopics] = await Promise.all([
|
|
1717
|
-
endpointGenerator.load(config.
|
|
1860
|
+
endpointGenerator.load(config.constructs, appRoot, bustCache),
|
|
1718
1861
|
config.functions ? functionGenerator.load(config.functions, appRoot, bustCache) : [],
|
|
1719
1862
|
config.crons ? cronGenerator.load(config.crons, appRoot, bustCache) : [],
|
|
1720
1863
|
config.subscribers ? subscriberGenerator.load(config.subscribers, appRoot, bustCache) : [],
|
|
@@ -2302,18 +2445,35 @@ export default app;
|
|
|
2302
2445
|
`);
|
|
2303
2446
|
}
|
|
2304
2447
|
const logger$7 = console;
|
|
2448
|
+
/**
|
|
2449
|
+
* What a build run at the workspace root reads.
|
|
2450
|
+
*
|
|
2451
|
+
* One backend app there, and it is that app's config, so its own `openapi`
|
|
2452
|
+
* and `telescope` settings still apply. Otherwise it is the workspace's: every
|
|
2453
|
+
* construct glob, and the backends they resolve to.
|
|
2454
|
+
*/
|
|
2455
|
+
function rootGkmConfig(workspace) {
|
|
2456
|
+
const backends = Object.entries(workspace.apps).filter(([, app]) => app.type === "backend");
|
|
2457
|
+
const only = backends.length === 1 ? backends[0] : void 0;
|
|
2458
|
+
const own = only ? getAppGkmConfig(workspace, only[0]) : void 0;
|
|
2459
|
+
return own ?? {
|
|
2460
|
+
services: workspace.services,
|
|
2461
|
+
constructs: allConstructGlobs(workspace)
|
|
2462
|
+
};
|
|
2463
|
+
}
|
|
2305
2464
|
async function buildCommand(options) {
|
|
2306
2465
|
const loadedConfig = await loadWorkspaceConfig();
|
|
2307
2466
|
if (loadedConfig.type === "workspace") {
|
|
2308
2467
|
const cwd = resolve(process.cwd());
|
|
2309
2468
|
const workspaceRoot = resolve(loadedConfig.workspace.root);
|
|
2310
2469
|
const isAtWorkspaceRoot = cwd === workspaceRoot;
|
|
2311
|
-
|
|
2470
|
+
const someAppIsElsewhere = Object.values(loadedConfig.workspace.apps).some((app) => resolve(workspaceRoot, app.path) !== workspaceRoot);
|
|
2471
|
+
if (isAtWorkspaceRoot && someAppIsElsewhere) {
|
|
2312
2472
|
logger$7.log("📦 Detected workspace configuration");
|
|
2313
2473
|
return workspaceBuildCommand(loadedConfig.workspace, options);
|
|
2314
2474
|
}
|
|
2315
2475
|
}
|
|
2316
|
-
const config = loadedConfig.type
|
|
2476
|
+
const config = loadedConfig.type !== "workspace" ? await loadConfig() : resolve(process.cwd()) === resolve(loadedConfig.workspace.root) ? rootGkmConfig(loadedConfig.workspace) : (await loadAppConfig()).gkmConfig;
|
|
2317
2477
|
const resolved = resolveProviders(config, options);
|
|
2318
2478
|
const backendConfig = config.services ?? loadedConfig.workspace?.services;
|
|
2319
2479
|
const cacheBackend = cacheBackendOf(backendConfig?.cache);
|
|
@@ -2321,13 +2481,7 @@ async function buildCommand(options) {
|
|
|
2321
2481
|
const production = normalizeProductionConfig(options.production ?? false, productionConfigFromGkm);
|
|
2322
2482
|
if (production) logger$7.log(`🏭 Building for PRODUCTION`);
|
|
2323
2483
|
logger$7.log(`Building with providers: ${resolved.providers.join(", ")}`);
|
|
2324
|
-
logger$7.log(`Loading
|
|
2325
|
-
if (config.functions) logger$7.log(`Loading functions from: ${formatRoutes(config.functions)}`);
|
|
2326
|
-
if (config.crons) logger$7.log(`Loading crons from: ${formatRoutes(config.crons)}`);
|
|
2327
|
-
if (config.subscribers) logger$7.log(`Loading subscribers from: ${formatRoutes(config.subscribers)}`);
|
|
2328
|
-
logger$7.log(`Using envParser: ${config.envParser}`);
|
|
2329
|
-
const { path: envParserPath, importPattern: envParserImportPattern } = parseModuleConfig(config.envParser, "envParser");
|
|
2330
|
-
const { path: loggerPath, importPattern: loggerImportPattern } = parseModuleConfig(config.logger, "logger");
|
|
2484
|
+
logger$7.log(`Loading constructs from: ${formatRoutes(config.constructs)}`);
|
|
2331
2485
|
const telescope = production ? void 0 : normalizeTelescopeConfig(config.telescope);
|
|
2332
2486
|
if (telescope) logger$7.log(`🔭 Telescope enabled at ${telescope.path}`);
|
|
2333
2487
|
const studio = production ? void 0 : normalizeStudioConfig(config.studio);
|
|
@@ -2355,7 +2509,18 @@ async function buildCommand(options) {
|
|
|
2355
2509
|
const primary = surfaces.find((d) => d.endpoints.length === 0) ?? surfaces[0];
|
|
2356
2510
|
const browsable = Object.entries(declared).find(([, d]) => d.kind === "database");
|
|
2357
2511
|
const browsableSource = browsable ? constructSources[browsable[0]] : void 0;
|
|
2512
|
+
const owners = {};
|
|
2513
|
+
for (const [id, declaration] of Object.entries(declared)) {
|
|
2514
|
+
if (declaration.kind !== "rest-api" && declaration.kind !== "worker") continue;
|
|
2515
|
+
const source = constructSources[id];
|
|
2516
|
+
if (!source) continue;
|
|
2517
|
+
owners[id] = {
|
|
2518
|
+
specifier: source.file,
|
|
2519
|
+
exportName: source.exportName
|
|
2520
|
+
};
|
|
2521
|
+
}
|
|
2358
2522
|
const buildContext = {
|
|
2523
|
+
owners,
|
|
2359
2524
|
...primary ? { surface: {
|
|
2360
2525
|
id: primary.id,
|
|
2361
2526
|
trustedOriginsKey: provideKey(primary.id, "trustedOrigins"),
|
|
@@ -2365,10 +2530,6 @@ async function buildCommand(options) {
|
|
|
2365
2530
|
exportName: constructSources[primary.id].exportName
|
|
2366
2531
|
} } : {}
|
|
2367
2532
|
} } : {},
|
|
2368
|
-
envParserPath,
|
|
2369
|
-
envParserImportPattern,
|
|
2370
|
-
loggerPath,
|
|
2371
|
-
loggerImportPattern,
|
|
2372
2533
|
telescope,
|
|
2373
2534
|
studio: studio && browsableSource ? {
|
|
2374
2535
|
...studio,
|
|
@@ -2395,13 +2556,14 @@ async function buildCommand(options) {
|
|
|
2395
2556
|
const subscriberGenerator = new SubscriberGenerator();
|
|
2396
2557
|
const queueGenerator = new QueueGenerator();
|
|
2397
2558
|
const topicGenerator = new TopicGenerator();
|
|
2559
|
+
const code = constructGlobs$1;
|
|
2398
2560
|
const [allEndpoints, allFunctions, allCrons, allSubscribers, allQueues, allTopics] = await Promise.all([
|
|
2399
|
-
endpointGenerator.load(
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2561
|
+
endpointGenerator.load(code),
|
|
2562
|
+
functionGenerator.load(code),
|
|
2563
|
+
cronGenerator.load(code),
|
|
2564
|
+
subscriberGenerator.load(code),
|
|
2565
|
+
queueGenerator.load(code),
|
|
2566
|
+
topicGenerator.load(code)
|
|
2405
2567
|
]);
|
|
2406
2568
|
logger$7.log(`Found ${allEndpoints.length} endpoints`);
|
|
2407
2569
|
logger$7.log(`Found ${allFunctions.length} functions`);
|
|
@@ -2410,7 +2572,7 @@ async function buildCommand(options) {
|
|
|
2410
2572
|
logger$7.log(`Found ${allQueues.length} queues`);
|
|
2411
2573
|
logger$7.log(`Found ${allTopics.length} topics`);
|
|
2412
2574
|
if (allEndpoints.length === 0 && allFunctions.length === 0 && allCrons.length === 0 && allSubscribers.length === 0 && allQueues.length === 0 && allTopics.length === 0) {
|
|
2413
|
-
const selfServing = Object.entries(declared).find(([id, d]) => d.kind === "rest-api" && d.endpoints.length > 0 && constructSources[id] !== void 0 && resolve(loadedConfig.workspace.root, resolveAppSpec(id, d.app ?? {}, loadedConfig.workspace.root
|
|
2575
|
+
const selfServing = Object.entries(declared).find(([id, d]) => d.kind === "rest-api" && d.endpoints.length > 0 && constructSources[id] !== void 0 && resolve(loadedConfig.workspace.root, resolveAppSpec(id, d.app ?? {}, loadedConfig.workspace.root).path) === resolve(process.cwd()));
|
|
2414
2576
|
if (selfServing) {
|
|
2415
2577
|
const [id] = selfServing;
|
|
2416
2578
|
await writeSurfaceEntry(join(process.cwd(), ".gkm", "server"), constructSources[id]);
|
|
@@ -2427,7 +2589,7 @@ async function buildCommand(options) {
|
|
|
2427
2589
|
const providerResult = await buildForProvider(provider, buildContext, rootOutputDir, endpointGenerator, functionGenerator, cronGenerator, subscriberGenerator, queueGenerator, topicGenerator, allEndpoints, allFunctions, allCrons, allSubscribers, allQueues, allTopics, resolved.enableOpenApi, options.skipBundle ?? false, options.stage);
|
|
2428
2590
|
if (providerResult.masterKey) result = providerResult;
|
|
2429
2591
|
}
|
|
2430
|
-
await generateOpenApi(
|
|
2592
|
+
await generateOpenApi(allEndpoints.map(({ construct }) => construct), { openapi: config.openapi });
|
|
2431
2593
|
return result;
|
|
2432
2594
|
}
|
|
2433
2595
|
async function buildForProvider(provider, context, rootOutputDir, endpointGenerator, functionGenerator, cronGenerator, subscriberGenerator, queueGenerator, topicGenerator, endpoints, functions, crons, subscribers, queues, topics, enableOpenApi, skipBundle, stage) {
|
|
@@ -3051,7 +3213,7 @@ function minioCompose(options) {
|
|
|
3051
3213
|
return [
|
|
3052
3214
|
"services:",
|
|
3053
3215
|
` ${service}:`,
|
|
3054
|
-
" image:
|
|
3216
|
+
" image: pgsty/minio:RELEASE.2026-08-04T00-00-00Z",
|
|
3055
3217
|
` command: server /data --console-address ":9001"`,
|
|
3056
3218
|
" environment:",
|
|
3057
3219
|
` MINIO_ROOT_USER: ${user}`,
|
|
@@ -3870,7 +4032,7 @@ const DEFAULT_SERVICE_IMAGES = {
|
|
|
3870
4032
|
postgres: "postgres",
|
|
3871
4033
|
redis: "redis",
|
|
3872
4034
|
rabbitmq: "rabbitmq",
|
|
3873
|
-
minio: "
|
|
4035
|
+
minio: "pgsty/minio",
|
|
3874
4036
|
mailpit: "axllent/mailpit",
|
|
3875
4037
|
localstack: "floci/floci"
|
|
3876
4038
|
};
|
|
@@ -3879,7 +4041,7 @@ const DEFAULT_SERVICE_VERSIONS = {
|
|
|
3879
4041
|
postgres: `${DEFAULT_POSTGRES_VERSION}-alpine`,
|
|
3880
4042
|
redis: "7-alpine",
|
|
3881
4043
|
rabbitmq: "3-management-alpine",
|
|
3882
|
-
minio: "RELEASE.
|
|
4044
|
+
minio: "RELEASE.2026-08-04T00-00-00Z",
|
|
3883
4045
|
mailpit: "latest",
|
|
3884
4046
|
localstack: "latest"
|
|
3885
4047
|
};
|
|
@@ -4331,7 +4493,7 @@ function getInfraServiceImage(serviceName$1, pin) {
|
|
|
4331
4493
|
const defaults = {
|
|
4332
4494
|
postgres: "postgres:18-alpine",
|
|
4333
4495
|
redis: "redis:7-alpine",
|
|
4334
|
-
minio: "
|
|
4496
|
+
minio: "pgsty/minio:RELEASE.2026-08-04T00-00-00Z"
|
|
4335
4497
|
};
|
|
4336
4498
|
return pin || defaults[serviceName$1];
|
|
4337
4499
|
}
|
|
@@ -7694,11 +7856,11 @@ Crons, subscribers and functions are declared the same way endpoints are: as
|
|
|
7694
7856
|
exported values the build discovers.
|
|
7695
7857
|
|
|
7696
7858
|
\`\`\`typescript
|
|
7697
|
-
// a scheduled job
|
|
7698
|
-
import {
|
|
7859
|
+
// a scheduled job, built from the worker that runs it
|
|
7860
|
+
import { worker } from '../constructs/worker.ts';
|
|
7699
7861
|
|
|
7700
|
-
export const cleanup =
|
|
7701
|
-
.
|
|
7862
|
+
export const cleanup = worker
|
|
7863
|
+
.cron('rate(1 day)')
|
|
7702
7864
|
.handle(async ({ logger }) => {
|
|
7703
7865
|
logger.info('cleaning up');
|
|
7704
7866
|
});
|
|
@@ -7706,10 +7868,10 @@ export const cleanup = c
|
|
|
7706
7868
|
|
|
7707
7869
|
\`\`\`typescript
|
|
7708
7870
|
// a topic subscriber
|
|
7709
|
-
import { s } from '@geekmidas/constructs/subscribers';
|
|
7710
7871
|
import { userEvents } from '../constructs/topics.ts';
|
|
7872
|
+
import { worker } from '../constructs/worker.ts';
|
|
7711
7873
|
|
|
7712
|
-
export const onUserCreated =
|
|
7874
|
+
export const onUserCreated = worker
|
|
7713
7875
|
.topic(userEvents)
|
|
7714
7876
|
.subscribe(['user.created'])
|
|
7715
7877
|
.handle(async ({ events, logger }) => {
|
|
@@ -7814,26 +7976,26 @@ const CLI_VERSION = `~${pkg.version}`;
|
|
|
7814
7976
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
7815
7977
|
*/
|
|
7816
7978
|
const GEEKMIDAS_VERSIONS = {
|
|
7817
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
7818
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
7819
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
7820
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
7821
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
7822
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
7823
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
7824
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
7825
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
7826
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
7827
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
7828
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
7829
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
7830
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
7831
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
7832
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
7833
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
7834
|
-
"@geekmidas/studio": "~10.0.0-alpha.
|
|
7835
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
7836
|
-
"@geekmidas/testkit": "~10.0.0-alpha.
|
|
7979
|
+
"@geekmidas/audit": "~10.0.0-alpha.5",
|
|
7980
|
+
"@geekmidas/auth": "~10.0.0-alpha.5",
|
|
7981
|
+
"@geekmidas/cache": "~10.0.0-alpha.5",
|
|
7982
|
+
"@geekmidas/client": "~10.0.0-alpha.5",
|
|
7983
|
+
"@geekmidas/cloud": "~10.0.0-alpha.5",
|
|
7984
|
+
"@geekmidas/constructs": "~10.0.0-alpha.5",
|
|
7985
|
+
"@geekmidas/db": "~10.0.0-alpha.5",
|
|
7986
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.5",
|
|
7987
|
+
"@geekmidas/envkit": "~10.0.0-alpha.5",
|
|
7988
|
+
"@geekmidas/errors": "~10.0.0-alpha.5",
|
|
7989
|
+
"@geekmidas/events": "~10.0.0-alpha.5",
|
|
7990
|
+
"@geekmidas/logger": "~10.0.0-alpha.5",
|
|
7991
|
+
"@geekmidas/manifest": "~10.0.0-alpha.5",
|
|
7992
|
+
"@geekmidas/rate-limit": "~10.0.0-alpha.5",
|
|
7993
|
+
"@geekmidas/schema": "~10.0.0-alpha.5",
|
|
7994
|
+
"@geekmidas/services": "~10.0.0-alpha.5",
|
|
7995
|
+
"@geekmidas/storage": "~10.0.0-alpha.5",
|
|
7996
|
+
"@geekmidas/studio": "~10.0.0-alpha.5",
|
|
7997
|
+
"@geekmidas/telescope": "~10.0.0-alpha.5",
|
|
7998
|
+
"@geekmidas/testkit": "~10.0.0-alpha.5",
|
|
7837
7999
|
"@geekmidas/cli": CLI_VERSION
|
|
7838
8000
|
};
|
|
7839
8001
|
|
|
@@ -8022,7 +8184,9 @@ export default defineConfig({${options.monorepo ? "" : `
|
|
|
8022
8184
|
forceConsistentCasingInFileNames: true,
|
|
8023
8185
|
resolveJsonModule: true,
|
|
8024
8186
|
noEmit: true,
|
|
8025
|
-
allowImportingTsExtensions: true
|
|
8187
|
+
allowImportingTsExtensions: true,
|
|
8188
|
+
baseUrl: ".",
|
|
8189
|
+
paths: { "~/*": ["./src/*"] }
|
|
8026
8190
|
},
|
|
8027
8191
|
include: ["src/**/*.ts"],
|
|
8028
8192
|
exclude: ["node_modules", "dist"]
|
|
@@ -8280,7 +8444,7 @@ function generateDockerFiles(options, template, dbApps) {
|
|
|
8280
8444
|
MP_SMTP_AUTH: \${SMTP_USER:-${options.name}}:\${SMTP_PASS:-${options.name}}`);
|
|
8281
8445
|
if (options.services?.storage) {
|
|
8282
8446
|
services.push(` minio:
|
|
8283
|
-
image:
|
|
8447
|
+
image: pgsty/minio:RELEASE.2026-08-04T00-00-00Z
|
|
8284
8448
|
container_name: ${options.name}-minio
|
|
8285
8449
|
restart: unless-stopped
|
|
8286
8450
|
entrypoint: sh
|
|
@@ -10449,15 +10613,23 @@ export const healthEndpoint = api
|
|
|
10449
10613
|
},
|
|
10450
10614
|
{
|
|
10451
10615
|
path: "src/functions/hello.ts",
|
|
10452
|
-
content: `import {
|
|
10453
|
-
import {
|
|
10616
|
+
content: `import { z } from 'zod';
|
|
10617
|
+
import { worker } from '~/constructs/worker.ts';
|
|
10454
10618
|
|
|
10455
|
-
export const helloFunction =
|
|
10619
|
+
export const helloFunction = worker
|
|
10456
10620
|
.input(z.object({ name: z.string() }))
|
|
10457
10621
|
.output(z.object({ message: z.string() }))
|
|
10458
10622
|
.handle(async ({ input }) => ({
|
|
10459
10623
|
message: \`Hello, \${input.name}!\`,
|
|
10460
10624
|
}));
|
|
10625
|
+
`
|
|
10626
|
+
},
|
|
10627
|
+
{
|
|
10628
|
+
path: "src/constructs/worker.ts",
|
|
10629
|
+
content: `import { Worker } from '@geekmidas/constructs/worker';
|
|
10630
|
+
import { logger } from '../config/logger.ts';
|
|
10631
|
+
|
|
10632
|
+
export const worker = new Worker('Jobs', { logger });
|
|
10461
10633
|
`
|
|
10462
10634
|
}
|
|
10463
10635
|
];
|
|
@@ -10490,12 +10662,11 @@ export const telescope = new Telescope({
|
|
|
10490
10662
|
* that was missing: it takes no authorizer, publishes no address, and hands out
|
|
10491
10663
|
* factories already carrying its logger.
|
|
10492
10664
|
*
|
|
10493
|
-
* It scaffolds
|
|
10494
|
-
*
|
|
10495
|
-
*
|
|
10496
|
-
*
|
|
10497
|
-
*
|
|
10498
|
-
* when there is a scheduler to run it.
|
|
10665
|
+
* It scaffolds a cron when the project has a database, and not otherwise. A
|
|
10666
|
+
* server fires its own crons, and the schedule lives in Postgres so that
|
|
10667
|
+
* running more than one replica still fires each job once — so a worker names
|
|
10668
|
+
* the database it keeps schedules in. Without one there is nowhere to keep
|
|
10669
|
+
* them, and an example that could not run is worse than none.
|
|
10499
10670
|
*/
|
|
10500
10671
|
const workerTemplate = {
|
|
10501
10672
|
name: "worker",
|
|
@@ -10529,7 +10700,7 @@ const workerTemplate = {
|
|
|
10529
10700
|
"fmt:check": "biome format ."
|
|
10530
10701
|
},
|
|
10531
10702
|
files: (options) => {
|
|
10532
|
-
const { loggerType, name: name$1 } = options;
|
|
10703
|
+
const { loggerType, name: name$1, database } = options;
|
|
10533
10704
|
const loggerContent = `import { createLogger } from '@geekmidas/logger/${loggerType}';
|
|
10534
10705
|
|
|
10535
10706
|
export const logger = createLogger();
|
|
@@ -10538,9 +10709,9 @@ export const logger = createLogger();
|
|
|
10538
10709
|
{
|
|
10539
10710
|
path: "src/constructs/worker.ts",
|
|
10540
10711
|
content: `import { Worker } from '@geekmidas/constructs/worker';
|
|
10541
|
-
import { logger } from '../config/logger.ts';
|
|
10712
|
+
import { logger } from '../config/logger.ts';${database ? "\nimport { database } from './database.ts';" : ""}
|
|
10542
10713
|
|
|
10543
|
-
export const worker = new Worker('Jobs', { logger });
|
|
10714
|
+
export const worker = new Worker('Jobs', { logger })${database ? ".database(database)" : ""};
|
|
10544
10715
|
`
|
|
10545
10716
|
},
|
|
10546
10717
|
{
|
|
@@ -10608,7 +10779,7 @@ export const eventsPublisherService = {
|
|
|
10608
10779
|
content: `import { worker } from '~/constructs/worker.ts';
|
|
10609
10780
|
import { eventsPublisherService } from '~/events/publisher.ts';
|
|
10610
10781
|
|
|
10611
|
-
export const userEventsSubscriber = worker
|
|
10782
|
+
export const userEventsSubscriber = worker
|
|
10612
10783
|
.publisher(eventsPublisherService)
|
|
10613
10784
|
.subscribe(['user.created', 'user.updated'])
|
|
10614
10785
|
.handle(async ({ events, logger }) => {
|
|
@@ -10630,6 +10801,21 @@ export const userEventsSubscriber = worker.subscribers
|
|
|
10630
10801
|
`
|
|
10631
10802
|
}
|
|
10632
10803
|
];
|
|
10804
|
+
if (database) files$1.push({
|
|
10805
|
+
path: "src/crons/cleanup.ts",
|
|
10806
|
+
content: `import { worker } from '~/constructs/worker.ts';
|
|
10807
|
+
|
|
10808
|
+
export const cleanup = worker
|
|
10809
|
+
.cron('rate(1 day)')
|
|
10810
|
+
.handle(async ({ logger }) => {
|
|
10811
|
+
logger.info('Running cleanup');
|
|
10812
|
+
|
|
10813
|
+
// Delete old sessions, clear temp files, whatever falls out of date.
|
|
10814
|
+
|
|
10815
|
+
logger.info('Cleanup complete');
|
|
10816
|
+
});
|
|
10817
|
+
`
|
|
10818
|
+
});
|
|
10633
10819
|
if (options.database) files$1.push(...databaseFiles(name$1));
|
|
10634
10820
|
if (options.telescope) files$1.push({
|
|
10635
10821
|
path: "src/config/telescope.ts",
|
|
@@ -13875,7 +14061,7 @@ async function testCommand(options = {}) {
|
|
|
13875
14061
|
console.log(`\n🧪 Running tests with ${stage} environment...\n`);
|
|
13876
14062
|
const autoSetup = options.autoSetup || Boolean(process.env.GKM_AUTO_SETUP);
|
|
13877
14063
|
if (autoSetup) {
|
|
13878
|
-
const { ensureStageSecrets } = await import("./setup-
|
|
14064
|
+
const { ensureStageSecrets } = await import("./setup-DGIPd2fh.mjs");
|
|
13879
14065
|
const generated = await ensureStageSecrets(stage, cwd);
|
|
13880
14066
|
if (generated) console.log(` 🔐 Generated fresh ${stage} secrets (auto-setup)`);
|
|
13881
14067
|
}
|
|
@@ -14350,7 +14536,7 @@ program.command("secrets:push").description("Push secrets to remote provider (SS
|
|
|
14350
14536
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
14351
14537
|
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|
|
14352
14538
|
if (secrets) {
|
|
14353
|
-
const { derivedContainers: derivedContainers$1 } = await import("./workspace-
|
|
14539
|
+
const { derivedContainers: derivedContainers$1 } = await import("./workspace-CnTqIlB3.mjs");
|
|
14354
14540
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers$1(workspace, options.stage));
|
|
14355
14541
|
if (result) {
|
|
14356
14542
|
await writeStageSecrets$1(result.secrets, workspace.root);
|
|
@@ -14379,7 +14565,7 @@ program.command("secrets:pull").description("Pull secrets from remote provider (
|
|
|
14379
14565
|
console.error(`No remote secrets found for stage "${options.stage}".`);
|
|
14380
14566
|
process.exit(1);
|
|
14381
14567
|
}
|
|
14382
|
-
const { derivedContainers: derivedContainers$1 } = await import("./workspace-
|
|
14568
|
+
const { derivedContainers: derivedContainers$1 } = await import("./workspace-CnTqIlB3.mjs");
|
|
14383
14569
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers$1(workspace, options.stage));
|
|
14384
14570
|
if (result) {
|
|
14385
14571
|
secrets = result.secrets;
|
|
@@ -14406,7 +14592,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
14406
14592
|
console.error(`No secrets found for stage "${options.stage}". Run "gkm secrets:init --stage ${options.stage}" first.`);
|
|
14407
14593
|
process.exit(1);
|
|
14408
14594
|
}
|
|
14409
|
-
const { derivedContainers: derivedContainers$1 } = await import("./workspace-
|
|
14595
|
+
const { derivedContainers: derivedContainers$1 } = await import("./workspace-CnTqIlB3.mjs");
|
|
14410
14596
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers$1(workspace, options.stage));
|
|
14411
14597
|
if (!result) {
|
|
14412
14598
|
console.log(`\n✓ Secrets for stage "${options.stage}" are up-to-date`);
|