@geekmidas/cli 1.11.0 → 2.0.0
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/CHANGELOG.md +155 -0
- package/dist/config.d.cts +2 -2
- package/dist/config.d.mts +2 -2
- package/dist/{index-Dx9Kk5bR.d.mts → index-Bw548Bta.d.mts} +2 -2
- package/dist/{index-Dx9Kk5bR.d.mts.map → index-Bw548Bta.d.mts.map} +1 -1
- package/dist/{index-Dz2a7xQU.d.cts → index-DTpkeFb8.d.cts} +2 -2
- package/dist/{index-Dz2a7xQU.d.cts.map → index-DTpkeFb8.d.cts.map} +1 -1
- package/dist/index.cjs +283 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +283 -31
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-CG1LBk7s.cjs → openapi-BzNOC7_i.cjs} +14 -2
- package/dist/openapi-BzNOC7_i.cjs.map +1 -0
- package/dist/{openapi-CtRVU6RD.mjs → openapi-Cfwi0fqF.mjs} +14 -2
- package/dist/openapi-Cfwi0fqF.mjs.map +1 -0
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/{types-CqfhdmRi.d.mts → types-B3GQUg3k.d.mts} +3 -1
- package/dist/{types-CqfhdmRi.d.mts.map → types-B3GQUg3k.d.mts.map} +1 -1
- package/dist/{types-DdHfUbxk.d.cts → types-BWQDiooe.d.cts} +3 -1
- package/dist/{types-DdHfUbxk.d.cts.map → types-BWQDiooe.d.cts.map} +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/package.json +8 -7
- package/src/build/__tests__/index-new.spec.ts +3 -1
- package/src/build/__tests__/manifests.spec.ts +2 -2
- package/src/build/index.ts +61 -21
- package/src/build/manifests.ts +62 -5
- package/src/dev/index.ts +25 -11
- package/src/generators/EndpointGenerator.ts +14 -2
- package/src/generators/QueueGenerator.ts +245 -0
- package/src/generators/SubscriberGenerator.ts +5 -0
- package/src/generators/TopicGenerator.ts +45 -0
- package/src/generators/__tests__/QueueGenerator.spec.ts +141 -0
- package/src/generators/index.ts +2 -0
- package/src/init/generators/test.ts +2 -1
- package/src/init/versions.ts +9 -9
- package/src/types.ts +14 -51
- package/dist/openapi-CG1LBk7s.cjs.map +0 -1
- package/dist/openapi-CtRVU6RD.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -6,7 +6,7 @@ const require_credentials = require('./credentials-C8DWtnMY.cjs');
|
|
|
6
6
|
const require_fullstack_secrets = require('./fullstack-secrets-BYzi6Hwl.cjs');
|
|
7
7
|
const require_storage = require('./storage-B1wvztiJ.cjs');
|
|
8
8
|
const require_setup = require('./setup-WZ-xqAQO.cjs');
|
|
9
|
-
const require_openapi = require('./openapi-
|
|
9
|
+
const require_openapi = require('./openapi-BzNOC7_i.cjs');
|
|
10
10
|
const require_dokploy_api = require('./dokploy-api-CqOIKHme.cjs');
|
|
11
11
|
const require_encryption = require('./encryption-MfXAiFTm.cjs');
|
|
12
12
|
const require_CachedStateProvider = require('./CachedStateProvider-klLWZXWF.cjs');
|
|
@@ -25,7 +25,9 @@ const yaml = require_chunk.__toESM(require("yaml"));
|
|
|
25
25
|
const node_crypto = require_chunk.__toESM(require("node:crypto"));
|
|
26
26
|
const __geekmidas_constructs_crons = require_chunk.__toESM(require("@geekmidas/constructs/crons"));
|
|
27
27
|
const __geekmidas_constructs_functions = require_chunk.__toESM(require("@geekmidas/constructs/functions"));
|
|
28
|
+
const __geekmidas_constructs_queue = require_chunk.__toESM(require("@geekmidas/constructs/queue"));
|
|
28
29
|
const __geekmidas_constructs_subscribers = require_chunk.__toESM(require("@geekmidas/constructs/subscribers"));
|
|
30
|
+
const __geekmidas_constructs_topic = require_chunk.__toESM(require("@geekmidas/constructs/topic"));
|
|
29
31
|
const node_url = require_chunk.__toESM(require("node:url"));
|
|
30
32
|
const pg = require_chunk.__toESM(require("pg"));
|
|
31
33
|
const node_dns_promises = require_chunk.__toESM(require("node:dns/promises"));
|
|
@@ -34,7 +36,7 @@ const prompts = require_chunk.__toESM(require("prompts"));
|
|
|
34
36
|
|
|
35
37
|
//#region package.json
|
|
36
38
|
var name = "@geekmidas/cli";
|
|
37
|
-
var version = "1.
|
|
39
|
+
var version = "1.12.0";
|
|
38
40
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
39
41
|
var private$1 = false;
|
|
40
42
|
var type = "module";
|
|
@@ -114,6 +116,7 @@ var dependencies = {
|
|
|
114
116
|
"@geekmidas/envkit": "workspace:~",
|
|
115
117
|
"@geekmidas/errors": "workspace:~",
|
|
116
118
|
"@geekmidas/logger": "workspace:~",
|
|
119
|
+
"@geekmidas/manifest": "workspace:*",
|
|
117
120
|
"@geekmidas/schema": "workspace:~",
|
|
118
121
|
"chokidar": "~4.0.3",
|
|
119
122
|
"commander": "^12.1.0",
|
|
@@ -461,6 +464,176 @@ export const handler = adapter.handler;
|
|
|
461
464
|
}
|
|
462
465
|
};
|
|
463
466
|
|
|
467
|
+
//#endregion
|
|
468
|
+
//#region src/generators/QueueGenerator.ts
|
|
469
|
+
/**
|
|
470
|
+
* Generates the runtime for `q` queue workers.
|
|
471
|
+
*
|
|
472
|
+
* - **server** (`gkm dev`): a single `queues.ts` exposing `setupQueues()` that
|
|
473
|
+
* runs an in-process pg-boss poller alongside the Hono server — no SQS/Lambda.
|
|
474
|
+
* Each queue subscribes by its **name** on the shared event connection.
|
|
475
|
+
* - **aws-lambda**: one handler file per queue wrapping `AWSLambdaQueue`, backed
|
|
476
|
+
* by an SQS event-source mapping.
|
|
477
|
+
*/
|
|
478
|
+
var QueueGenerator = class extends require_openapi.ConstructGenerator {
|
|
479
|
+
isConstruct(value) {
|
|
480
|
+
return __geekmidas_constructs_queue.Queue.isQueue(value);
|
|
481
|
+
}
|
|
482
|
+
async build(context, constructs, outputDir, options) {
|
|
483
|
+
const provider = options?.provider || "aws-lambda";
|
|
484
|
+
const logger$13 = console;
|
|
485
|
+
const queueInfos = [];
|
|
486
|
+
if (provider === "server") {
|
|
487
|
+
await this.generateServerQueuesFile(outputDir, constructs);
|
|
488
|
+
logger$13.log(`Generated server queues file with ${constructs.length} queues (polling mode)`);
|
|
489
|
+
return queueInfos;
|
|
490
|
+
}
|
|
491
|
+
if (constructs.length === 0 || provider !== "aws-lambda") return queueInfos;
|
|
492
|
+
const queuesDir = (0, node_path.join)(outputDir, "queues");
|
|
493
|
+
await (0, node_fs_promises.mkdir)(queuesDir, { recursive: true });
|
|
494
|
+
for (const { key, construct, path } of constructs) {
|
|
495
|
+
const handlerFile = await this.generateQueueHandler(queuesDir, path.relative, key, context);
|
|
496
|
+
queueInfos.push({
|
|
497
|
+
name: construct.name,
|
|
498
|
+
handler: (0, node_path.relative)(process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
|
|
499
|
+
batchSize: construct.batchSize,
|
|
500
|
+
fifo: construct.fifo,
|
|
501
|
+
timeout: construct.timeout,
|
|
502
|
+
environment: await construct.getEnvironment({ markOptional: context.markOptional })
|
|
503
|
+
});
|
|
504
|
+
logger$13.log(`Generated queue handler: ${key}`);
|
|
505
|
+
}
|
|
506
|
+
return queueInfos;
|
|
507
|
+
}
|
|
508
|
+
async generateQueueHandler(outputDir, sourceFile, exportName, context) {
|
|
509
|
+
const handlerPath = (0, node_path.join)(outputDir, `${exportName}.ts`);
|
|
510
|
+
const importPath = (0, node_path.relative)((0, node_path.dirname)(handlerPath), sourceFile).replace(/\.ts$/, ".js");
|
|
511
|
+
const relativeEnvParserPath = (0, node_path.relative)((0, node_path.dirname)(handlerPath), context.envParserPath);
|
|
512
|
+
const content = `import { AWSLambdaQueue } from '@geekmidas/constructs/aws';
|
|
513
|
+
import { ${exportName} } from '${importPath}';
|
|
514
|
+
import ${context.envParserImportPattern} from '${relativeEnvParserPath}';
|
|
515
|
+
|
|
516
|
+
const adapter = new AWSLambdaQueue(envParser, ${exportName});
|
|
517
|
+
|
|
518
|
+
export const handler = adapter.handler;
|
|
519
|
+
`;
|
|
520
|
+
await (0, node_fs_promises.writeFile)(handlerPath, content);
|
|
521
|
+
return handlerPath;
|
|
522
|
+
}
|
|
523
|
+
async generateServerQueuesFile(outputDir, queues) {
|
|
524
|
+
await (0, node_fs_promises.mkdir)(outputDir, { recursive: true });
|
|
525
|
+
const queuesPath = (0, node_path.join)(outputDir, "queues.ts");
|
|
526
|
+
const importsByFile = /* @__PURE__ */ new Map();
|
|
527
|
+
for (const { path, key } of queues) {
|
|
528
|
+
const importPath = (0, node_path.relative)((0, node_path.dirname)(queuesPath), path.relative).replace(/\.ts$/, ".js");
|
|
529
|
+
if (!importsByFile.has(importPath)) importsByFile.set(importPath, []);
|
|
530
|
+
importsByFile.get(importPath)?.push(key);
|
|
531
|
+
}
|
|
532
|
+
const imports = Array.from(importsByFile.entries()).map(([importPath, exports$2]) => `import { ${exports$2.join(", ")} } from '${importPath}';`).join("\n");
|
|
533
|
+
const allExportNames = queues.map(({ key }) => key);
|
|
534
|
+
const content = `/**
|
|
535
|
+
* Generated queue workers setup
|
|
536
|
+
*
|
|
537
|
+
* ⚠️ WARNING: This is for LOCAL DEVELOPMENT ONLY
|
|
538
|
+
* This runs an in-process pg-boss poller alongside the HTTP server. Each queue
|
|
539
|
+
* subscribes by its name on the shared event connection
|
|
540
|
+
* (EVENT_SUBSCRIBER_CONNECTION_STRING), so producers using
|
|
541
|
+
* <NAME>_PUBLISHER_CONNECTION_STRING (pgboss:// locally) reach it.
|
|
542
|
+
*
|
|
543
|
+
* For production, queues run as AWS Lambda with SQS event-source mappings.
|
|
544
|
+
*/
|
|
545
|
+
import type { EnvironmentParser } from '@geekmidas/envkit';
|
|
546
|
+
import type { Logger } from '@geekmidas/logger';
|
|
547
|
+
import { EventConnectionFactory, Subscriber } from '@geekmidas/events';
|
|
548
|
+
import type { EventConnection, EventSubscriber } from '@geekmidas/events';
|
|
549
|
+
import { ServiceDiscovery } from '@geekmidas/services';
|
|
550
|
+
${imports}
|
|
551
|
+
|
|
552
|
+
const queues = [
|
|
553
|
+
${allExportNames.join(",\n ")}
|
|
554
|
+
];
|
|
555
|
+
|
|
556
|
+
const activeSubscribers: EventSubscriber<any>[] = [];
|
|
557
|
+
|
|
558
|
+
export async function setupQueues(
|
|
559
|
+
envParser: EnvironmentParser<any>,
|
|
560
|
+
logger: Logger,
|
|
561
|
+
): Promise<void> {
|
|
562
|
+
if (queues.length === 0) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
logger.info('Setting up queue workers in polling mode (local development)');
|
|
567
|
+
|
|
568
|
+
const config = envParser.create((get) => ({
|
|
569
|
+
connectionString: get('EVENT_SUBSCRIBER_CONNECTION_STRING').string(),
|
|
570
|
+
})).parse();
|
|
571
|
+
|
|
572
|
+
const serviceDiscovery = ServiceDiscovery.getInstance(envParser);
|
|
573
|
+
|
|
574
|
+
let connection: EventConnection;
|
|
575
|
+
try {
|
|
576
|
+
connection = await EventConnectionFactory.fromConnectionString(config.connectionString);
|
|
577
|
+
const connectionType = new URL(config.connectionString).protocol.replace(':', '');
|
|
578
|
+
logger.info({ connectionType }, 'Created shared event connection for queues');
|
|
579
|
+
} catch (error) {
|
|
580
|
+
logger.error({ error }, 'Failed to create event connection for queues');
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
for (const queue of queues) {
|
|
585
|
+
try {
|
|
586
|
+
const eventSubscriber = await Subscriber.fromConnection(connection);
|
|
587
|
+
|
|
588
|
+
const services = queue.services.length > 0
|
|
589
|
+
? await serviceDiscovery.register(queue.services)
|
|
590
|
+
: {};
|
|
591
|
+
|
|
592
|
+
// A queue subscribes to a single "type" — its own name.
|
|
593
|
+
await eventSubscriber.subscribe([queue.name], async (message) => {
|
|
594
|
+
try {
|
|
595
|
+
const validation = await queue.messageSchema['~standard'].validate(message.payload);
|
|
596
|
+
if (validation.issues) {
|
|
597
|
+
logger.error({ issues: validation.issues, queue: queue.name }, 'Queue message failed validation');
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
await queue.handler({
|
|
602
|
+
messages: [validation.value],
|
|
603
|
+
services: services,
|
|
604
|
+
logger: queue.logger,
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
logger.debug({ queue: queue.name }, 'Successfully processed queue message');
|
|
608
|
+
} catch (error) {
|
|
609
|
+
logger.error({ error, queue: queue.name }, 'Failed to process queue message');
|
|
610
|
+
// Message will become visible again for retry.
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
activeSubscribers.push(eventSubscriber);
|
|
615
|
+
logger.info({ queue: queue.name }, 'Queue worker started polling');
|
|
616
|
+
} catch (error) {
|
|
617
|
+
logger.error({ error, queue: queue.name }, 'Failed to setup queue worker');
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const shutdown = () => {
|
|
622
|
+
logger.info('Stopping all queue workers');
|
|
623
|
+
for (const _ of activeSubscribers) {
|
|
624
|
+
connection.stop();
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
process.on('SIGTERM', shutdown);
|
|
629
|
+
process.on('SIGINT', shutdown);
|
|
630
|
+
}
|
|
631
|
+
`;
|
|
632
|
+
await (0, node_fs_promises.writeFile)(queuesPath, content);
|
|
633
|
+
return queuesPath;
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
|
|
464
637
|
//#endregion
|
|
465
638
|
//#region src/generators/SubscriberGenerator.ts
|
|
466
639
|
var SubscriberGenerator = class extends require_openapi.ConstructGenerator {
|
|
@@ -486,6 +659,10 @@ var SubscriberGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
486
659
|
name: key,
|
|
487
660
|
handler: (0, node_path.relative)(process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
|
|
488
661
|
subscribedEvents: construct.subscribedEvents || [],
|
|
662
|
+
...construct.topicName ? {
|
|
663
|
+
transport: "topic",
|
|
664
|
+
topic: construct.topicName
|
|
665
|
+
} : {},
|
|
489
666
|
timeout: construct.timeout,
|
|
490
667
|
memorySize: construct.memorySize,
|
|
491
668
|
environment: await construct.getEnvironment({ markOptional: context.markOptional })
|
|
@@ -654,6 +831,32 @@ export async function setupSubscribers(
|
|
|
654
831
|
}
|
|
655
832
|
};
|
|
656
833
|
|
|
834
|
+
//#endregion
|
|
835
|
+
//#region src/generators/TopicGenerator.ts
|
|
836
|
+
/**
|
|
837
|
+
* Discovers `t` topics into `manifest.topics`. A topic is a *resource*, not a
|
|
838
|
+
* function — it has no handler to generate. Producers reach it via its derived
|
|
839
|
+
* publisher (`topic.publisher`, whose connection string is sniffed onto the
|
|
840
|
+
* producing construct) and subscribers bind via `s.topic(topic)`; this generator
|
|
841
|
+
* only records the topic + its event contract so infra can provision the SNS
|
|
842
|
+
* topic. Identical output for every provider (locally pg-boss routes by event
|
|
843
|
+
* name, so there's nothing to run).
|
|
844
|
+
*/
|
|
845
|
+
var TopicGenerator = class extends require_openapi.ConstructGenerator {
|
|
846
|
+
isConstruct(value) {
|
|
847
|
+
return __geekmidas_constructs_topic.Topic.isTopic(value);
|
|
848
|
+
}
|
|
849
|
+
async build(_context, constructs, _outputDir, _options) {
|
|
850
|
+
const logger$13 = console;
|
|
851
|
+
const topicInfos = constructs.map(({ construct }) => ({
|
|
852
|
+
name: construct.name,
|
|
853
|
+
events: construct.eventTypes
|
|
854
|
+
}));
|
|
855
|
+
for (const topic of topicInfos) logger$13.log(`Discovered topic: ${topic.name}`);
|
|
856
|
+
return topicInfos;
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
|
|
657
860
|
//#endregion
|
|
658
861
|
//#region src/exec/index.ts
|
|
659
862
|
const logger$11 = console;
|
|
@@ -1321,11 +1524,15 @@ async function buildServer(config, context, provider, enableOpenApi, appRoot = p
|
|
|
1321
1524
|
const functionGenerator = new FunctionGenerator();
|
|
1322
1525
|
const cronGenerator = new CronGenerator();
|
|
1323
1526
|
const subscriberGenerator = new SubscriberGenerator();
|
|
1324
|
-
const
|
|
1527
|
+
const queueGenerator = new QueueGenerator();
|
|
1528
|
+
const topicGenerator = new TopicGenerator();
|
|
1529
|
+
const [allEndpoints, allFunctions, allCrons, allSubscribers, allQueues, allTopics] = await Promise.all([
|
|
1325
1530
|
endpointGenerator.load(config.routes, appRoot, bustCache),
|
|
1326
1531
|
config.functions ? functionGenerator.load(config.functions, appRoot, bustCache) : [],
|
|
1327
1532
|
config.crons ? cronGenerator.load(config.crons, appRoot, bustCache) : [],
|
|
1328
|
-
config.subscribers ? subscriberGenerator.load(config.subscribers, appRoot, bustCache) : []
|
|
1533
|
+
config.subscribers ? subscriberGenerator.load(config.subscribers, appRoot, bustCache) : [],
|
|
1534
|
+
config.queues ? queueGenerator.load(config.queues, appRoot, bustCache) : [],
|
|
1535
|
+
config.topics ? topicGenerator.load(config.topics, appRoot, bustCache) : []
|
|
1329
1536
|
]);
|
|
1330
1537
|
const outputDir = (0, node_path.join)(appRoot, ".gkm", provider);
|
|
1331
1538
|
await (0, node_fs_promises.mkdir)(outputDir, { recursive: true });
|
|
@@ -1336,7 +1543,9 @@ async function buildServer(config, context, provider, enableOpenApi, appRoot = p
|
|
|
1336
1543
|
}),
|
|
1337
1544
|
functionGenerator.build(context, allFunctions, outputDir, { provider }),
|
|
1338
1545
|
cronGenerator.build(context, allCrons, outputDir, { provider }),
|
|
1339
|
-
subscriberGenerator.build(context, allSubscribers, outputDir, { provider })
|
|
1546
|
+
subscriberGenerator.build(context, allSubscribers, outputDir, { provider }),
|
|
1547
|
+
queueGenerator.build(context, allQueues, outputDir, { provider }),
|
|
1548
|
+
topicGenerator.build(context, allTopics, outputDir, { provider })
|
|
1340
1549
|
]);
|
|
1341
1550
|
}
|
|
1342
1551
|
/**
|
|
@@ -1667,7 +1876,7 @@ function generateDerivedType(fieldName, typeName, partitioned) {
|
|
|
1667
1876
|
}
|
|
1668
1877
|
return `export type ${typeName} = (typeof manifest.${fieldName})[number];`;
|
|
1669
1878
|
}
|
|
1670
|
-
async function generateAwsManifest(outputDir, routes, functions, crons, subscribers) {
|
|
1879
|
+
async function generateAwsManifest(outputDir, routes, functions, crons, subscribers, queues = [], topics = []) {
|
|
1671
1880
|
const manifestDir = (0, node_path.join)(outputDir, "manifest");
|
|
1672
1881
|
await (0, node_fs_promises.mkdir)(manifestDir, { recursive: true });
|
|
1673
1882
|
const awsRoutes = filterAllRoutes(routes);
|
|
@@ -1675,11 +1884,15 @@ async function generateAwsManifest(outputDir, routes, functions, crons, subscrib
|
|
|
1675
1884
|
const functionsPartitioned = isPartitioned(functions);
|
|
1676
1885
|
const cronsPartitioned = isPartitioned(crons);
|
|
1677
1886
|
const subscribersPartitioned = isPartitioned(subscribers);
|
|
1887
|
+
const queuesPartitioned = isPartitioned(queues);
|
|
1888
|
+
const topicsPartitioned = isPartitioned(topics);
|
|
1678
1889
|
const content = `export const manifest = {
|
|
1679
1890
|
routes: ${serializeField(awsRoutes)},
|
|
1680
1891
|
functions: ${serializeField(functions)},
|
|
1681
1892
|
crons: ${serializeField(crons)},
|
|
1682
1893
|
subscribers: ${serializeField(subscribers)},
|
|
1894
|
+
queues: ${serializeField(queues)},
|
|
1895
|
+
topics: ${serializeField(topics)},
|
|
1683
1896
|
} as const;
|
|
1684
1897
|
|
|
1685
1898
|
// Derived types
|
|
@@ -1687,6 +1900,8 @@ ${generateDerivedType("routes", "Route", routesPartitioned)}
|
|
|
1687
1900
|
${generateDerivedType("functions", "Function", functionsPartitioned)}
|
|
1688
1901
|
${generateDerivedType("crons", "Cron", cronsPartitioned)}
|
|
1689
1902
|
${generateDerivedType("subscribers", "Subscriber", subscribersPartitioned)}
|
|
1903
|
+
${generateDerivedType("queues", "Queue", queuesPartitioned)}
|
|
1904
|
+
${generateDerivedType("topics", "Topic", topicsPartitioned)}
|
|
1690
1905
|
|
|
1691
1906
|
// Useful union types
|
|
1692
1907
|
export type Authorizer = Route['authorizer'];
|
|
@@ -1695,25 +1910,33 @@ export type RoutePath = Route['path'];
|
|
|
1695
1910
|
`;
|
|
1696
1911
|
const manifestPath = (0, node_path.join)(manifestDir, "aws.ts");
|
|
1697
1912
|
await (0, node_fs_promises.writeFile)(manifestPath, content);
|
|
1698
|
-
logger$9.log(`Generated AWS manifest with ${countItems(awsRoutes)} routes, ${countItems(functions)} functions, ${countItems(crons)} crons, ${countItems(subscribers)} subscribers`);
|
|
1913
|
+
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`);
|
|
1699
1914
|
logger$9.log(`Manifest: ${(0, node_path.relative)(process.cwd(), manifestPath)}`);
|
|
1700
1915
|
}
|
|
1701
|
-
async function generateServerManifest(outputDir, appInfo, routes, subscribers) {
|
|
1916
|
+
async function generateServerManifest(outputDir, appInfo, routes, subscribers, queues = [], topics = []) {
|
|
1702
1917
|
const manifestDir = (0, node_path.join)(outputDir, "manifest");
|
|
1703
1918
|
await (0, node_fs_promises.mkdir)(manifestDir, { recursive: true });
|
|
1704
1919
|
const serverRoutes = mapRouteMetadata(filterAllRoutes(routes));
|
|
1705
1920
|
const serverSubscribers = mapSubscriberMetadata(subscribers);
|
|
1921
|
+
const serverQueues = mapQueueMetadata(queues);
|
|
1922
|
+
const serverTopics = mapTopicMetadata(topics);
|
|
1706
1923
|
const routesPartitioned = isPartitioned(serverRoutes);
|
|
1707
1924
|
const subscribersPartitioned = isPartitioned(serverSubscribers);
|
|
1925
|
+
const queuesPartitioned = isPartitioned(serverQueues);
|
|
1926
|
+
const topicsPartitioned = isPartitioned(serverTopics);
|
|
1708
1927
|
const content = `export const manifest = {
|
|
1709
1928
|
app: ${JSON.stringify(appInfo, null, 2)},
|
|
1710
1929
|
routes: ${serializeField(serverRoutes)},
|
|
1711
1930
|
subscribers: ${serializeField(serverSubscribers)},
|
|
1931
|
+
queues: ${serializeField(serverQueues)},
|
|
1932
|
+
topics: ${serializeField(serverTopics)},
|
|
1712
1933
|
} as const;
|
|
1713
1934
|
|
|
1714
1935
|
// Derived types
|
|
1715
1936
|
${generateDerivedType("routes", "Route", routesPartitioned)}
|
|
1716
1937
|
${generateDerivedType("subscribers", "Subscriber", subscribersPartitioned)}
|
|
1938
|
+
${generateDerivedType("queues", "Queue", queuesPartitioned)}
|
|
1939
|
+
${generateDerivedType("topics", "Topic", topicsPartitioned)}
|
|
1717
1940
|
|
|
1718
1941
|
// Useful union types
|
|
1719
1942
|
export type Authorizer = Route['authorizer'];
|
|
@@ -1722,7 +1945,7 @@ export type RoutePath = Route['path'];
|
|
|
1722
1945
|
`;
|
|
1723
1946
|
const manifestPath = (0, node_path.join)(manifestDir, "server.ts");
|
|
1724
1947
|
await (0, node_fs_promises.writeFile)(manifestPath, content);
|
|
1725
|
-
logger$9.log(`Generated server manifest with ${countItems(serverRoutes)} routes, ${countItems(serverSubscribers)} subscribers`);
|
|
1948
|
+
logger$9.log(`Generated server manifest with ${countItems(serverRoutes)} routes, ${countItems(serverSubscribers)} subscribers, ${countItems(serverQueues)} queues, ${countItems(serverTopics)} topics`);
|
|
1726
1949
|
logger$9.log(`Manifest: ${(0, node_path.relative)(process.cwd(), manifestPath)}`);
|
|
1727
1950
|
}
|
|
1728
1951
|
/**
|
|
@@ -1761,6 +1984,23 @@ function mapSubscriberMetadata(subscribers) {
|
|
|
1761
1984
|
for (const [partition, partitionSubs] of Object.entries(subscribers)) result[partition] = partitionSubs.map(mapFn);
|
|
1762
1985
|
return result;
|
|
1763
1986
|
}
|
|
1987
|
+
function mapQueueMetadata(queues) {
|
|
1988
|
+
const mapFn = (q) => ({ name: q.name });
|
|
1989
|
+
if (Array.isArray(queues)) return queues.map(mapFn);
|
|
1990
|
+
const result = {};
|
|
1991
|
+
for (const [partition, partitionQueues] of Object.entries(queues)) result[partition] = partitionQueues.map(mapFn);
|
|
1992
|
+
return result;
|
|
1993
|
+
}
|
|
1994
|
+
function mapTopicMetadata(topics) {
|
|
1995
|
+
const mapFn = (t) => ({
|
|
1996
|
+
name: t.name,
|
|
1997
|
+
events: t.events
|
|
1998
|
+
});
|
|
1999
|
+
if (Array.isArray(topics)) return topics.map(mapFn);
|
|
2000
|
+
const result = {};
|
|
2001
|
+
for (const [partition, partitionTopics] of Object.entries(topics)) result[partition] = partitionTopics.map(mapFn);
|
|
2002
|
+
return result;
|
|
2003
|
+
}
|
|
1764
2004
|
|
|
1765
2005
|
//#endregion
|
|
1766
2006
|
//#region src/build/partitions.ts
|
|
@@ -1846,47 +2086,57 @@ async function buildCommand(options) {
|
|
|
1846
2086
|
const functionGenerator = new FunctionGenerator();
|
|
1847
2087
|
const cronGenerator = new CronGenerator();
|
|
1848
2088
|
const subscriberGenerator = new SubscriberGenerator();
|
|
1849
|
-
const
|
|
2089
|
+
const queueGenerator = new QueueGenerator();
|
|
2090
|
+
const topicGenerator = new TopicGenerator();
|
|
2091
|
+
const [allEndpoints, allFunctions, allCrons, allSubscribers, allQueues, allTopics] = await Promise.all([
|
|
1850
2092
|
endpointGenerator.load(config.routes),
|
|
1851
2093
|
config.functions ? functionGenerator.load(config.functions) : [],
|
|
1852
2094
|
config.crons ? cronGenerator.load(config.crons) : [],
|
|
1853
|
-
config.subscribers ? subscriberGenerator.load(config.subscribers) : []
|
|
2095
|
+
config.subscribers ? subscriberGenerator.load(config.subscribers) : [],
|
|
2096
|
+
config.queues ? queueGenerator.load(config.queues) : [],
|
|
2097
|
+
config.topics ? topicGenerator.load(config.topics) : []
|
|
1854
2098
|
]);
|
|
1855
2099
|
logger$8.log(`Found ${allEndpoints.length} endpoints`);
|
|
1856
2100
|
logger$8.log(`Found ${allFunctions.length} functions`);
|
|
1857
2101
|
logger$8.log(`Found ${allCrons.length} crons`);
|
|
1858
2102
|
logger$8.log(`Found ${allSubscribers.length} subscribers`);
|
|
1859
|
-
|
|
1860
|
-
|
|
2103
|
+
logger$8.log(`Found ${allQueues.length} queues`);
|
|
2104
|
+
logger$8.log(`Found ${allTopics.length} topics`);
|
|
2105
|
+
if (allEndpoints.length === 0 && allFunctions.length === 0 && allCrons.length === 0 && allSubscribers.length === 0 && allQueues.length === 0 && allTopics.length === 0) {
|
|
2106
|
+
logger$8.log("No endpoints, functions, crons, subscribers, queues, or topics found to process");
|
|
1861
2107
|
return {};
|
|
1862
2108
|
}
|
|
1863
2109
|
const rootOutputDir = (0, node_path.join)(process.cwd(), ".gkm");
|
|
1864
2110
|
await (0, node_fs_promises.mkdir)(rootOutputDir, { recursive: true });
|
|
1865
2111
|
let result = {};
|
|
1866
2112
|
for (const provider of resolved.providers) {
|
|
1867
|
-
const providerResult = await buildForProvider(provider, buildContext, rootOutputDir, endpointGenerator, functionGenerator, cronGenerator, subscriberGenerator, allEndpoints, allFunctions, allCrons, allSubscribers, resolved.enableOpenApi, options.skipBundle ?? false, options.stage);
|
|
2113
|
+
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);
|
|
1868
2114
|
if (providerResult.masterKey) result = providerResult;
|
|
1869
2115
|
}
|
|
1870
2116
|
await require_openapi.generateOpenApi(config, { bustCache: true });
|
|
1871
2117
|
return result;
|
|
1872
2118
|
}
|
|
1873
|
-
async function buildForProvider(provider, context, rootOutputDir, endpointGenerator, functionGenerator, cronGenerator, subscriberGenerator, endpoints, functions, crons, subscribers, enableOpenApi, skipBundle, stage) {
|
|
2119
|
+
async function buildForProvider(provider, context, rootOutputDir, endpointGenerator, functionGenerator, cronGenerator, subscriberGenerator, queueGenerator, topicGenerator, endpoints, functions, crons, subscribers, queues, topics, enableOpenApi, skipBundle, stage) {
|
|
1874
2120
|
const outputDir = (0, node_path.join)(process.cwd(), ".gkm", provider);
|
|
1875
2121
|
await (0, node_fs_promises.mkdir)(outputDir, { recursive: true });
|
|
1876
2122
|
logger$8.log(`\nGenerating handlers for provider: ${provider}`);
|
|
1877
|
-
const [routes, functionInfos, cronInfos, subscriberInfos] = await Promise.all([
|
|
2123
|
+
const [routes, functionInfos, cronInfos, subscriberInfos, queueInfos, topicInfos] = await Promise.all([
|
|
1878
2124
|
endpointGenerator.build(context, endpoints, outputDir, {
|
|
1879
2125
|
provider,
|
|
1880
2126
|
enableOpenApi
|
|
1881
2127
|
}),
|
|
1882
2128
|
functionGenerator.build(context, functions, outputDir, { provider }),
|
|
1883
2129
|
cronGenerator.build(context, crons, outputDir, { provider }),
|
|
1884
|
-
subscriberGenerator.build(context, subscribers, outputDir, { provider })
|
|
2130
|
+
subscriberGenerator.build(context, subscribers, outputDir, { provider }),
|
|
2131
|
+
queueGenerator.build(context, queues, outputDir, { provider }),
|
|
2132
|
+
topicGenerator.build(context, topics, outputDir, { provider })
|
|
1885
2133
|
]);
|
|
1886
|
-
logger$8.log(`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers for ${provider}`);
|
|
2134
|
+
logger$8.log(`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers, ${queueInfos.length} queues, ${topicInfos.length} topics for ${provider}`);
|
|
1887
2135
|
const manifestRoutes = assembleManifestField(routes, endpoints);
|
|
1888
2136
|
const manifestFunctions = assembleManifestField(functionInfos, functions);
|
|
1889
2137
|
const manifestCrons = assembleManifestField(cronInfos, crons);
|
|
2138
|
+
const manifestQueues = assembleManifestField(queueInfos, queues);
|
|
2139
|
+
const manifestTopics = assembleManifestField(topicInfos, topics);
|
|
1890
2140
|
const manifestSubscribers = assembleManifestField(subscriberInfos, subscribers);
|
|
1891
2141
|
if (provider === "server") {
|
|
1892
2142
|
const routeMetadata = await Promise.all(endpoints.map(async ({ construct }) => ({
|
|
@@ -1900,7 +2150,7 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
1900
2150
|
handler: (0, node_path.relative)(process.cwd(), (0, node_path.join)(outputDir, "app.ts")),
|
|
1901
2151
|
endpoints: (0, node_path.relative)(process.cwd(), (0, node_path.join)(outputDir, "endpoints.ts"))
|
|
1902
2152
|
};
|
|
1903
|
-
await generateServerManifest(rootOutputDir, appInfo, serverRouteField, manifestSubscribers);
|
|
2153
|
+
await generateServerManifest(rootOutputDir, appInfo, serverRouteField, manifestSubscribers, manifestQueues, manifestTopics);
|
|
1904
2154
|
let masterKey;
|
|
1905
2155
|
if (context.production?.bundle && !skipBundle) {
|
|
1906
2156
|
logger$8.log(`\n📦 Bundling production server...`);
|
|
@@ -1909,7 +2159,8 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
1909
2159
|
...endpoints.map((e) => e.construct),
|
|
1910
2160
|
...functions.map((f) => f.construct),
|
|
1911
2161
|
...crons.map((c) => c.construct),
|
|
1912
|
-
...subscribers.map((s) => s.construct)
|
|
2162
|
+
...subscribers.map((s) => s.construct),
|
|
2163
|
+
...queues.map((q) => q.construct)
|
|
1913
2164
|
];
|
|
1914
2165
|
const dockerServices = context.dockerServices;
|
|
1915
2166
|
const bundleResult = await bundleServer({
|
|
@@ -1930,7 +2181,7 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
1930
2181
|
}
|
|
1931
2182
|
}
|
|
1932
2183
|
return { masterKey };
|
|
1933
|
-
} else await generateAwsManifest(rootOutputDir, manifestRoutes, manifestFunctions, manifestCrons, manifestSubscribers);
|
|
2184
|
+
} else await generateAwsManifest(rootOutputDir, manifestRoutes, manifestFunctions, manifestCrons, manifestSubscribers, manifestQueues, manifestTopics);
|
|
1934
2185
|
return {};
|
|
1935
2186
|
}
|
|
1936
2187
|
/**
|
|
@@ -6647,22 +6898,22 @@ const GEEKMIDAS_VERSIONS = {
|
|
|
6647
6898
|
"@geekmidas/audit": "~2.0.1",
|
|
6648
6899
|
"@geekmidas/auth": "~2.0.1",
|
|
6649
6900
|
"@geekmidas/cache": "~1.1.1",
|
|
6650
|
-
"@geekmidas/client": "~
|
|
6651
|
-
"@geekmidas/cloud": "~1.0
|
|
6652
|
-
"@geekmidas/constructs": "~
|
|
6901
|
+
"@geekmidas/client": "~7.0.0",
|
|
6902
|
+
"@geekmidas/cloud": "~1.1.0",
|
|
6903
|
+
"@geekmidas/constructs": "~5.0.0",
|
|
6653
6904
|
"@geekmidas/db": "~1.0.2",
|
|
6654
6905
|
"@geekmidas/emailkit": "~1.0.1",
|
|
6655
|
-
"@geekmidas/envkit": "~1.0
|
|
6906
|
+
"@geekmidas/envkit": "~1.1.0",
|
|
6656
6907
|
"@geekmidas/errors": "~1.0.1",
|
|
6657
|
-
"@geekmidas/events": "~1.1.
|
|
6908
|
+
"@geekmidas/events": "~1.1.5",
|
|
6658
6909
|
"@geekmidas/logger": "~1.0.2",
|
|
6659
|
-
"@geekmidas/rate-limit": "~
|
|
6660
|
-
"@geekmidas/schema": "~1.0.
|
|
6661
|
-
"@geekmidas/services": "~
|
|
6910
|
+
"@geekmidas/rate-limit": "~4.0.0",
|
|
6911
|
+
"@geekmidas/schema": "~1.0.3",
|
|
6912
|
+
"@geekmidas/services": "~2.0.0",
|
|
6662
6913
|
"@geekmidas/storage": "~2.0.3",
|
|
6663
6914
|
"@geekmidas/studio": "~1.0.1",
|
|
6664
6915
|
"@geekmidas/telescope": "~1.0.1",
|
|
6665
|
-
"@geekmidas/testkit": "~
|
|
6916
|
+
"@geekmidas/testkit": "~3.0.0",
|
|
6666
6917
|
"@geekmidas/cli": CLI_VERSION
|
|
6667
6918
|
};
|
|
6668
6919
|
|
|
@@ -9710,7 +9961,8 @@ import path from 'node:path';
|
|
|
9710
9961
|
import { Credentials } from '@geekmidas/envkit/credentials';
|
|
9711
9962
|
import { PostgresKyselyMigrator } from '@geekmidas/testkit/kysely';
|
|
9712
9963
|
import { runInitScript } from '@geekmidas/testkit/postgres';
|
|
9713
|
-
import {
|
|
9964
|
+
import { Kysely, PostgresDialect } from 'kysely';
|
|
9965
|
+
import { FileMigrationProvider } from 'kysely/migration';
|
|
9714
9966
|
import pg from 'pg';
|
|
9715
9967
|
|
|
9716
9968
|
export default async function globalSetup() {
|