create-m5kdev 0.32.8 → 0.34.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 (152) hide show
  1. package/dist/src/catalog.cjs +76 -11
  2. package/dist/src/catalog.cjs.map +1 -1
  3. package/dist/src/catalog.d.cts +13 -1
  4. package/dist/src/create.cjs +6 -2
  5. package/dist/src/create.cjs.map +1 -1
  6. package/dist/src/doctor.cjs +13 -7
  7. package/dist/src/doctor.cjs.map +1 -1
  8. package/dist/src/fs.cjs +10 -5
  9. package/dist/src/fs.cjs.map +1 -1
  10. package/dist/src/prompts.cjs +32 -0
  11. package/dist/src/prompts.cjs.map +1 -1
  12. package/dist/src/prompts.d.cts +7 -1
  13. package/dist/src/template.cjs +23 -4
  14. package/dist/src/template.cjs.map +1 -1
  15. package/dist/src/template.d.cts +17 -3
  16. package/dist/src/types.d.cts +11 -4
  17. package/package.json +4 -1
  18. package/templates/minimal-app/.cursor/rules/module-dto-guide.mdc +1 -1
  19. package/templates/minimal-app/.cursor/rules/module-grants-guide.mdc +3 -3
  20. package/templates/minimal-app/.cursor/rules/module-module-guide.mdc +3 -2
  21. package/templates/minimal-app/.cursor/rules/module-service-guide.mdc +1 -1
  22. package/templates/minimal-app/.dockerignore.tpl +31 -0
  23. package/templates/minimal-app/.gitignore.tpl +1 -0
  24. package/templates/minimal-app/AGENTS.md.tpl +13 -10
  25. package/templates/minimal-app/CLAUDE.md.tpl +132 -131
  26. package/templates/minimal-app/README.md.tpl +50 -19
  27. package/templates/minimal-app/apps/e2e/package.json.tpl +23 -23
  28. package/templates/minimal-app/apps/e2e/playwright.config.ts.tpl +51 -39
  29. package/templates/minimal-app/apps/e2e/playwright.expo.config.ts.tpl +38 -38
  30. package/templates/minimal-app/apps/e2e/servers.ts.tpl +136 -112
  31. package/templates/minimal-app/apps/e2e/tests/conversation.spec.ts.tpl +22 -0
  32. package/templates/minimal-app/apps/e2e/tests/email.spec.ts.tpl +47 -47
  33. package/templates/minimal-app/apps/e2e/tests/expo-helpers.ts.tpl +73 -73
  34. package/templates/minimal-app/apps/e2e/tests/expo-standard.spec.ts.tpl +517 -517
  35. package/templates/minimal-app/apps/e2e/tests/expo-waitlist.spec.ts.tpl +94 -94
  36. package/templates/minimal-app/apps/e2e/tests/files.spec.ts.tpl +35 -0
  37. package/templates/minimal-app/apps/e2e/tests/helpers.ts.tpl +289 -289
  38. package/templates/minimal-app/apps/e2e/tests/standard.spec.ts.tpl +516 -516
  39. package/templates/minimal-app/apps/e2e/tests/waitlist.spec.ts.tpl +107 -107
  40. package/templates/minimal-app/apps/e2e/tests/workflow.spec.ts.tpl +26 -0
  41. package/templates/minimal-app/apps/e2e/tsconfig.json.tpl +10 -10
  42. package/templates/minimal-app/apps/email/package.json.tpl +34 -34
  43. package/templates/minimal-app/apps/email/src/emails/trialEndingEmail.tsx.tpl +40 -0
  44. package/templates/minimal-app/apps/email/src/index.ts.tpl +7 -0
  45. package/templates/minimal-app/apps/email/src/resources.ts.tpl +72 -65
  46. package/templates/minimal-app/apps/expo/app.json.tpl +36 -36
  47. package/templates/minimal-app/apps/expo/eslint.config.js.tpl +8 -8
  48. package/templates/minimal-app/apps/expo/metro.config.js.tpl +16 -16
  49. package/templates/minimal-app/apps/expo/package.json.tpl +59 -59
  50. package/templates/minimal-app/apps/expo/src/app/_layout.tsx.tpl +31 -31
  51. package/templates/minimal-app/apps/expo/src/app/claim-account.tsx.tpl +151 -151
  52. package/templates/minimal-app/apps/expo/src/app/forgot-password.tsx.tpl +53 -53
  53. package/templates/minimal-app/apps/expo/src/app/index.tsx.tpl +8 -8
  54. package/templates/minimal-app/apps/expo/src/app/login.tsx.tpl +71 -71
  55. package/templates/minimal-app/apps/expo/src/app/logout.tsx.tpl +19 -19
  56. package/templates/minimal-app/apps/expo/src/app/organization/accept-invitation.tsx.tpl +85 -85
  57. package/templates/minimal-app/apps/expo/src/app/posts.tsx.tpl +40 -40
  58. package/templates/minimal-app/apps/expo/src/app/reset-password.tsx.tpl +75 -75
  59. package/templates/minimal-app/apps/expo/src/app/signup.tsx.tpl +193 -193
  60. package/templates/minimal-app/apps/expo/src/components/AuthScaffold.tsx.tpl +34 -34
  61. package/templates/minimal-app/apps/expo/src/components/FormTextField.tsx.tpl +37 -37
  62. package/templates/minimal-app/apps/expo/src/components/LoadingScreen.tsx.tpl +10 -10
  63. package/templates/minimal-app/apps/expo/src/components/StatusText.tsx.tpl +22 -22
  64. package/templates/minimal-app/apps/expo/src/config.ts.tpl +9 -9
  65. package/templates/minimal-app/apps/expo/src/global.css.tpl +11 -11
  66. package/templates/minimal-app/apps/expo/src/lib/trpc.ts.tpl +20 -20
  67. package/templates/minimal-app/apps/expo/tsconfig.json.tpl +11 -11
  68. package/templates/minimal-app/apps/expo/uniwind-env.d.ts.tpl +1 -1
  69. package/templates/minimal-app/apps/landing/.env.production.example.tpl +7 -0
  70. package/templates/minimal-app/apps/landing/Dockerfile.tpl +39 -0
  71. package/templates/minimal-app/apps/landing/fly.toml.tpl +20 -0
  72. package/templates/minimal-app/apps/landing/index.html.tpl +35 -0
  73. package/templates/minimal-app/apps/landing/package.json.tpl +34 -0
  74. package/templates/minimal-app/apps/landing/scripts/fly-deploy.mjs.tpl +75 -0
  75. package/templates/minimal-app/apps/landing/scripts/fly-secrets.mjs.tpl +31 -0
  76. package/templates/minimal-app/apps/landing/src/App.tsx.tpl +12 -0
  77. package/templates/minimal-app/apps/landing/src/LandingPage.tsx.tpl +37 -0
  78. package/templates/minimal-app/apps/landing/src/index.css.tpl +24 -0
  79. package/templates/minimal-app/apps/landing/src/main.tsx.tpl +16 -0
  80. package/templates/minimal-app/apps/landing/src/vite-env.d.ts.tpl +11 -0
  81. package/templates/minimal-app/apps/landing/tsconfig.json.tpl +12 -0
  82. package/templates/minimal-app/apps/landing/vite.config.ts.tpl +25 -0
  83. package/templates/minimal-app/apps/server/AGENTS.md.tpl +54 -46
  84. package/templates/minimal-app/apps/server/{drizzle/seed.e2e.ts.tpl → db.e2e.ts.tpl} +57 -40
  85. package/templates/minimal-app/apps/server/{drizzle/seed.ts.tpl → db.ts.tpl} +27 -20
  86. package/templates/minimal-app/apps/server/drizzle/0002_concerned_proudstar.sql +3 -0
  87. package/templates/minimal-app/apps/server/drizzle/0003_silly_the_liberteens.sql +27 -0
  88. package/templates/minimal-app/apps/server/drizzle/meta/0000_snapshot.json.tpl +56 -166
  89. package/templates/minimal-app/apps/server/drizzle/meta/0001_snapshot.json.tpl +1796 -1910
  90. package/templates/minimal-app/apps/server/drizzle/meta/0002_snapshot.json.tpl +1818 -0
  91. package/templates/minimal-app/apps/server/drizzle/meta/0003_snapshot.json.tpl +2137 -0
  92. package/templates/minimal-app/apps/server/drizzle/meta/_journal.json.tpl +15 -1
  93. package/templates/minimal-app/apps/server/drizzle.config.ts.tpl +3 -33
  94. package/templates/minimal-app/apps/server/package.json.tpl +57 -56
  95. package/templates/minimal-app/apps/server/src/app.ts.tpl +149 -141
  96. package/templates/minimal-app/apps/server/src/index.ts.tpl +12 -65
  97. package/templates/minimal-app/apps/server/src/modules/conversation/conversation.mastra.ts.tpl +26 -0
  98. package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.module.ts.tpl +47 -0
  99. package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.service.ts.tpl +43 -0
  100. package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.trpc.ts.tpl +16 -0
  101. package/templates/minimal-app/apps/server/src/modules/posts/posts.db.ts.tpl +28 -28
  102. package/templates/minimal-app/apps/server/src/modules/posts/posts.grants.ts.tpl +28 -30
  103. package/templates/minimal-app/apps/server/src/modules/posts/posts.module.ts.tpl +54 -54
  104. package/templates/minimal-app/apps/server/src/modules/posts/posts.repository.ts.tpl +67 -67
  105. package/templates/minimal-app/apps/server/src/modules/posts/posts.service.ts.tpl +116 -115
  106. package/templates/minimal-app/apps/server/src/modules/posts/posts.trpc.ts.tpl +37 -37
  107. package/templates/minimal-app/apps/server/src/modules/test-harness/test-harness.module.ts.tpl +279 -279
  108. package/templates/minimal-app/apps/server/src/schema.ts.tpl +31 -20
  109. package/templates/minimal-app/apps/server/src/types.ts.tpl +12 -1
  110. package/templates/minimal-app/apps/server/tsconfig.json.tpl +1 -1
  111. package/templates/minimal-app/apps/server/tsup.config.ts.tpl +23 -9
  112. package/templates/minimal-app/apps/shared/.env.example.tpl +5 -0
  113. package/templates/minimal-app/apps/shared/.env.production.example.tpl +23 -0
  114. package/templates/minimal-app/apps/shared/.env.tpl +5 -0
  115. package/templates/minimal-app/apps/shared/Dockerfile.tpl +48 -0
  116. package/templates/minimal-app/apps/shared/fly.toml.tpl +24 -0
  117. package/templates/minimal-app/apps/shared/package.json.tpl +25 -24
  118. package/templates/minimal-app/apps/shared/scripts/fly-deploy.mjs.tpl +75 -0
  119. package/templates/minimal-app/apps/shared/scripts/fly-secrets.mjs.tpl +31 -0
  120. package/templates/minimal-app/apps/shared/src/modules/app/app.constants.ts.tpl +28 -28
  121. package/templates/minimal-app/apps/shared/src/modules/conversation/conversation.constants.ts.tpl +1 -0
  122. package/templates/minimal-app/apps/shared/src/modules/notification/notification.schema.ts.tpl +3 -3
  123. package/templates/minimal-app/apps/shared/src/modules/posts/posts.constants.ts.tpl +5 -5
  124. package/templates/minimal-app/apps/shared/src/modules/posts/posts.schema.ts.tpl +25 -25
  125. package/templates/minimal-app/apps/webapp/package.json.tpl +53 -53
  126. package/templates/minimal-app/apps/webapp/public/push-sw.js.tpl +34 -34
  127. package/templates/minimal-app/apps/webapp/src/App.tsx.tpl +46 -44
  128. package/templates/minimal-app/apps/webapp/src/Layout.tsx.tpl +96 -65
  129. package/templates/minimal-app/apps/webapp/src/Router.tsx.tpl +114 -93
  130. package/templates/minimal-app/apps/webapp/src/index.css.tpl +15 -15
  131. package/templates/minimal-app/apps/webapp/src/main.tsx.tpl +26 -26
  132. package/templates/minimal-app/apps/webapp/src/modules/conversation/ConversationRoute.tsx.tpl +36 -0
  133. package/templates/minimal-app/apps/webapp/src/modules/files/FilesRoute.tsx.tpl +88 -0
  134. package/templates/minimal-app/apps/webapp/src/modules/files/hooks/useFilesRoute.ts.tpl +50 -0
  135. package/templates/minimal-app/apps/webapp/src/modules/workflows/WorkflowsRoute.tsx.tpl +97 -0
  136. package/templates/minimal-app/apps/webapp/src/modules/workflows/hooks/useWorkflowsRoute.ts.tpl +64 -0
  137. package/templates/minimal-app/apps/webapp/src/utils/i18n.ts.tpl +13 -13
  138. package/templates/minimal-app/apps/webapp/src/utils/trpc.ts.tpl +10 -10
  139. package/templates/minimal-app/apps/webapp/src/vite-env.d.ts.tpl +1 -1
  140. package/templates/minimal-app/apps/webapp/translations/en/app.json.tpl +7 -7
  141. package/templates/minimal-app/apps/webapp/translations/en/{{APP_SLUG}}.json.tpl +193 -135
  142. package/templates/minimal-app/apps/webapp/tsconfig.json.tpl +17 -17
  143. package/templates/minimal-app/apps/webapp/vite.config.ts.tpl +31 -31
  144. package/templates/minimal-app/biome.json.tpl +17 -12
  145. package/templates/minimal-app/package.json.tpl +9 -5
  146. package/templates/minimal-app/pnpm-workspace.yaml.tpl +67 -58
  147. package/templates/minimal-app/template.manifest.json +92 -2
  148. package/templates/minimal-app/apps/server/drizzle/db.ts.tpl +0 -26
  149. package/templates/minimal-app/apps/server/drizzle/guard.ts.tpl +0 -36
  150. package/templates/minimal-app/apps/server/drizzle/reset.ts.tpl +0 -40
  151. package/templates/minimal-app/apps/server/drizzle/sync.ts.tpl +0 -16
  152. package/templates/minimal-app/apps/server/src/lib/auth.ts.tpl +0 -12
@@ -15,6 +15,20 @@
15
15
  "when": 1785171166863,
16
16
  "tag": "0001_third_ink",
17
17
  "breakpoints": true
18
+ },
19
+ {
20
+ "idx": 2,
21
+ "version": "6",
22
+ "when": 1786951360347,
23
+ "tag": "0002_concerned_proudstar",
24
+ "breakpoints": true
25
+ },
26
+ {
27
+ "idx": 3,
28
+ "version": "6",
29
+ "when": 1787145865079,
30
+ "tag": "0003_silly_the_liberteens",
31
+ "breakpoints": true
18
32
  }
19
33
  ]
20
- }
34
+ }
@@ -1,33 +1,3 @@
1
- import * as dotenv from "dotenv";
2
- import { defineConfig } from "drizzle-kit";
3
-
4
- dotenv.config({ path: process.env.DRIZZLE_ENV_PATH || "../shared/.env" });
5
-
6
- const url = process.env.TURSO_DATABASE_URL || process.env.DATABASE_URL;
7
- const authToken = process.env.TURSO_AUTH_TOKEN;
8
- const schema = process.env.DRIZZLE_SCHEMA_PATH || "./src/schema.ts";
9
-
10
- if (!url) {
11
- throw new Error("DATABASE_URL or TURSO_DATABASE_URL must be set");
12
- }
13
-
14
- export default defineConfig(
15
- url && authToken
16
- ? {
17
- dialect: "turso",
18
- schema,
19
- out: "./drizzle",
20
- dbCredentials: {
21
- url,
22
- authToken,
23
- },
24
- }
25
- : {
26
- dialect: "sqlite",
27
- schema,
28
- out: "./drizzle",
29
- dbCredentials: {
30
- url,
31
- },
32
- }
33
- );
1
+ import { defineDrizzleKitConfig } from "@m5kdev/backend/db";
2
+
3
+ export default defineDrizzleKitConfig({ schema: "./src/schema.ts" });
@@ -1,56 +1,57 @@
1
- {
2
- "name": "{{PACKAGE_SCOPE}}/server",
3
- "version": "0.0.0",
4
- "private": true,
5
- "main": "dist/index.js",
6
- "scripts": {
7
- "dev": "tsx watch --env-file=../shared/.env src/index.ts",
8
- "build": "tsup",
9
- "start": "node dist/index.js",
10
- "lint": "biome check .",
11
- "lint:fix": "biome check . --write",
12
- "check-types": "tsc --noEmit",
13
- "drizzle:generate": "drizzle-kit generate",
14
- "drizzle:migrate": "drizzle-kit migrate",
15
- "drizzle:studio": "drizzle-kit studio",
16
- "drizzle:sync": "tsx --env-file=../shared/.env drizzle/sync.ts",
17
- "drizzle:seed": "tsx --env-file=../shared/.env drizzle/seed.ts",
18
- "drizzle:reset": "tsx --env-file=../shared/.env drizzle/reset.ts"
19
- },
20
- "dependencies": {
21
- "{{PACKAGE_SCOPE}}/email": "workspace:*",
22
- "{{PACKAGE_SCOPE}}/shared": "workspace:*",
23
- "@libsql/client": "catalog:",
24
- "@m5kdev/backend": "catalog:",
25
- "@m5kdev/commons": "catalog:",
26
- "@trpc/server": "catalog:",
27
- "better-auth": "catalog:",
28
- "cors": "catalog:",
29
- "dotenv": "catalog:",
30
- "drizzle-orm": "catalog:",
31
- "drizzle-zod": "catalog:",
32
- "express": "catalog:",
33
- "ioredis": "catalog:",
34
- "neverthrow": "catalog:",
35
- "react": "catalog:",
36
- "react-dom": "catalog:",
37
- "uuid": "catalog:",
38
- "zod": "catalog:"
39
- },
40
- "devDependencies": {
41
- "@m5kdev/config": "catalog:",
42
- "@types/cors": "catalog:",
43
- "@types/express": "catalog:",
44
- "@types/node": "catalog:",
45
- "@types/react": "catalog:",
46
- "@types/react-dom": "catalog:",
47
- "drizzle-kit": "catalog:",
48
- "tslib": "catalog:",
49
- "tsup": "catalog:",
50
- "tsx": "catalog:",
51
- "typescript": "catalog:"
52
- },
53
- "exports": {
54
- "./types": "./src/types.ts"
55
- }
56
- }
1
+ {
2
+ "name": "{{PACKAGE_SCOPE}}/server",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "dev": "tsx watch --env-file=../shared/.env src/index.ts",
8
+ "build": "tsup",
9
+ "start": "node dist/index.js",
10
+ "lint": "biome check .",
11
+ "lint:fix": "biome check . --write",
12
+ "check-types": "tsc --noEmit",
13
+ "drizzle:generate": "drizzle-kit generate",
14
+ "drizzle:migrate": "drizzle-kit migrate",
15
+ "drizzle:studio": "drizzle-kit studio",
16
+ "drizzle:sync": "tsx --env-file=../shared/.env db.ts sync",
17
+ "drizzle:seed": "tsx --env-file=../shared/.env db.ts seed",
18
+ "drizzle:reset": "tsx --env-file=../shared/.env db.ts reset"
19
+ },
20
+ "dependencies": {
21
+ "{{PACKAGE_SCOPE}}/email": "workspace:*",
22
+ "{{PACKAGE_SCOPE}}/shared": "workspace:*",
23
+ "@libsql/client": "catalog:m5kdev",
24
+ "@m5kdev/backend": "catalog:m5kdev",
25
+ "@m5kdev/commons": "catalog:m5kdev",
26
+ "@mastra/core": "catalog:m5kdev",
27
+ "@openrouter/ai-sdk-provider": "catalog:m5kdev",
28
+ "@opentelemetry/api": "catalog:m5kdev",
29
+ "@trpc/server": "catalog:m5kdev",
30
+ "better-auth": "catalog:m5kdev",
31
+ "dotenv": "catalog:m5kdev",
32
+ "drizzle-orm": "catalog:m5kdev",
33
+ "drizzle-zod": "catalog:m5kdev",
34
+ "express": "catalog:m5kdev",
35
+ "ioredis": "catalog:m5kdev",
36
+ "neverthrow": "catalog:m5kdev",
37
+ "react": "catalog:m5kdev",
38
+ "react-dom": "catalog:m5kdev",
39
+ "uuid": "catalog:m5kdev",
40
+ "zod": "catalog:m5kdev"
41
+ },
42
+ "devDependencies": {
43
+ "@m5kdev/config": "catalog:m5kdev",
44
+ "@types/express": "catalog:m5kdev",
45
+ "@types/node": "catalog:m5kdev",
46
+ "@types/react": "catalog:m5kdev",
47
+ "@types/react-dom": "catalog:m5kdev",
48
+ "drizzle-kit": "catalog:m5kdev",
49
+ "tslib": "catalog:m5kdev",
50
+ "tsup": "catalog:m5kdev",
51
+ "tsx": "catalog:m5kdev",
52
+ "typescript": "catalog:m5kdev"
53
+ },
54
+ "exports": {
55
+ "./types": "./src/types.ts"
56
+ }
57
+ }
@@ -1,141 +1,149 @@
1
- import { createBackendApp } from "@m5kdev/backend/app";
2
- import { createBetterAuth } from "@m5kdev/backend/modules/auth/auth.lib";
3
- import { AuthModule } from "@m5kdev/backend/modules/auth/auth.module";
4
- import { EmailModule } from "@m5kdev/backend/modules/email/email.module";
5
- import { EmailPreviewModule } from "@m5kdev/backend/modules/email/email.preview.module";
6
- import { WorkflowModule } from "@m5kdev/backend/modules/workflow/workflow.module";
7
- import { USER_LOCALE_HEADER } from "@m5kdev/commons/modules/auth/auth.constants";
8
- import { templates } from "{{PACKAGE_SCOPE}}/email";
9
- import { emailResources } from "{{PACKAGE_SCOPE}}/email/resources";
10
- import {
11
- APP_LOCALE_CONFIG,
12
- APP_NAME,
13
- APP_ROLES_CONFIG,
14
- } from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
15
- import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
16
- import cors from "cors";
17
- import express from "express";
18
- import { PostsModule } from "./modules/posts/posts.module";
19
- // m5k:test-harness:start
20
- import { TestHarnessModule } from "./modules/test-harness/test-harness.module";
21
- // m5k:test-harness:end
22
- import * as schema from "./schema";
23
-
24
- const app = express();
25
- const appUrl = process.env.VITE_APP_URL ?? "http://localhost:5173";
26
- const serverUrl = process.env.VITE_SERVER_URL ?? "http://localhost:8080";
27
- const databaseUrl = process.env.DATABASE_URL ?? "file:./local.db";
28
- const syncUrl = process.env.TURSO_DATABASE_URL;
29
- const authToken = process.env.TURSO_AUTH_TOKEN;
30
- const redisUrl = process.env.REDIS_URL ?? "redis://127.0.0.1:6379";
31
- const resendApiKey = process.env.RESEND_API_KEY;
32
- const enableWaitlist = process.env.VITE_ENABLE_WAITLIST === "true";
33
-
34
- const connection =
35
- syncUrl && authToken
36
- ? {
37
- url: databaseUrl,
38
- syncUrl,
39
- authToken,
40
- syncInterval: 60,
41
- readYourWrites: true,
42
- }
43
- : {
44
- url: databaseUrl,
45
- };
46
-
47
- app.use(express.json());
48
- app.use(
49
- cors({
50
- origin: [appUrl],
51
- credentials: true,
52
- methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
53
- allowedHeaders: [
54
- "Content-Type",
55
- "Authorization",
56
- "Waitlist-Invitation-Code",
57
- "Organization-Invitation-Code",
58
- "Admin-Create-Verified-User",
59
- USER_LOCALE_HEADER,
60
- ],
61
- })
62
- );
63
-
64
- export const builtBackendApp = createBackendApp(
65
- {
66
- db: connection,
67
- express: app,
68
- schema,
69
- app: {
70
- name: APP_NAME,
71
- urls: {
72
- web: appUrl,
73
- api: serverUrl,
74
- },
75
- locales: APP_LOCALE_CONFIG,
76
- roles: APP_ROLES_CONFIG,
77
- },
78
- i18n: {
79
- resources: emailResources,
80
- },
81
- redis: {
82
- url: redisUrl,
83
- options: { maxRetriesPerRequest: null },
84
- },
85
- resend: resendApiKey ? { apiKey: resendApiKey } : undefined,
86
- email: {
87
- mode: resendApiKey ? "send" : "store",
88
- from: process.env.EMAIL_FROM ?? "no-reply@local.m5kdev.test",
89
- systemNotificationEmail: process.env.SYSTEM_NOTIFICATION_EMAIL ?? "ops@local.m5kdev.test",
90
- outputDirectory: process.env.EMAIL_OUTPUT_DIRECTORY ?? ".emails",
91
- },
92
- auth: {
93
- factory({ db, services, appConfig, i18n }) {
94
- return createBetterAuth({
95
- orm: db.orm,
96
- schema: db.schema,
97
- services: {
98
- email: services.email.email,
99
- },
100
- app: appConfig,
101
- i18n,
102
- config: {
103
- waitlist: enableWaitlist,
104
- provisionedAccountEmailDomain:
105
- process.env.PROVISIONED_EMAIL_DOMAIN ?? "provisioned.{{APP_SLUG}}.local",
106
- },
107
- options: {
108
- secret: process.env.BETTER_AUTH_SECRET ?? "QJvCmK9UBuHAWbNr2vV3ROVt8XrBYV5d",
109
- },
110
- });
111
- },
112
- },
113
- },
114
- [
115
- new EmailModule(templates as never),
116
- new AuthModule(),
117
- new WorkflowModule({
118
- queues: {
119
- fast: { concurrency: 5 },
120
- },
121
- defaultQueue: "fast",
122
- defaults: {
123
- timeout: 60_000,
124
- jobOptions: { removeOnComplete: { age: 3600 } },
125
- },
126
- reconcile: { enabled: true },
127
- }),
128
- new PostsModule(),
129
- new EmailPreviewModule({ allowDelete: process.env.NODE_ENV !== "production" }),
130
- // m5k:test-harness:start
131
- new TestHarnessModule(),
132
- // m5k:test-harness:end
133
- ] as const
134
- );
135
-
136
- export type AppRouter = typeof builtBackendApp.trpc.router;
137
- export type RouterInputs = inferRouterInputs<AppRouter>;
138
- export type RouterOutputs = inferRouterOutputs<AppRouter>;
139
-
140
- export type Orm = typeof builtBackendApp.db.orm;
141
- export type Schema = typeof builtBackendApp.db.schema;
1
+ import { createBackendApp } from "@m5kdev/backend/app";
2
+ import { shutdownTelemetry } from "@m5kdev/backend/lib/otel";
3
+ // m5k:ai:start
4
+ import { AIModule } from "@m5kdev/backend/modules/ai/ai.module";
5
+ // m5k:ai:end
6
+ import { createBetterAuth } from "@m5kdev/backend/modules/auth/auth.lib";
7
+ import { AuthModule } from "@m5kdev/backend/modules/auth/auth.module";
8
+ import { EmailModule } from "@m5kdev/backend/modules/email/email.module";
9
+ import { EmailPreviewModule } from "@m5kdev/backend/modules/email/email.preview.module";
10
+ // m5k:files:start
11
+ import { FileModule } from "@m5kdev/backend/modules/file/file.module";
12
+ // m5k:files:end
13
+ // m5k:notifications:start
14
+ import { NotificationModule } from "@m5kdev/backend/modules/notification/notification.module";
15
+ // m5k:notifications:end
16
+ // m5k:workflows:start
17
+ import { WorkflowModule } from "@m5kdev/backend/modules/workflow/workflow.module";
18
+ // m5k:workflows:end
19
+ import { templates } from "{{PACKAGE_SCOPE}}/email";
20
+ import { emailResources } from "{{PACKAGE_SCOPE}}/email/resources";
21
+ import {
22
+ APP_LOCALE_CONFIG,
23
+ APP_NAME,
24
+ APP_ROLES_CONFIG,
25
+ } from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
26
+ // m5k:ai:start
27
+ import { mastra, openrouter } from "./modules/conversation/conversation.mastra";
28
+ // m5k:ai:end
29
+ // m5k:workflows:start
30
+ import { DemoWorkflowModule } from "./modules/demo-workflow/demo-workflow.module";
31
+ // m5k:workflows:end
32
+ import { PostsModule } from "./modules/posts/posts.module";
33
+ // m5k:test-harness:start
34
+ import { TestHarnessModule } from "./modules/test-harness/test-harness.module";
35
+ // m5k:test-harness:end
36
+ import * as schema from "./schema";
37
+
38
+ const appUrl = process.env.VITE_APP_URL ?? "http://localhost:5173";
39
+ const serverUrl = process.env.VITE_SERVER_URL ?? "http://localhost:8080";
40
+ const databaseUrl = process.env.DATABASE_URL ?? "file:./local.db";
41
+ const syncUrl = process.env.TURSO_DATABASE_URL;
42
+ const authToken = process.env.TURSO_AUTH_TOKEN;
43
+ // m5k:workflows:start
44
+ const redisUrl = process.env.REDIS_URL ?? "redis://127.0.0.1:6379";
45
+ // m5k:workflows:end
46
+ const resendApiKey = process.env.RESEND_API_KEY;
47
+ const enableWaitlist = process.env.VITE_ENABLE_WAITLIST === "true";
48
+
49
+ const connection =
50
+ syncUrl && authToken
51
+ ? {
52
+ url: databaseUrl,
53
+ syncUrl,
54
+ authToken,
55
+ syncInterval: 60,
56
+ readYourWrites: true,
57
+ }
58
+ : {
59
+ url: databaseUrl,
60
+ };
61
+
62
+ export const builtBackendApp = createBackendApp(
63
+ {
64
+ db: connection,
65
+ schema,
66
+ onShutdown: shutdownTelemetry,
67
+ app: {
68
+ name: APP_NAME,
69
+ urls: {
70
+ web: appUrl,
71
+ api: serverUrl,
72
+ },
73
+ locales: APP_LOCALE_CONFIG,
74
+ roles: APP_ROLES_CONFIG,
75
+ },
76
+ i18n: {
77
+ resources: emailResources,
78
+ },
79
+ spa: { root: "./client" },
80
+ // m5k:workflows:start
81
+ redis: {
82
+ url: redisUrl,
83
+ options: { maxRetriesPerRequest: null },
84
+ },
85
+ // m5k:workflows:end
86
+ resend: resendApiKey ? { apiKey: resendApiKey } : undefined,
87
+ email: {
88
+ mode: resendApiKey ? "send" : "store",
89
+ from: process.env.EMAIL_FROM ?? "no-reply@local.m5kdev.test",
90
+ systemNotificationEmail: process.env.SYSTEM_NOTIFICATION_EMAIL ?? "ops@local.m5kdev.test",
91
+ outputDirectory: process.env.EMAIL_OUTPUT_DIRECTORY ?? ".emails",
92
+ },
93
+ auth: {
94
+ factory({ db, services, appConfig, i18n }) {
95
+ return createBetterAuth({
96
+ orm: db.orm,
97
+ schema: db.schema,
98
+ services: {
99
+ email: services.email.email,
100
+ },
101
+ app: appConfig,
102
+ i18n,
103
+ config: {
104
+ waitlist: enableWaitlist,
105
+ provisionedAccountEmailDomain:
106
+ process.env.PROVISIONED_EMAIL_DOMAIN ?? "provisioned.{{APP_SLUG}}.local",
107
+ },
108
+ options: {
109
+ secret: process.env.BETTER_AUTH_SECRET ?? "QJvCmK9UBuHAWbNr2vV3ROVt8XrBYV5d",
110
+ },
111
+ });
112
+ },
113
+ },
114
+ },
115
+ [
116
+ new EmailModule(templates as never),
117
+ new AuthModule(),
118
+ // m5k:ai:start
119
+ new AIModule({
120
+ libs: { mastra, openrouter },
121
+ }),
122
+ // m5k:ai:end
123
+ // m5k:files:start
124
+ new FileModule(),
125
+ // m5k:files:end
126
+ // m5k:workflows:start
127
+ new WorkflowModule({
128
+ queues: {
129
+ fast: { concurrency: 5 },
130
+ },
131
+ defaultQueue: "fast",
132
+ defaults: {
133
+ timeout: 60_000,
134
+ jobOptions: { removeOnComplete: { age: 3600 } },
135
+ },
136
+ reconcile: { enabled: true },
137
+ }),
138
+ new DemoWorkflowModule(),
139
+ // m5k:workflows:end
140
+ // m5k:notifications:start
141
+ new NotificationModule(),
142
+ // m5k:notifications:end
143
+ new PostsModule(),
144
+ new EmailPreviewModule({ allowDelete: process.env.NODE_ENV !== "production" }),
145
+ // m5k:test-harness:start
146
+ new TestHarnessModule(),
147
+ // m5k:test-harness:end
148
+ ] as const
149
+ );
@@ -1,65 +1,12 @@
1
- import "./instrumentation";
2
- import type { Server } from "node:http";
3
- import { shutdownTelemetry } from "@m5kdev/backend/lib/otel";
4
- import { builtBackendApp } from "./app";
5
-
6
- const port = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : 8080;
7
-
8
- let httpServer: Server | undefined;
9
-
10
- function logError(context: string, error: unknown): void {
11
- console.error(`[server] ${context}`, error);
12
- }
13
-
14
- async function shutdown(): Promise<void> {
15
- try {
16
- await builtBackendApp.shutdown();
17
- } catch (e) {
18
- logError("builtBackendApp.shutdown() failed", e);
19
- }
20
- try {
21
- await shutdownTelemetry();
22
- } catch (e) {
23
- logError("shutdownTelemetry() failed", e);
24
- }
25
- if (httpServer) {
26
- await new Promise<void>((resolve) => {
27
- httpServer?.close((err) => {
28
- if (err) {
29
- logError("HTTP server close failed", err);
30
- }
31
- resolve();
32
- });
33
- });
34
- }
35
- process.exit(0);
36
- }
37
-
38
- process.once("SIGINT", () => {
39
- void shutdown();
40
- });
41
- process.once("SIGTERM", () => {
42
- void shutdown();
43
- });
44
-
45
- async function start(): Promise<void> {
46
- await builtBackendApp.start();
47
- await new Promise<void>((resolve, reject) => {
48
- httpServer = builtBackendApp.express.app.listen(port, () => {
49
- console.info(
50
- `Server running at ${process.env.VITE_SERVER_URL ?? `http://localhost:${port}`}`
51
- );
52
- resolve();
53
- });
54
- httpServer.on("error", reject);
55
- });
56
- }
57
-
58
- void (async () => {
59
- try {
60
- await start();
61
- } catch (e) {
62
- logError("Fatal: builtBackendApp.start() or HTTP listen failed", e);
63
- process.exit(1);
64
- }
65
- })();
1
+ import "./instrumentation";
2
+ import { builtBackendApp } from "./app";
3
+
4
+ void (async () => {
5
+ try {
6
+ await builtBackendApp.start();
7
+ console.info(`Server running at ${process.env.VITE_SERVER_URL}`);
8
+ } catch (e) {
9
+ console.error("[server] Fatal: builtBackendApp.start() failed", e);
10
+ process.exit(1);
11
+ }
12
+ })();
@@ -0,0 +1,26 @@
1
+ import { Mastra } from "@mastra/core";
2
+ import { Agent } from "@mastra/core/agent";
3
+ import { createOpenRouter } from "@openrouter/ai-sdk-provider";
4
+ import { STARTER_ASSISTANT_AGENT_ID } from "{{PACKAGE_SCOPE}}/shared/modules/conversation/conversation.constants";
5
+
6
+ /** Cheap OpenRouter chat model for the starter Conversation example. */
7
+ const STARTER_CHAT_MODEL = "google/gemini-2.5-flash";
8
+
9
+ export const openrouter = createOpenRouter({
10
+ apiKey: process.env.OPENROUTER_API_KEY ?? "",
11
+ });
12
+
13
+ const assistant = new Agent({
14
+ id: STARTER_ASSISTANT_AGENT_ID,
15
+ name: "Assistant",
16
+ instructions: "You are a helpful assistant for this starter app. Keep answers concise.",
17
+ model: openrouter.chat(STARTER_CHAT_MODEL, {
18
+ usage: { include: true },
19
+ }),
20
+ });
21
+
22
+ export const mastra = new Mastra({
23
+ agents: {
24
+ [STARTER_ASSISTANT_AGENT_ID]: assistant,
25
+ },
26
+ });
@@ -0,0 +1,47 @@
1
+ import { createBackendRouterMap } from "@m5kdev/backend/app";
2
+ import {
3
+ BaseModule,
4
+ type ModuleServicesContext,
5
+ type ModuleTRPCContext,
6
+ type TableMap,
7
+ } from "@m5kdev/backend/modules/base/base.module";
8
+ import type { WorkflowModule } from "@m5kdev/backend/modules/workflow/workflow.module";
9
+ import { DemoWorkflowService } from "./demo-workflow.service";
10
+ import { createDemoWorkflowTRPC } from "./demo-workflow.trpc";
11
+
12
+ type DemoWorkflowModuleDeps = { workflow: WorkflowModule };
13
+ type DemoWorkflowModuleServices = {
14
+ demoWorkflow: DemoWorkflowService;
15
+ };
16
+ type DemoWorkflowModuleRouters = {
17
+ demoWorkflow: ReturnType<typeof createDemoWorkflowTRPC>;
18
+ };
19
+
20
+ export class DemoWorkflowModule extends BaseModule<
21
+ DemoWorkflowModuleDeps,
22
+ TableMap,
23
+ Record<string, never>,
24
+ DemoWorkflowModuleServices,
25
+ DemoWorkflowModuleRouters
26
+ > {
27
+ readonly id = "demo-workflow";
28
+ override readonly dependsOn = ["workflow"] as const;
29
+
30
+ override services({
31
+ deps,
32
+ }: ModuleServicesContext<DemoWorkflowModuleDeps, Record<string, never>>) {
33
+ return {
34
+ demoWorkflow: new DemoWorkflowService({ workflow: deps.workflow.services.workflow }),
35
+ };
36
+ }
37
+
38
+ override trpc({
39
+ trpc,
40
+ services,
41
+ }: ModuleTRPCContext<DemoWorkflowModuleDeps, DemoWorkflowModuleServices>) {
42
+ return createBackendRouterMap(
43
+ "demoWorkflow",
44
+ createDemoWorkflowTRPC(trpc, services.demoWorkflow)
45
+ );
46
+ }
47
+ }