create-m5kdev 0.33.0 → 0.35.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 (147) hide show
  1. package/dist/src/catalog.cjs +21 -1
  2. package/dist/src/catalog.cjs.map +1 -1
  3. package/dist/src/catalog.d.cts +3 -1
  4. package/dist/src/create.cjs +6 -2
  5. package/dist/src/create.cjs.map +1 -1
  6. package/dist/src/doctor.cjs.map +1 -1
  7. package/dist/src/fs.cjs +10 -5
  8. package/dist/src/fs.cjs.map +1 -1
  9. package/dist/src/prompts.cjs +32 -0
  10. package/dist/src/prompts.cjs.map +1 -1
  11. package/dist/src/prompts.d.cts +7 -1
  12. package/dist/src/template.cjs +23 -4
  13. package/dist/src/template.cjs.map +1 -1
  14. package/dist/src/template.d.cts +17 -3
  15. package/dist/src/types.d.cts +11 -4
  16. package/package.json +4 -1
  17. package/templates/minimal-app/.cursor/rules/frontend-data-guide.mdc +1 -0
  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/.cursor/rules/server-event-emit.mdc +41 -0
  23. package/templates/minimal-app/.dockerignore.tpl +31 -0
  24. package/templates/minimal-app/.gitignore.tpl +1 -0
  25. package/templates/minimal-app/AGENTS.md.tpl +14 -10
  26. package/templates/minimal-app/CLAUDE.md.tpl +133 -131
  27. package/templates/minimal-app/README.md.tpl +51 -19
  28. package/templates/minimal-app/apps/e2e/package.json.tpl +23 -23
  29. package/templates/minimal-app/apps/e2e/playwright.config.ts.tpl +51 -39
  30. package/templates/minimal-app/apps/e2e/playwright.expo.config.ts.tpl +38 -38
  31. package/templates/minimal-app/apps/e2e/servers.ts.tpl +136 -112
  32. package/templates/minimal-app/apps/e2e/tests/conversation.spec.ts.tpl +22 -0
  33. package/templates/minimal-app/apps/e2e/tests/email.spec.ts.tpl +47 -47
  34. package/templates/minimal-app/apps/e2e/tests/expo-helpers.ts.tpl +73 -73
  35. package/templates/minimal-app/apps/e2e/tests/expo-standard.spec.ts.tpl +517 -517
  36. package/templates/minimal-app/apps/e2e/tests/expo-waitlist.spec.ts.tpl +94 -94
  37. package/templates/minimal-app/apps/e2e/tests/files.spec.ts.tpl +35 -0
  38. package/templates/minimal-app/apps/e2e/tests/helpers.ts.tpl +289 -289
  39. package/templates/minimal-app/apps/e2e/tests/standard.spec.ts.tpl +516 -516
  40. package/templates/minimal-app/apps/e2e/tests/waitlist.spec.ts.tpl +107 -107
  41. package/templates/minimal-app/apps/e2e/tests/workflow.spec.ts.tpl +26 -0
  42. package/templates/minimal-app/apps/e2e/tsconfig.json.tpl +10 -10
  43. package/templates/minimal-app/apps/email/package.json.tpl +34 -34
  44. package/templates/minimal-app/apps/email/src/emails/trialEndingEmail.tsx.tpl +40 -0
  45. package/templates/minimal-app/apps/email/src/index.ts.tpl +7 -0
  46. package/templates/minimal-app/apps/email/src/resources.ts.tpl +72 -65
  47. package/templates/minimal-app/apps/expo/app.json.tpl +36 -36
  48. package/templates/minimal-app/apps/expo/eslint.config.js.tpl +8 -8
  49. package/templates/minimal-app/apps/expo/metro.config.js.tpl +16 -16
  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/src/App.tsx.tpl +12 -0
  75. package/templates/minimal-app/apps/landing/src/LandingPage.tsx.tpl +37 -0
  76. package/templates/minimal-app/apps/landing/src/index.css.tpl +24 -0
  77. package/templates/minimal-app/apps/landing/src/main.tsx.tpl +16 -0
  78. package/templates/minimal-app/apps/landing/src/vite-env.d.ts.tpl +11 -0
  79. package/templates/minimal-app/apps/landing/tsconfig.json.tpl +12 -0
  80. package/templates/minimal-app/apps/landing/vite.config.ts.tpl +25 -0
  81. package/templates/minimal-app/apps/server/AGENTS.md.tpl +55 -46
  82. package/templates/minimal-app/apps/server/{drizzle/seed.e2e.ts.tpl → db.e2e.ts.tpl} +57 -40
  83. package/templates/minimal-app/apps/server/{drizzle/seed.ts.tpl → db.ts.tpl} +27 -20
  84. package/templates/minimal-app/apps/server/drizzle/0003_silly_the_liberteens.sql +27 -0
  85. package/templates/minimal-app/apps/server/drizzle/meta/0000_snapshot.json.tpl +56 -166
  86. package/templates/minimal-app/apps/server/drizzle/meta/0001_snapshot.json.tpl +1796 -1910
  87. package/templates/minimal-app/apps/server/drizzle/meta/0002_snapshot.json.tpl +1818 -1932
  88. package/templates/minimal-app/apps/server/drizzle/meta/0003_snapshot.json.tpl +2137 -0
  89. package/templates/minimal-app/apps/server/drizzle/meta/_journal.json.tpl +8 -1
  90. package/templates/minimal-app/apps/server/drizzle.config.ts.tpl +3 -33
  91. package/templates/minimal-app/apps/server/package.json.tpl +5 -3
  92. package/templates/minimal-app/apps/server/src/app.ts.tpl +36 -0
  93. package/templates/minimal-app/apps/server/src/modules/conversation/conversation.mastra.ts.tpl +26 -0
  94. package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.module.ts.tpl +51 -0
  95. package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.service.ts.tpl +51 -0
  96. package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.trpc.ts.tpl +16 -0
  97. package/templates/minimal-app/apps/server/src/modules/posts/posts.db.ts.tpl +28 -28
  98. package/templates/minimal-app/apps/server/src/modules/posts/posts.grants.ts.tpl +28 -28
  99. package/templates/minimal-app/apps/server/src/modules/posts/posts.module.ts.tpl +57 -54
  100. package/templates/minimal-app/apps/server/src/modules/posts/posts.repository.ts.tpl +67 -67
  101. package/templates/minimal-app/apps/server/src/modules/posts/posts.service.ts.tpl +116 -116
  102. package/templates/minimal-app/apps/server/src/modules/posts/posts.trpc.ts.tpl +37 -37
  103. package/templates/minimal-app/apps/server/src/modules/test-harness/test-harness.module.ts.tpl +279 -279
  104. package/templates/minimal-app/apps/server/src/schema.ts.tpl +31 -20
  105. package/templates/minimal-app/apps/server/src/types.ts.tpl +12 -12
  106. package/templates/minimal-app/apps/server/tsconfig.json.tpl +1 -1
  107. package/templates/minimal-app/apps/server/tsup.config.ts.tpl +23 -9
  108. package/templates/minimal-app/apps/shared/.env.example.tpl +5 -0
  109. package/templates/minimal-app/apps/shared/.env.production.example.tpl +23 -0
  110. package/templates/minimal-app/apps/shared/.env.tpl +5 -0
  111. package/templates/minimal-app/apps/shared/Dockerfile.tpl +48 -0
  112. package/templates/minimal-app/apps/shared/fly.toml.tpl +24 -0
  113. package/templates/minimal-app/apps/shared/package.json.tpl +26 -24
  114. package/templates/minimal-app/apps/shared/src/modules/app/app.constants.ts.tpl +28 -28
  115. package/templates/minimal-app/apps/shared/src/modules/conversation/conversation.constants.ts.tpl +1 -0
  116. package/templates/minimal-app/apps/shared/src/modules/demo-workflow/demo-workflow.constants.ts.tpl +1 -0
  117. package/templates/minimal-app/apps/shared/src/modules/notification/notification.schema.ts.tpl +3 -3
  118. package/templates/minimal-app/apps/shared/src/modules/posts/posts.constants.ts.tpl +5 -5
  119. package/templates/minimal-app/apps/shared/src/modules/posts/posts.schema.ts.tpl +25 -25
  120. package/templates/minimal-app/apps/webapp/package.json.tpl +53 -53
  121. package/templates/minimal-app/apps/webapp/public/push-sw.js.tpl +34 -34
  122. package/templates/minimal-app/apps/webapp/src/App.tsx.tpl +49 -44
  123. package/templates/minimal-app/apps/webapp/src/Layout.tsx.tpl +96 -65
  124. package/templates/minimal-app/apps/webapp/src/Router.tsx.tpl +114 -93
  125. package/templates/minimal-app/apps/webapp/src/StarterServerEventProvider.tsx.tpl +38 -0
  126. package/templates/minimal-app/apps/webapp/src/index.css.tpl +15 -15
  127. package/templates/minimal-app/apps/webapp/src/main.tsx.tpl +26 -26
  128. package/templates/minimal-app/apps/webapp/src/modules/conversation/ConversationRoute.tsx.tpl +36 -0
  129. package/templates/minimal-app/apps/webapp/src/modules/files/FilesRoute.tsx.tpl +88 -0
  130. package/templates/minimal-app/apps/webapp/src/modules/files/hooks/useFilesRoute.ts.tpl +50 -0
  131. package/templates/minimal-app/apps/webapp/src/modules/workflows/WorkflowsRoute.tsx.tpl +97 -0
  132. package/templates/minimal-app/apps/webapp/src/modules/workflows/hooks/useWorkflowsRoute.ts.tpl +64 -0
  133. package/templates/minimal-app/apps/webapp/src/utils/i18n.ts.tpl +13 -13
  134. package/templates/minimal-app/apps/webapp/src/utils/trpc.ts.tpl +10 -10
  135. package/templates/minimal-app/apps/webapp/src/vite-env.d.ts.tpl +1 -1
  136. package/templates/minimal-app/apps/webapp/translations/en/app.json.tpl +7 -7
  137. package/templates/minimal-app/apps/webapp/translations/en/{{APP_SLUG}}.json.tpl +193 -135
  138. package/templates/minimal-app/apps/webapp/tsconfig.json.tpl +17 -17
  139. package/templates/minimal-app/apps/webapp/vite.config.ts.tpl +31 -31
  140. package/templates/minimal-app/biome.json.tpl +17 -12
  141. package/templates/minimal-app/package.json.tpl +7 -2
  142. package/templates/minimal-app/pnpm-workspace.yaml.tpl +14 -7
  143. package/templates/minimal-app/template.manifest.json +93 -2
  144. package/templates/minimal-app/apps/server/drizzle/db.ts.tpl +0 -26
  145. package/templates/minimal-app/apps/server/drizzle/guard.ts.tpl +0 -36
  146. package/templates/minimal-app/apps/server/drizzle/reset.ts.tpl +0 -40
  147. package/templates/minimal-app/apps/server/drizzle/sync.ts.tpl +0 -16
@@ -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,26 @@
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/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/demo-workflow/*": "./src/modules/demo-workflow/*.ts",
24
+ "./modules/posts/*": "./src/modules/posts/*.ts"
25
+ }
26
+ }
@@ -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;
@@ -0,0 +1 @@
1
+ export const DEMO_WORKFLOW_SERVER_EVENT_RESOURCE = "workflow";
@@ -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: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
- }
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
+ }
@@ -1,34 +1,34 @@
1
- /* global self */
2
- self.addEventListener("push", (event) => {
3
- let payload = { title: "Notification", body: "" };
4
- try {
5
- if (event.data) {
6
- payload = { ...payload, ...event.data.json() };
7
- }
8
- } catch {
9
- const text = event.data?.text();
10
- if (text) payload = { title: "Notification", body: text };
11
- }
12
- event.waitUntil(
13
- self.registration.showNotification(payload.title, {
14
- body: payload.body,
15
- data: payload.data ?? {},
16
- })
17
- );
18
- });
19
-
20
- self.addEventListener("notificationclick", (event) => {
21
- event.notification.close();
22
- event.waitUntil(
23
- self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clientList) => {
24
- if (clientList.length > 0) {
25
- const client = clientList[0];
26
- if ("focus" in client) return client.focus();
27
- }
28
- if (self.clients.openWindow) {
29
- return self.clients.openWindow("/");
30
- }
31
- return undefined;
32
- })
33
- );
34
- });
1
+ /* global self */
2
+ self.addEventListener("push", (event) => {
3
+ let payload = { title: "Notification", body: "" };
4
+ try {
5
+ if (event.data) {
6
+ payload = { ...payload, ...event.data.json() };
7
+ }
8
+ } catch {
9
+ const text = event.data?.text();
10
+ if (text) payload = { title: "Notification", body: text };
11
+ }
12
+ event.waitUntil(
13
+ self.registration.showNotification(payload.title, {
14
+ body: payload.body,
15
+ data: payload.data ?? {},
16
+ })
17
+ );
18
+ });
19
+
20
+ self.addEventListener("notificationclick", (event) => {
21
+ event.notification.close();
22
+ event.waitUntil(
23
+ self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clientList) => {
24
+ if (clientList.length > 0) {
25
+ const client = clientList[0];
26
+ if ("focus" in client) return client.focus();
27
+ }
28
+ if (self.clients.openWindow) {
29
+ return self.clients.openWindow("/");
30
+ }
31
+ return undefined;
32
+ })
33
+ );
34
+ });
@@ -1,44 +1,49 @@
1
- import { AppConfigProvider } from "@m5kdev/frontend/modules/app/components/AppConfigProvider";
2
- import { AppTrpcQueryProvider } from "@m5kdev/frontend/modules/app/components/AppTrpcQueryProvider";
3
- import { AuthProvider } from "@m5kdev/frontend/modules/auth/components/AuthProvider";
4
- import { DialogProvider } from "@m5kdev/web-ui/components/DialogProvider";
5
- import { ThemeProvider } from "@m5kdev/web-ui/components/theme-provider";
6
- import { AppLoader } from "@m5kdev/web-ui/modules/app/components/AppLoader";
7
- import {
8
- APP_LOCALE_CONFIG,
9
- APP_NAME,
10
- APP_ROLES_CONFIG,
11
- } from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
12
- import { NuqsAdapter } from "nuqs/adapters/react-router/v7";
13
- import { BrowserRouter } from "react-router";
14
- import { Toaster } from "sonner";
15
- import { Router } from "./Router";
16
-
17
- export function App() {
18
- return (
19
- <NuqsAdapter>
20
- <BrowserRouter>
21
- <AppConfigProvider
22
- config={{
23
- appName: APP_NAME,
24
- appUrl: import.meta.env.VITE_APP_URL,
25
- serverUrl: import.meta.env.VITE_SERVER_URL,
26
- locales: APP_LOCALE_CONFIG,
27
- roles: APP_ROLES_CONFIG,
28
- }}
29
- >
30
- <ThemeProvider defaultTheme="light" storageKey="m5kdev-theme">
31
- <AuthProvider loader={<AppLoader />}>
32
- <AppTrpcQueryProvider>
33
- <DialogProvider>
34
- <Router />
35
- </DialogProvider>
36
- <Toaster richColors closeButton />
37
- </AppTrpcQueryProvider>
38
- </AuthProvider>
39
- </ThemeProvider>
40
- </AppConfigProvider>
41
- </BrowserRouter>
42
- </NuqsAdapter>
43
- );
44
- }
1
+ import { Toast } from "@heroui/react";
2
+ import { AppConfigProvider } from "@m5kdev/frontend/modules/app/components/AppConfigProvider";
3
+ import { AppTrpcQueryProvider } from "@m5kdev/frontend/modules/app/components/AppTrpcQueryProvider";
4
+ import { AuthProvider } from "@m5kdev/frontend/modules/auth/components/AuthProvider";
5
+ import { DialogProvider } from "@m5kdev/web-ui/components/DialogProvider";
6
+ import { ThemeProvider } from "@m5kdev/web-ui/components/theme-provider";
7
+ import { AppLoader } from "@m5kdev/web-ui/modules/app/components/AppLoader";
8
+ import {
9
+ APP_LOCALE_CONFIG,
10
+ APP_NAME,
11
+ APP_ROLES_CONFIG,
12
+ } from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
13
+ import { NuqsAdapter } from "nuqs/adapters/react-router/v7";
14
+ import { BrowserRouter } from "react-router";
15
+ import { Toaster } from "sonner";
16
+ import { Router } from "./Router";
17
+ import { StarterServerEventProvider } from "./StarterServerEventProvider";
18
+
19
+ export function App() {
20
+ return (
21
+ <NuqsAdapter>
22
+ <BrowserRouter>
23
+ <AppConfigProvider
24
+ config={{
25
+ appName: APP_NAME,
26
+ appUrl: import.meta.env.VITE_APP_URL,
27
+ serverUrl: import.meta.env.VITE_SERVER_URL,
28
+ locales: APP_LOCALE_CONFIG,
29
+ roles: APP_ROLES_CONFIG,
30
+ }}
31
+ >
32
+ <ThemeProvider defaultTheme="light" storageKey="m5kdev-theme">
33
+ <AuthProvider loader={<AppLoader />}>
34
+ <AppTrpcQueryProvider>
35
+ <StarterServerEventProvider>
36
+ <DialogProvider>
37
+ <Router />
38
+ </DialogProvider>
39
+ <Toaster richColors closeButton />
40
+ <Toast.Provider placement="bottom end" />
41
+ </StarterServerEventProvider>
42
+ </AppTrpcQueryProvider>
43
+ </AuthProvider>
44
+ </ThemeProvider>
45
+ </AppConfigProvider>
46
+ </BrowserRouter>
47
+ </NuqsAdapter>
48
+ );
49
+ }