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
@@ -1,20 +1,31 @@
1
- export {
2
- accountClaimMagicLinks,
3
- accounts,
4
- apikeys,
5
- invitations,
6
- members,
7
- organizations,
8
- sessions,
9
- teamMembers,
10
- teams,
11
- users,
12
- verifications,
13
- waitlist,
14
- } from "@m5kdev/backend/modules/auth/auth.db";
15
- export {
16
- notificationDevices,
17
- notificationSendLogs,
18
- } from "@m5kdev/backend/modules/notification/notification.db";
19
- export { workflows } from "@m5kdev/backend/modules/workflow/workflow.db";
20
- export { posts } from "./modules/posts/posts.db";
1
+ // biome-ignore-all assist/source/organizeImports: feature-gated table exports stay in marker blocks
2
+ export {
3
+ accountClaimMagicLinks,
4
+ accounts,
5
+ apikeys,
6
+ invitations,
7
+ members,
8
+ organizations,
9
+ sessions,
10
+ teamMembers,
11
+ teams,
12
+ users,
13
+ verifications,
14
+ waitlist,
15
+ } from "@m5kdev/backend/modules/auth/auth.db";
16
+ // m5k:ai:start
17
+ export { aiUsage, chats } from "@m5kdev/backend/modules/ai/ai.db";
18
+ // m5k:ai:end
19
+ // m5k:files:start
20
+ export { files } from "@m5kdev/backend/modules/file/file.db";
21
+ // m5k:files:end
22
+ // m5k:notifications:start
23
+ export {
24
+ notificationDevices,
25
+ notificationSendLogs,
26
+ } from "@m5kdev/backend/modules/notification/notification.db";
27
+ // m5k:notifications:end
28
+ // m5k:workflows:start
29
+ export { workflows } from "@m5kdev/backend/modules/workflow/workflow.db";
30
+ // m5k:workflows:end
31
+ export { posts } from "./modules/posts/posts.db";
@@ -1 +1,12 @@
1
- export type { AppRouter } from "./app";
1
+ import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
2
+ import type { builtBackendApp } from "./app";
3
+
4
+ export type AppRouter = typeof builtBackendApp.trpc.router;
5
+ export type RouterInputs = inferRouterInputs<AppRouter>;
6
+ export type RouterOutputs = inferRouterOutputs<AppRouter>;
7
+
8
+ export type Orm = typeof builtBackendApp.db.orm;
9
+ export type Schema = typeof builtBackendApp.db.schema;
10
+
11
+ export type Session = NonNullable<typeof builtBackendApp.auth>["instance"]["$Infer"]["Session"];
12
+ export type User = Session["user"];
@@ -9,5 +9,5 @@
9
9
  "rootDir": "."
10
10
  },
11
11
  "exclude": ["node_modules"],
12
- "include": ["src/**/*.ts", "drizzle/**/*.ts", "drizzle.config.ts", "tsup.config.ts"]
12
+ "include": ["src/**/*.ts", "db.ts", "db.e2e.ts", "drizzle/**/*.ts", "drizzle.config.ts", "tsup.config.ts"]
13
13
  }
@@ -1,9 +1,23 @@
1
- import { defineConfig } from "tsup";
2
-
3
- export default defineConfig({
4
- entry: ["src/index.ts"],
5
- format: ["cjs"],
6
- outDir: "dist",
7
- sourcemap: true,
8
- clean: true,
9
- });
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: ["src/index.ts"],
5
+ format: ["cjs"],
6
+ outDir: "dist",
7
+ sourcemap: true,
8
+ clean: true,
9
+ // Workspace packages export TypeScript source for local tsx/Vite. Node
10
+ // cannot strip types from files under node_modules
11
+ // (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING), so bundle them into dist.
12
+ noExternal: [/^{{PACKAGE_SCOPE}}\//],
13
+ // Only leave runtime requires for packages the server installs. Bundling
14
+ // @beatquill/email would otherwise emit require("@m5kdev/email/...") and
15
+ // require("@react-email/...") that pnpm deploy does not hoist to /app.
16
+ external: [
17
+ /^@m5kdev\/backend(\/|$)/,
18
+ /^@m5kdev\/commons(\/|$)/,
19
+ /^@m5kdev\/config(\/|$)/,
20
+ "react",
21
+ "react-dom",
22
+ ],
23
+ });
@@ -30,6 +30,11 @@ VAPID_SUBJECT=mailto:you@example.com
30
30
  # Optional: FCM (Android) — service account JSON path
31
31
  # FIREBASE_SERVICE_ACCOUNT_PATH=
32
32
 
33
+ # Optional OpenRouter (AI Conversation). Required to get an assistant reply.
34
+ # m5k:ai:start
35
+ OPENROUTER_API_KEY=
36
+ # m5k:ai:end
37
+
33
38
  # Optional email and analytics providers
34
39
  RESEND_API_KEY=
35
40
  VITE_PUBLIC_POSTHOG_KEY=demo
@@ -0,0 +1,23 @@
1
+ # Copy to .env.production and fill in values. This file is not copied into the
2
+ # image (.dockerignore excludes .env*). app:deploy forwards every key as a Fly
3
+ # build secret; app:secrets imports the same file as runtime secrets.
4
+
5
+ # Database (Fly volume at /app/data)
6
+ DATABASE_URL=file:/app/data/local.db
7
+
8
+ # Redis — set as a Fly runtime secret (do not bake into the image)
9
+ REDIS_URL=
10
+
11
+ # Auth
12
+ BETTER_AUTH_SECRET=
13
+
14
+ # Public URLs (baked into the webapp at image build)
15
+ VITE_APP_NAME={{APP_NAME}}
16
+ VITE_APP_URL=
17
+ VITE_SERVER_URL=
18
+ VITE_ENABLE_WAITLIST=false
19
+
20
+ # Optional
21
+ RESEND_API_KEY=
22
+ VITE_PUBLIC_POSTHOG_KEY=
23
+ VITE_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
@@ -12,6 +12,11 @@ DATABASE_URL=file:./local.db
12
12
  TURSO_DATABASE_URL=
13
13
  TURSO_AUTH_TOKEN=
14
14
 
15
+ # m5k:ai:start
16
+ # Optional OpenRouter (AI Conversation). Required to get an assistant reply.
17
+ OPENROUTER_API_KEY=
18
+ # m5k:ai:end
19
+
15
20
  # Local email delivery falls back to file output when unset
16
21
  RESEND_API_KEY=
17
22
 
@@ -0,0 +1,48 @@
1
+ # syntax = docker/dockerfile:1
2
+
3
+ FROM node:24-slim AS base
4
+
5
+ LABEL fly_launch_runtime="NodeJS"
6
+
7
+ WORKDIR /app
8
+
9
+ ENV NODE_ENV=production
10
+
11
+ FROM base AS build
12
+
13
+ RUN apt-get update -qq && \
14
+ apt-get install -y python-is-python3 pkg-config build-essential ca-certificates && \
15
+ update-ca-certificates
16
+
17
+ RUN npm install -g pnpm@10.13.1
18
+
19
+ COPY --link . .
20
+
21
+ RUN pnpm install --frozen-lockfile --prod=false
22
+ RUN pnpm --filter={{PACKAGE_SCOPE}}/server build
23
+
24
+ # Frontend bake needs VITE_* from the operator env file. Fly cannot declare
25
+ # an unknown set of --mount=type=secret,id=<each key>, so the wrapper also
26
+ # passes ALL_SECRETS (export KEY=value for every key) and still forwards each
27
+ # key as --build-secret.
28
+ # m5k:webapp:start
29
+ RUN --mount=type=secret,id=ALL_SECRETS,required=false \
30
+ if [ -f /run/secrets/ALL_SECRETS ]; then . /run/secrets/ALL_SECRETS; fi && \
31
+ pnpm --filter={{PACKAGE_SCOPE}}/webapp build
32
+ # m5k:webapp:end
33
+
34
+ RUN pnpm --filter={{PACKAGE_SCOPE}}/server deploy --prod --legacy /deploy
35
+ RUN cp -r /app/apps/server/dist/. /deploy
36
+
37
+ # m5k:webapp:start
38
+ RUN cp -r /app/apps/webapp/dist /deploy/client
39
+ # m5k:webapp:end
40
+
41
+ FROM base
42
+
43
+ COPY --from=build /deploy /app
44
+ COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
45
+
46
+ WORKDIR /app
47
+
48
+ CMD ["node", "index.js"]
@@ -0,0 +1,24 @@
1
+ app = "{{APP_SLUG}}-app"
2
+ primary_region = "iad"
3
+
4
+ [build]
5
+
6
+ [env]
7
+ PORT = "8080"
8
+
9
+ [[mounts]]
10
+ source = "libsql_data"
11
+ destination = "/app/data"
12
+
13
+ [http_service]
14
+ internal_port = 8080
15
+ force_https = true
16
+ auto_stop_machines = "stop"
17
+ auto_start_machines = true
18
+ min_machines_running = 0
19
+ processes = ["app"]
20
+
21
+ [[vm]]
22
+ memory = "1gb"
23
+ cpu_kind = "shared"
24
+ cpus = 1
@@ -1,24 +1,25 @@
1
- {
2
- "name": "{{PACKAGE_SCOPE}}/shared",
3
- "version": "0.0.0",
4
- "private": true,
5
- "scripts": {
6
- "build": "tsc --noEmit",
7
- "lint": "biome check .",
8
- "lint:fix": "biome check . --write",
9
- "check-types": "tsc --noEmit"
10
- },
11
- "dependencies": {
12
- "@m5kdev/commons": "catalog:",
13
- "zod": "catalog:"
14
- },
15
- "devDependencies": {
16
- "@m5kdev/config": "catalog:",
17
- "tslib": "catalog:",
18
- "typescript": "catalog:"
19
- },
20
- "exports": {
21
- "./modules/app/*": "./src/modules/app/*.ts",
22
- "./modules/posts/*": "./src/modules/posts/*.ts"
23
- }
24
- }
1
+ {
2
+ "name": "{{PACKAGE_SCOPE}}/shared",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "build": "tsc --noEmit",
7
+ "lint": "biome check .",
8
+ "lint:fix": "biome check . --write",
9
+ "check-types": "tsc --noEmit"
10
+ },
11
+ "dependencies": {
12
+ "@m5kdev/commons": "catalog:m5kdev",
13
+ "zod": "catalog:m5kdev"
14
+ },
15
+ "devDependencies": {
16
+ "@m5kdev/config": "catalog:m5kdev",
17
+ "tslib": "catalog:m5kdev",
18
+ "typescript": "catalog:m5kdev"
19
+ },
20
+ "exports": {
21
+ "./modules/app/*": "./src/modules/app/*.ts",
22
+ "./modules/conversation/*": "./src/modules/conversation/*.ts",
23
+ "./modules/posts/*": "./src/modules/posts/*.ts"
24
+ }
25
+ }
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from "node:child_process";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { parseArgs } from "node:util";
5
+
6
+ function missingEnvMessage(envPath) {
7
+ return `Missing ${envPath}. Copy the .env.production.example next to it, fill in values, then retry.`;
8
+ }
9
+
10
+ function parseDotenv(source) {
11
+ const vars = {};
12
+ for (const rawLine of source.split(/\r?\n/)) {
13
+ const line = rawLine.trim();
14
+ if (!line || line.startsWith("#")) continue;
15
+ const eq = line.indexOf("=");
16
+ if (eq === -1) continue;
17
+ const key = line.slice(0, eq).trim();
18
+ let value = line.slice(eq + 1);
19
+ if (
20
+ (value.startsWith('"') && value.endsWith('"')) ||
21
+ (value.startsWith("'") && value.endsWith("'"))
22
+ ) {
23
+ value = value.slice(1, -1);
24
+ }
25
+ vars[key] = value;
26
+ }
27
+ return vars;
28
+ }
29
+
30
+ function shellSingleQuote(value) {
31
+ return `'${value.replaceAll("'", `'\\''`)}'`;
32
+ }
33
+
34
+ function toExportScript(vars) {
35
+ return Object.entries(vars)
36
+ .map(([key, value]) => `export ${key}=${shellSingleQuote(value)}`)
37
+ .join("\n");
38
+ }
39
+
40
+ const { values } = parseArgs({
41
+ options: {
42
+ config: { type: "string" },
43
+ dockerfile: { type: "string" },
44
+ env: { type: "string" },
45
+ },
46
+ });
47
+
48
+ if (!values.config || !values.dockerfile || !values.env) {
49
+ console.error(
50
+ "Usage: fly-deploy.mjs --config <fly.toml> --dockerfile <Dockerfile> --env <.env.production>"
51
+ );
52
+ process.exit(1);
53
+ }
54
+
55
+ if (!existsSync(values.env)) {
56
+ console.error(missingEnvMessage(values.env));
57
+ process.exit(1);
58
+ }
59
+
60
+ const vars = parseDotenv(readFileSync(values.env, "utf8"));
61
+ const args = [
62
+ "deploy",
63
+ "-c",
64
+ values.config,
65
+ "--dockerfile",
66
+ values.dockerfile,
67
+ "--build-secret",
68
+ `ALL_SECRETS=${toExportScript(vars)}`,
69
+ ];
70
+ for (const [key, value] of Object.entries(vars)) {
71
+ args.push("--build-secret", `${key}=${value}`);
72
+ }
73
+
74
+ const result = spawnSync("fly", args, { stdio: "inherit" });
75
+ process.exit(result.status ?? 1);
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from "node:child_process";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { parseArgs } from "node:util";
5
+
6
+ function missingEnvMessage(envPath) {
7
+ return `Missing ${envPath}. Copy the .env.production.example next to it, fill in values, then retry.`;
8
+ }
9
+
10
+ const { values } = parseArgs({
11
+ options: {
12
+ config: { type: "string" },
13
+ env: { type: "string" },
14
+ },
15
+ });
16
+
17
+ if (!values.config || !values.env) {
18
+ console.error("Usage: fly-secrets.mjs --config <fly.toml> --env <.env.production>");
19
+ process.exit(1);
20
+ }
21
+
22
+ if (!existsSync(values.env)) {
23
+ console.error(missingEnvMessage(values.env));
24
+ process.exit(1);
25
+ }
26
+
27
+ const result = spawnSync("fly", ["secrets", "import", "-c", values.config], {
28
+ input: readFileSync(values.env),
29
+ stdio: ["pipe", "inherit", "inherit"],
30
+ });
31
+ process.exit(result.status ?? 1);
@@ -1,28 +1,28 @@
1
- import { defineAuthRoles } from "@m5kdev/commons/modules/auth/auth.roles";
2
-
3
- export const APP_NAME = "{{APP_NAME}}";
4
- export const APP_SLUG = "{{APP_SLUG}}";
5
-
6
- export const APP_LOCALE_CONFIG = {
7
- defaultLocale: "en",
8
- locales: [
9
- { code: "en", displayName: "English" },
10
- { code: "en_GB", displayName: "English (UK)" },
11
- ],
12
- } as const;
13
-
14
- export const APP_ROLES_CONFIG = defineAuthRoles({
15
- user: {
16
- roles: ["user", "admin"],
17
- },
18
- organization: {
19
- roles: ["member", "admin", "owner"],
20
- managerRoles: ["admin", "owner"],
21
- assignableRoles: ["member", "admin", "owner"],
22
- defaultRole: "member",
23
- },
24
- team: {
25
- roles: ["owner"],
26
- defaultRole: "owner",
27
- },
28
- });
1
+ import { defineAuthRoles } from "@m5kdev/commons/modules/auth/auth.roles";
2
+
3
+ export const APP_NAME = "{{APP_NAME}}";
4
+ export const APP_SLUG = "{{APP_SLUG}}";
5
+
6
+ export const APP_LOCALE_CONFIG = {
7
+ defaultLocale: "en",
8
+ locales: [
9
+ { code: "en", displayName: "English" },
10
+ { code: "en_GB", displayName: "English (UK)" },
11
+ ],
12
+ } as const;
13
+
14
+ export const APP_ROLES_CONFIG = defineAuthRoles({
15
+ user: {
16
+ roles: ["user", "admin"],
17
+ },
18
+ organization: {
19
+ roles: ["member", "admin", "owner"],
20
+ managerRoles: ["admin", "owner"],
21
+ assignableRoles: ["member", "admin", "owner"],
22
+ defaultRole: "member",
23
+ },
24
+ team: {
25
+ roles: ["owner"],
26
+ defaultRole: "owner",
27
+ },
28
+ });
@@ -0,0 +1 @@
1
+ export const STARTER_ASSISTANT_AGENT_ID = "assistant" as const;
@@ -1,3 +1,3 @@
1
- /** Re-export shared push-notification contracts from commons. */
2
- export * from "@m5kdev/commons/modules/notification/notification.constants";
3
- export * from "@m5kdev/commons/modules/notification/notification.schema";
1
+ /** Re-export shared push-notification contracts from commons. */
2
+ export * from "@m5kdev/commons/modules/notification/notification.constants";
3
+ export * from "@m5kdev/commons/modules/notification/notification.schema";
@@ -1,5 +1,5 @@
1
- export const POST_STATUS_VALUES = ["draft", "published"] as const;
2
-
3
- export const POST_FILTER_VALUES = ["all", ...POST_STATUS_VALUES] as const;
4
-
5
- export const POSTS_PAGE_SIZE = 6;
1
+ export const POST_STATUS_VALUES = ["draft", "published"] as const;
2
+
3
+ export const POST_FILTER_VALUES = ["all", ...POST_STATUS_VALUES] as const;
4
+
5
+ export const POSTS_PAGE_SIZE = 6;
@@ -1,25 +1,25 @@
1
- import { z } from "zod";
2
- import { POST_STATUS_VALUES } from "./posts.constants";
3
-
4
- /**
5
- * Shared schemas have a deliberately narrow role: server row/list types are
6
- * inferred from the tRPC router (AppRouter), and the API surface is defined by
7
- * the server module's DTO. A schema lives here only when the frontend needs it
8
- * at runtime (form validation) or when both sides genuinely share it — the
9
- * DTO imports these create/update schemas so the contract cannot drift.
10
- */
11
- export const postStatusSchema = z.enum(POST_STATUS_VALUES);
12
- export type PostStatusSchema = z.infer<typeof postStatusSchema>;
13
-
14
- export const postCreateInputSchema = z.object({
15
- title: z.string().min(1),
16
- slug: z.string().optional(),
17
- excerpt: z.string().optional(),
18
- content: z.string().min(1),
19
- });
20
- export type PostCreateInputSchema = z.infer<typeof postCreateInputSchema>;
21
-
22
- export const postUpdateInputSchema = postCreateInputSchema.extend({
23
- id: z.string(),
24
- });
25
- export type PostUpdateInputSchema = z.infer<typeof postUpdateInputSchema>;
1
+ import { z } from "zod";
2
+ import { POST_STATUS_VALUES } from "./posts.constants";
3
+
4
+ /**
5
+ * Shared schemas have a deliberately narrow role: server row/list types are
6
+ * inferred from the tRPC router (AppRouter), and the API surface is defined by
7
+ * the server module's DTO. A schema lives here only when the frontend needs it
8
+ * at runtime (form validation) or when both sides genuinely share it — the
9
+ * DTO imports these create/update schemas so the contract cannot drift.
10
+ */
11
+ export const postStatusSchema = z.enum(POST_STATUS_VALUES);
12
+ export type PostStatusSchema = z.infer<typeof postStatusSchema>;
13
+
14
+ export const postCreateInputSchema = z.object({
15
+ title: z.string().min(1),
16
+ slug: z.string().optional(),
17
+ excerpt: z.string().optional(),
18
+ content: z.string().min(1),
19
+ });
20
+ export type PostCreateInputSchema = z.infer<typeof postCreateInputSchema>;
21
+
22
+ export const postUpdateInputSchema = postCreateInputSchema.extend({
23
+ id: z.string(),
24
+ });
25
+ export type PostUpdateInputSchema = z.infer<typeof postUpdateInputSchema>;
@@ -1,53 +1,53 @@
1
- {
2
- "name": "{{PACKAGE_SCOPE}}/webapp",
3
- "private": true,
4
- "version": "0.0.0",
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite",
8
- "build": "vite build",
9
- "lint": "biome check .",
10
- "lint:fix": "biome check . --write",
11
- "check-types": "tsc --noEmit",
12
- "preview": "vite preview"
13
- },
14
- "dependencies": {
15
- "{{PACKAGE_SCOPE}}/shared": "workspace:*",
16
- "@heroui/react": "catalog:",
17
- "@heroui/styles": "catalog:",
18
- "@m5kdev/commons": "catalog:",
19
- "@m5kdev/frontend": "catalog:",
20
- "@m5kdev/web-ui": "catalog:",
21
- "@tanstack/react-query": "catalog:",
22
- "@trpc/client": "catalog:",
23
- "@trpc/tanstack-react-query": "catalog:",
24
- "better-auth": "catalog:",
25
- "i18next": "catalog:",
26
- "lucide-react": "catalog:",
27
- "nuqs": "catalog:",
28
- "posthog-js": "catalog:",
29
- "react": "catalog:",
30
- "react-dom": "catalog:",
31
- "react-hook-form": "catalog:",
32
- "react-i18next": "catalog:",
33
- "react-router": "catalog:",
34
- "sonner": "catalog:",
35
- "tailwindcss": "catalog:",
36
- "tw-animate-css": "catalog:",
37
- "zod": "catalog:"
38
- },
39
- "devDependencies": {
40
- "{{PACKAGE_SCOPE}}/server": "workspace:*",
41
- "@m5kdev/config": "catalog:",
42
- "@tailwindcss/vite": "catalog:",
43
- "@types/react": "catalog:",
44
- "@types/react-dom": "catalog:",
45
- "@vitejs/plugin-react": "catalog:",
46
- "babel-plugin-react-compiler": "catalog:",
47
- "typescript": "catalog:",
48
- "vite": "catalog:",
49
- "vite-plugin-i18next-loader": "catalog:",
50
- "vite-tsconfig-paths": "catalog:",
51
- "@trpc/server": "catalog:"
52
- }
53
- }
1
+ {
2
+ "name": "{{PACKAGE_SCOPE}}/webapp",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "lint": "biome check .",
10
+ "lint:fix": "biome check . --write",
11
+ "check-types": "tsc --noEmit",
12
+ "preview": "vite preview"
13
+ },
14
+ "dependencies": {
15
+ "{{PACKAGE_SCOPE}}/shared": "workspace:*",
16
+ "@heroui/react": "catalog:m5kdev",
17
+ "@heroui/styles": "catalog:m5kdev",
18
+ "@m5kdev/commons": "catalog:m5kdev",
19
+ "@m5kdev/frontend": "catalog:m5kdev",
20
+ "@m5kdev/web-ui": "catalog:m5kdev",
21
+ "@tanstack/react-query": "catalog:m5kdev",
22
+ "@trpc/client": "catalog:m5kdev",
23
+ "@trpc/tanstack-react-query": "catalog:m5kdev",
24
+ "better-auth": "catalog:m5kdev",
25
+ "i18next": "catalog:m5kdev",
26
+ "lucide-react": "catalog:m5kdev",
27
+ "nuqs": "catalog:m5kdev",
28
+ "posthog-js": "catalog:m5kdev",
29
+ "react": "catalog:m5kdev",
30
+ "react-dom": "catalog:m5kdev",
31
+ "react-hook-form": "catalog:m5kdev",
32
+ "react-i18next": "catalog:m5kdev",
33
+ "react-router": "catalog:m5kdev",
34
+ "sonner": "catalog:m5kdev",
35
+ "tailwindcss": "catalog:m5kdev",
36
+ "tw-animate-css": "catalog:m5kdev",
37
+ "zod": "catalog:m5kdev"
38
+ },
39
+ "devDependencies": {
40
+ "{{PACKAGE_SCOPE}}/server": "workspace:*",
41
+ "@m5kdev/config": "catalog:m5kdev",
42
+ "@tailwindcss/vite": "catalog:m5kdev",
43
+ "@types/react": "catalog:m5kdev",
44
+ "@types/react-dom": "catalog:m5kdev",
45
+ "@vitejs/plugin-react": "catalog:m5kdev",
46
+ "babel-plugin-react-compiler": "catalog:m5kdev",
47
+ "typescript": "catalog:m5kdev",
48
+ "vite": "catalog:m5kdev",
49
+ "vite-plugin-i18next-loader": "catalog:m5kdev",
50
+ "vite-tsconfig-paths": "catalog:m5kdev",
51
+ "@trpc/server": "catalog:m5kdev"
52
+ }
53
+ }