kavoru 0.7.0 → 0.8.1

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/features.ts +57 -32
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kavoru",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "Scaffold a new Kavoru (Elysia + Bun) backend from the official template",
5
5
  "type": "module",
6
6
  "bin": {
package/src/features.ts CHANGED
@@ -108,7 +108,7 @@ const FEATURE_PATHS: Record<FeatureId, string[]> = {
108
108
  ],
109
109
  resend: ["src/infra/resend"],
110
110
  cron: ["src/schedules"],
111
- docker: ["Dockerfile", "docker-compose.yaml", "docker/otel.Dockerfile"],
111
+ docker: ["docker-compose.yaml", "docker"],
112
112
  };
113
113
 
114
114
  const FEATURE_DEPENDENCIES: Partial<
@@ -276,11 +276,14 @@ async function patchModulesIndex(
276
276
  await writeText(projectDir, relativePath, content);
277
277
  }
278
278
 
279
- async function patchEntryIndex(projectDir: string, selection: FeatureSelection) {
279
+ export function buildEntryIndex(selection: FeatureSelection): string {
280
280
  const imports = [
281
281
  selection.sentry
282
282
  ? 'import { initSentry, flushSentry } from "./infra/sentry";'
283
283
  : null,
284
+ selection.otel
285
+ ? 'import {\n bootstrapOpenTelemetry,\n shutdownOpenTelemetry,\n} from "./infra/telemetry";'
286
+ : null,
284
287
  selection.kafka
285
288
  ? 'import { startKafka, stopKafka } from "./infra/kafka";'
286
289
  : null,
@@ -291,8 +294,11 @@ async function patchEntryIndex(projectDir: string, selection: FeatureSelection)
291
294
 
292
295
  const body: string[] = [];
293
296
 
297
+ if (selection.otel) {
298
+ body.push("", "bootstrapOpenTelemetry();");
299
+ }
294
300
  if (selection.sentry) {
295
- body.push("", "initSentry();");
301
+ body.push("initSentry();");
296
302
  }
297
303
 
298
304
  body.push("", "const server = new HttpServer();", "");
@@ -326,6 +332,9 @@ async function patchEntryIndex(projectDir: string, selection: FeatureSelection)
326
332
  if (selection.sentry) {
327
333
  body.push(" await flushSentry();");
328
334
  }
335
+ if (selection.otel) {
336
+ body.push(" await shutdownOpenTelemetry();");
337
+ }
329
338
 
330
339
  body.push(
331
340
  " process.exit(0);",
@@ -337,7 +346,11 @@ async function patchEntryIndex(projectDir: string, selection: FeatureSelection)
337
346
  "",
338
347
  );
339
348
 
340
- await writeText(projectDir, "src/index.ts", [...imports, ...body].join("\n"));
349
+ return [...imports, ...body].join("\n");
350
+ }
351
+
352
+ async function patchEntryIndex(projectDir: string, selection: FeatureSelection) {
353
+ await writeText(projectDir, "src/index.ts", buildEntryIndex(selection));
341
354
  }
342
355
 
343
356
  async function patchServerIndex(projectDir: string, selection: FeatureSelection) {
@@ -497,7 +510,7 @@ async function patchEnvExample(
497
510
  async function patchDockerfile(projectDir: string, selection: FeatureSelection) {
498
511
  if (!selection.docker) return;
499
512
 
500
- const relativePath = "Dockerfile";
513
+ const relativePath = "docker/app/Dockerfile";
501
514
  const current = await readText(projectDir, relativePath);
502
515
  if (!current) return;
503
516
 
@@ -517,19 +530,21 @@ async function patchDockerfile(projectDir: string, selection: FeatureSelection)
517
530
  await writeText(projectDir, relativePath, content);
518
531
  }
519
532
 
520
- function buildAppEnvironment(selection: FeatureSelection): string {
521
- const lines = [" NODE_ENV: production"];
533
+ function buildDockerAppEnv(selection: FeatureSelection): string {
534
+ const lines = [
535
+ "# Docker-only overrides (loaded after root .env)",
536
+ "NODE_ENV=production",
537
+ ];
522
538
  if (selection.kafka) {
523
- lines.push(" KAFKA_BROKERS: kafka:9092");
539
+ lines.push("KAFKA_BROKERS=kafka:9092");
524
540
  }
525
541
  if (selection.otel) {
526
- lines.push(" OTEL_EXPORTER_OTLP_ENDPOINT: http://otel:4318/v1/traces");
527
- lines.push(" OTEL_SERVICE_NAME: ${OTEL_SERVICE_NAME:-kavoru}");
542
+ lines.push("OTEL_EXPORTER_OTLP_ENDPOINT=http://otel:4318/v1/traces");
528
543
  }
529
544
  if (selection.sentry) {
530
- lines.push(" SENTRY_SPOTLIGHT: http://spotlight:8969/stream");
545
+ lines.push("SENTRY_SPOTLIGHT=http://spotlight:8969/stream");
531
546
  }
532
- return ` environment:\n${lines.join("\n")}\n`;
547
+ return `${lines.join("\n")}\n`;
533
548
  }
534
549
 
535
550
  function generateDockerCompose(selection: FeatureSelection): string {
@@ -539,29 +554,17 @@ function generateDockerCompose(selection: FeatureSelection): string {
539
554
  condition: service_started
540
555
  `
541
556
  : "";
542
- const appEnvironment = buildAppEnvironment(selection);
543
557
 
544
558
  const kafkaService = selection.kafka
545
559
  ? `
546
560
  kafka:
547
- image: confluentinc/cp-kafka:7.6.1
561
+ build:
562
+ context: docker/kafka
548
563
  hostname: kafka
549
564
  ports:
550
565
  - "9094:9094"
551
- environment:
552
- CLUSTER_ID: MkU3OEVBNTcwNTJENDM2Qk
553
- KAFKA_NODE_ID: "0"
554
- KAFKA_PROCESS_ROLES: broker,controller
555
- KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094
556
- KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,EXTERNAL://localhost:9094
557
- KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,EXTERNAL:PLAINTEXT
558
- KAFKA_CONTROLLER_QUORUM_VOTERS: 0@kafka:9093
559
- KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
560
- KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
561
- KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
562
- KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
563
- KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
564
- KAFKA_LOG_DIRS: /tmp/kraft-combined-logs
566
+ env_file:
567
+ - docker/kafka/.env
565
568
  networks:
566
569
  - app_network
567
570
  restart: unless-stopped
@@ -572,10 +575,11 @@ function generateDockerCompose(selection: FeatureSelection): string {
572
575
  ? `
573
576
  otel:
574
577
  build:
575
- context: .
576
- dockerfile: docker/otel.Dockerfile
578
+ context: docker/otel
577
579
  ports:
578
580
  - "4318:4318"
581
+ env_file:
582
+ - docker/otel/.env
579
583
  networks:
580
584
  - app_network
581
585
  restart: unless-stopped
@@ -585,9 +589,12 @@ function generateDockerCompose(selection: FeatureSelection): string {
585
589
  const spotlightService = selection.sentry
586
590
  ? `
587
591
  spotlight:
588
- image: ghcr.io/getsentry/spotlight:latest
592
+ build:
593
+ context: docker/spotlight
589
594
  ports:
590
595
  - "8969:8969"
596
+ env_file:
597
+ - docker/spotlight/.env
591
598
  networks:
592
599
  - app_network
593
600
  restart: unless-stopped
@@ -598,6 +605,7 @@ function generateDockerCompose(selection: FeatureSelection): string {
598
605
  app:
599
606
  build:
600
607
  context: .
608
+ dockerfile: docker/app/Dockerfile
601
609
  target: build
602
610
  args:
603
611
  PORT: \${PORT:-3131}
@@ -615,7 +623,8 @@ function generateDockerCompose(selection: FeatureSelection): string {
615
623
  restart: unless-stopped
616
624
  env_file:
617
625
  - .env
618
- ${appDependsOn}${appEnvironment} healthcheck:
626
+ - docker/app/.env
627
+ ${appDependsOn} healthcheck:
619
628
  test: ["CMD", "curl", "-f", "http://localhost:\${PORT}/healthz"]
620
629
  interval: 600s
621
630
  timeout: 300s
@@ -633,6 +642,22 @@ async function patchDockerCompose(
633
642
  selection: FeatureSelection,
634
643
  ) {
635
644
  if (!selection.docker) return;
645
+
646
+ if (!selection.kafka) {
647
+ await removePaths(projectDir, ["docker/kafka"]);
648
+ }
649
+ if (!selection.otel) {
650
+ await removePaths(projectDir, ["docker/otel"]);
651
+ }
652
+ if (!selection.sentry) {
653
+ await removePaths(projectDir, ["docker/spotlight"]);
654
+ }
655
+
656
+ await writeText(
657
+ projectDir,
658
+ "docker/app/.env",
659
+ buildDockerAppEnv(selection),
660
+ );
636
661
  await writeText(projectDir, "docker-compose.yaml", generateDockerCompose(selection));
637
662
  }
638
663