@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.
Files changed (42) hide show
  1. package/CHANGELOG.md +155 -0
  2. package/dist/config.d.cts +2 -2
  3. package/dist/config.d.mts +2 -2
  4. package/dist/{index-Dx9Kk5bR.d.mts → index-Bw548Bta.d.mts} +2 -2
  5. package/dist/{index-Dx9Kk5bR.d.mts.map → index-Bw548Bta.d.mts.map} +1 -1
  6. package/dist/{index-Dz2a7xQU.d.cts → index-DTpkeFb8.d.cts} +2 -2
  7. package/dist/{index-Dz2a7xQU.d.cts.map → index-DTpkeFb8.d.cts.map} +1 -1
  8. package/dist/index.cjs +283 -31
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.mjs +283 -31
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/{openapi-CG1LBk7s.cjs → openapi-BzNOC7_i.cjs} +14 -2
  13. package/dist/openapi-BzNOC7_i.cjs.map +1 -0
  14. package/dist/{openapi-CtRVU6RD.mjs → openapi-Cfwi0fqF.mjs} +14 -2
  15. package/dist/openapi-Cfwi0fqF.mjs.map +1 -0
  16. package/dist/openapi.cjs +1 -1
  17. package/dist/openapi.d.cts +1 -1
  18. package/dist/openapi.d.mts +1 -1
  19. package/dist/openapi.mjs +1 -1
  20. package/dist/{types-CqfhdmRi.d.mts → types-B3GQUg3k.d.mts} +3 -1
  21. package/dist/{types-CqfhdmRi.d.mts.map → types-B3GQUg3k.d.mts.map} +1 -1
  22. package/dist/{types-DdHfUbxk.d.cts → types-BWQDiooe.d.cts} +3 -1
  23. package/dist/{types-DdHfUbxk.d.cts.map → types-BWQDiooe.d.cts.map} +1 -1
  24. package/dist/workspace/index.d.cts +2 -2
  25. package/dist/workspace/index.d.mts +2 -2
  26. package/package.json +8 -7
  27. package/src/build/__tests__/index-new.spec.ts +3 -1
  28. package/src/build/__tests__/manifests.spec.ts +2 -2
  29. package/src/build/index.ts +61 -21
  30. package/src/build/manifests.ts +62 -5
  31. package/src/dev/index.ts +25 -11
  32. package/src/generators/EndpointGenerator.ts +14 -2
  33. package/src/generators/QueueGenerator.ts +245 -0
  34. package/src/generators/SubscriberGenerator.ts +5 -0
  35. package/src/generators/TopicGenerator.ts +45 -0
  36. package/src/generators/__tests__/QueueGenerator.spec.ts +141 -0
  37. package/src/generators/index.ts +2 -0
  38. package/src/init/generators/test.ts +2 -1
  39. package/src/init/versions.ts +9 -9
  40. package/src/types.ts +14 -51
  41. package/dist/openapi-CG1LBk7s.cjs.map +0 -1
  42. package/dist/openapi-CtRVU6RD.mjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -6,7 +6,7 @@ import { getCredentialsPath, getDokployCredentials, getDokployRegistryId, getDok
6
6
  import { createStageSecrets, generateDbPassword, generateDbUrl, generateFullstackCustomSecrets, rotateServicePassword } from "./fullstack-secrets-l8s3II_H.mjs";
7
7
  import { getKeyPath, maskPassword, readStageSecrets, secretsExist, setCustomSecret, toEmbeddableSecrets, writeStageSecrets } from "./storage-dbb9RyBl.mjs";
8
8
  import { createCredentialsPreload, createEntryWrapper, findAvailablePort, isPortAvailable, loadEnvFiles, loadSecretsForApp, prepareEntryCredentials, resolveServicePorts, rewriteUrlsWithPorts, setupCommand, startWorkspaceServices } from "./setup-DDXUXzzl.mjs";
9
- import { ConstructGenerator, EndpointGenerator, OPENAPI_OUTPUT_PATH, generateOpenApi, isPartitionedRoutes, normalizeRoutes, openapiCommand, resolveOpenApiConfig } from "./openapi-CtRVU6RD.mjs";
9
+ import { ConstructGenerator, EndpointGenerator, OPENAPI_OUTPUT_PATH, generateOpenApi, isPartitionedRoutes, normalizeRoutes, openapiCommand, resolveOpenApiConfig } from "./openapi-Cfwi0fqF.mjs";
10
10
  import { DokployApi } from "./dokploy-api-sfZFlWuE.mjs";
11
11
  import { encryptSecrets } from "./encryption-CO5Q4Bhf.mjs";
12
12
  import { CachedStateProvider } from "./CachedStateProvider-CzyFct42.mjs";
@@ -26,7 +26,9 @@ import { parse as parse$1 } from "yaml";
26
26
  import { randomBytes } from "node:crypto";
27
27
  import { Cron } from "@geekmidas/constructs/crons";
28
28
  import { Function } from "@geekmidas/constructs/functions";
29
+ import { Queue } from "@geekmidas/constructs/queue";
29
30
  import { Subscriber } from "@geekmidas/constructs/subscribers";
31
+ import { Topic } from "@geekmidas/constructs/topic";
30
32
  import { fileURLToPath, pathToFileURL } from "node:url";
31
33
  import { Client } from "pg";
32
34
  import { lookup } from "node:dns/promises";
@@ -34,7 +36,7 @@ import prompts from "prompts";
34
36
 
35
37
  //#region package.json
36
38
  var name = "@geekmidas/cli";
37
- var version = "1.10.41";
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 ConstructGenerator {
479
+ isConstruct(value) {
480
+ return 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 = join(outputDir, "queues");
493
+ await 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: 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 = join(outputDir, `${exportName}.ts`);
510
+ const importPath = relative(dirname(handlerPath), sourceFile).replace(/\.ts$/, ".js");
511
+ const relativeEnvParserPath = relative(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 writeFile(handlerPath, content);
521
+ return handlerPath;
522
+ }
523
+ async generateServerQueuesFile(outputDir, queues) {
524
+ await mkdir(outputDir, { recursive: true });
525
+ const queuesPath = join(outputDir, "queues.ts");
526
+ const importsByFile = /* @__PURE__ */ new Map();
527
+ for (const { path, key } of queues) {
528
+ const importPath = relative(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$1]) => `import { ${exports$1.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 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 ConstructGenerator {
@@ -486,6 +659,10 @@ var SubscriberGenerator = class extends ConstructGenerator {
486
659
  name: key,
487
660
  handler: 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 ConstructGenerator {
846
+ isConstruct(value) {
847
+ return 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 [allEndpoints, allFunctions, allCrons, allSubscribers] = await Promise.all([
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 = join(appRoot, ".gkm", provider);
1331
1538
  await 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 = join(outputDir, "manifest");
1672
1881
  await 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 = join(manifestDir, "aws.ts");
1697
1912
  await 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: ${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 = join(outputDir, "manifest");
1703
1918
  await 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 = join(manifestDir, "server.ts");
1724
1947
  await 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: ${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 [allEndpoints, allFunctions, allCrons, allSubscribers] = await Promise.all([
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
- if (allEndpoints.length === 0 && allFunctions.length === 0 && allCrons.length === 0 && allSubscribers.length === 0) {
1860
- logger$8.log("No endpoints, functions, crons, or subscribers found to process");
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 = join(process.cwd(), ".gkm");
1864
2110
  await 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 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 = join(process.cwd(), ".gkm", provider);
1875
2121
  await 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: relative(process.cwd(), join(outputDir, "app.ts")),
1901
2151
  endpoints: relative(process.cwd(), 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": "~6.0.0",
6651
- "@geekmidas/cloud": "~1.0.1",
6652
- "@geekmidas/constructs": "~4.0.0",
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.7",
6906
+ "@geekmidas/envkit": "~1.1.0",
6656
6907
  "@geekmidas/errors": "~1.0.1",
6657
- "@geekmidas/events": "~1.1.4",
6908
+ "@geekmidas/events": "~1.1.5",
6658
6909
  "@geekmidas/logger": "~1.0.2",
6659
- "@geekmidas/rate-limit": "~3.0.0",
6660
- "@geekmidas/schema": "~1.0.2",
6661
- "@geekmidas/services": "~1.1.1",
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": "~2.0.0",
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 { FileMigrationProvider, Kysely, PostgresDialect } from 'kysely';
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() {