create-m5kdev 0.33.0 → 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.
- package/dist/src/catalog.cjs +21 -1
- package/dist/src/catalog.cjs.map +1 -1
- package/dist/src/catalog.d.cts +3 -1
- package/dist/src/create.cjs +6 -2
- package/dist/src/create.cjs.map +1 -1
- package/dist/src/doctor.cjs.map +1 -1
- package/dist/src/fs.cjs +10 -5
- package/dist/src/fs.cjs.map +1 -1
- package/dist/src/prompts.cjs +32 -0
- package/dist/src/prompts.cjs.map +1 -1
- package/dist/src/prompts.d.cts +7 -1
- package/dist/src/template.cjs +23 -4
- package/dist/src/template.cjs.map +1 -1
- package/dist/src/template.d.cts +17 -3
- package/dist/src/types.d.cts +11 -4
- package/package.json +4 -1
- package/templates/minimal-app/.cursor/rules/module-dto-guide.mdc +1 -1
- package/templates/minimal-app/.cursor/rules/module-grants-guide.mdc +3 -3
- package/templates/minimal-app/.cursor/rules/module-module-guide.mdc +3 -2
- package/templates/minimal-app/.cursor/rules/module-service-guide.mdc +1 -1
- package/templates/minimal-app/.dockerignore.tpl +31 -0
- package/templates/minimal-app/.gitignore.tpl +1 -0
- package/templates/minimal-app/AGENTS.md.tpl +13 -10
- package/templates/minimal-app/CLAUDE.md.tpl +132 -131
- package/templates/minimal-app/README.md.tpl +50 -19
- package/templates/minimal-app/apps/e2e/package.json.tpl +23 -23
- package/templates/minimal-app/apps/e2e/playwright.config.ts.tpl +51 -39
- package/templates/minimal-app/apps/e2e/playwright.expo.config.ts.tpl +38 -38
- package/templates/minimal-app/apps/e2e/servers.ts.tpl +136 -112
- package/templates/minimal-app/apps/e2e/tests/conversation.spec.ts.tpl +22 -0
- package/templates/minimal-app/apps/e2e/tests/email.spec.ts.tpl +47 -47
- package/templates/minimal-app/apps/e2e/tests/expo-helpers.ts.tpl +73 -73
- package/templates/minimal-app/apps/e2e/tests/expo-standard.spec.ts.tpl +517 -517
- package/templates/minimal-app/apps/e2e/tests/expo-waitlist.spec.ts.tpl +94 -94
- package/templates/minimal-app/apps/e2e/tests/files.spec.ts.tpl +35 -0
- package/templates/minimal-app/apps/e2e/tests/helpers.ts.tpl +289 -289
- package/templates/minimal-app/apps/e2e/tests/standard.spec.ts.tpl +516 -516
- package/templates/minimal-app/apps/e2e/tests/waitlist.spec.ts.tpl +107 -107
- package/templates/minimal-app/apps/e2e/tests/workflow.spec.ts.tpl +26 -0
- package/templates/minimal-app/apps/e2e/tsconfig.json.tpl +10 -10
- package/templates/minimal-app/apps/email/package.json.tpl +34 -34
- package/templates/minimal-app/apps/email/src/emails/trialEndingEmail.tsx.tpl +40 -0
- package/templates/minimal-app/apps/email/src/index.ts.tpl +7 -0
- package/templates/minimal-app/apps/email/src/resources.ts.tpl +72 -65
- package/templates/minimal-app/apps/expo/app.json.tpl +36 -36
- package/templates/minimal-app/apps/expo/eslint.config.js.tpl +8 -8
- package/templates/minimal-app/apps/expo/metro.config.js.tpl +16 -16
- package/templates/minimal-app/apps/expo/src/app/_layout.tsx.tpl +31 -31
- package/templates/minimal-app/apps/expo/src/app/claim-account.tsx.tpl +151 -151
- package/templates/minimal-app/apps/expo/src/app/forgot-password.tsx.tpl +53 -53
- package/templates/minimal-app/apps/expo/src/app/index.tsx.tpl +8 -8
- package/templates/minimal-app/apps/expo/src/app/login.tsx.tpl +71 -71
- package/templates/minimal-app/apps/expo/src/app/logout.tsx.tpl +19 -19
- package/templates/minimal-app/apps/expo/src/app/organization/accept-invitation.tsx.tpl +85 -85
- package/templates/minimal-app/apps/expo/src/app/posts.tsx.tpl +40 -40
- package/templates/minimal-app/apps/expo/src/app/reset-password.tsx.tpl +75 -75
- package/templates/minimal-app/apps/expo/src/app/signup.tsx.tpl +193 -193
- package/templates/minimal-app/apps/expo/src/components/AuthScaffold.tsx.tpl +34 -34
- package/templates/minimal-app/apps/expo/src/components/FormTextField.tsx.tpl +37 -37
- package/templates/minimal-app/apps/expo/src/components/LoadingScreen.tsx.tpl +10 -10
- package/templates/minimal-app/apps/expo/src/components/StatusText.tsx.tpl +22 -22
- package/templates/minimal-app/apps/expo/src/config.ts.tpl +9 -9
- package/templates/minimal-app/apps/expo/src/global.css.tpl +11 -11
- package/templates/minimal-app/apps/expo/src/lib/trpc.ts.tpl +20 -20
- package/templates/minimal-app/apps/expo/tsconfig.json.tpl +11 -11
- package/templates/minimal-app/apps/expo/uniwind-env.d.ts.tpl +1 -1
- package/templates/minimal-app/apps/landing/.env.production.example.tpl +7 -0
- package/templates/minimal-app/apps/landing/Dockerfile.tpl +39 -0
- package/templates/minimal-app/apps/landing/fly.toml.tpl +20 -0
- package/templates/minimal-app/apps/landing/index.html.tpl +35 -0
- package/templates/minimal-app/apps/landing/package.json.tpl +34 -0
- package/templates/minimal-app/apps/landing/scripts/fly-deploy.mjs.tpl +75 -0
- package/templates/minimal-app/apps/landing/scripts/fly-secrets.mjs.tpl +31 -0
- package/templates/minimal-app/apps/landing/src/App.tsx.tpl +12 -0
- package/templates/minimal-app/apps/landing/src/LandingPage.tsx.tpl +37 -0
- package/templates/minimal-app/apps/landing/src/index.css.tpl +24 -0
- package/templates/minimal-app/apps/landing/src/main.tsx.tpl +16 -0
- package/templates/minimal-app/apps/landing/src/vite-env.d.ts.tpl +11 -0
- package/templates/minimal-app/apps/landing/tsconfig.json.tpl +12 -0
- package/templates/minimal-app/apps/landing/vite.config.ts.tpl +25 -0
- package/templates/minimal-app/apps/server/AGENTS.md.tpl +54 -46
- package/templates/minimal-app/apps/server/{drizzle/seed.e2e.ts.tpl → db.e2e.ts.tpl} +57 -40
- package/templates/minimal-app/apps/server/{drizzle/seed.ts.tpl → db.ts.tpl} +27 -20
- package/templates/minimal-app/apps/server/drizzle/0003_silly_the_liberteens.sql +27 -0
- package/templates/minimal-app/apps/server/drizzle/meta/0000_snapshot.json.tpl +56 -166
- package/templates/minimal-app/apps/server/drizzle/meta/0001_snapshot.json.tpl +1796 -1910
- package/templates/minimal-app/apps/server/drizzle/meta/0002_snapshot.json.tpl +1818 -1932
- package/templates/minimal-app/apps/server/drizzle/meta/0003_snapshot.json.tpl +2137 -0
- package/templates/minimal-app/apps/server/drizzle/meta/_journal.json.tpl +8 -1
- package/templates/minimal-app/apps/server/drizzle.config.ts.tpl +3 -33
- package/templates/minimal-app/apps/server/package.json.tpl +5 -3
- package/templates/minimal-app/apps/server/src/app.ts.tpl +36 -0
- package/templates/minimal-app/apps/server/src/modules/conversation/conversation.mastra.ts.tpl +26 -0
- package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.module.ts.tpl +47 -0
- package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.service.ts.tpl +43 -0
- package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.trpc.ts.tpl +16 -0
- package/templates/minimal-app/apps/server/src/modules/posts/posts.db.ts.tpl +28 -28
- package/templates/minimal-app/apps/server/src/modules/posts/posts.grants.ts.tpl +28 -28
- package/templates/minimal-app/apps/server/src/modules/posts/posts.module.ts.tpl +54 -54
- package/templates/minimal-app/apps/server/src/modules/posts/posts.repository.ts.tpl +67 -67
- package/templates/minimal-app/apps/server/src/modules/posts/posts.service.ts.tpl +116 -116
- package/templates/minimal-app/apps/server/src/modules/posts/posts.trpc.ts.tpl +37 -37
- package/templates/minimal-app/apps/server/src/modules/test-harness/test-harness.module.ts.tpl +279 -279
- package/templates/minimal-app/apps/server/src/schema.ts.tpl +31 -20
- package/templates/minimal-app/apps/server/src/types.ts.tpl +12 -12
- package/templates/minimal-app/apps/server/tsconfig.json.tpl +1 -1
- package/templates/minimal-app/apps/server/tsup.config.ts.tpl +23 -9
- package/templates/minimal-app/apps/shared/.env.example.tpl +5 -0
- package/templates/minimal-app/apps/shared/.env.production.example.tpl +23 -0
- package/templates/minimal-app/apps/shared/.env.tpl +5 -0
- package/templates/minimal-app/apps/shared/Dockerfile.tpl +48 -0
- package/templates/minimal-app/apps/shared/fly.toml.tpl +24 -0
- package/templates/minimal-app/apps/shared/package.json.tpl +25 -24
- package/templates/minimal-app/apps/shared/scripts/fly-deploy.mjs.tpl +75 -0
- package/templates/minimal-app/apps/shared/scripts/fly-secrets.mjs.tpl +31 -0
- package/templates/minimal-app/apps/shared/src/modules/app/app.constants.ts.tpl +28 -28
- package/templates/minimal-app/apps/shared/src/modules/conversation/conversation.constants.ts.tpl +1 -0
- package/templates/minimal-app/apps/shared/src/modules/notification/notification.schema.ts.tpl +3 -3
- package/templates/minimal-app/apps/shared/src/modules/posts/posts.constants.ts.tpl +5 -5
- package/templates/minimal-app/apps/shared/src/modules/posts/posts.schema.ts.tpl +25 -25
- package/templates/minimal-app/apps/webapp/package.json.tpl +53 -53
- package/templates/minimal-app/apps/webapp/public/push-sw.js.tpl +34 -34
- package/templates/minimal-app/apps/webapp/src/App.tsx.tpl +46 -44
- package/templates/minimal-app/apps/webapp/src/Layout.tsx.tpl +96 -65
- package/templates/minimal-app/apps/webapp/src/Router.tsx.tpl +114 -93
- package/templates/minimal-app/apps/webapp/src/index.css.tpl +15 -15
- package/templates/minimal-app/apps/webapp/src/main.tsx.tpl +26 -26
- package/templates/minimal-app/apps/webapp/src/modules/conversation/ConversationRoute.tsx.tpl +36 -0
- package/templates/minimal-app/apps/webapp/src/modules/files/FilesRoute.tsx.tpl +88 -0
- package/templates/minimal-app/apps/webapp/src/modules/files/hooks/useFilesRoute.ts.tpl +50 -0
- package/templates/minimal-app/apps/webapp/src/modules/workflows/WorkflowsRoute.tsx.tpl +97 -0
- package/templates/minimal-app/apps/webapp/src/modules/workflows/hooks/useWorkflowsRoute.ts.tpl +64 -0
- package/templates/minimal-app/apps/webapp/src/utils/i18n.ts.tpl +13 -13
- package/templates/minimal-app/apps/webapp/src/utils/trpc.ts.tpl +10 -10
- package/templates/minimal-app/apps/webapp/src/vite-env.d.ts.tpl +1 -1
- package/templates/minimal-app/apps/webapp/translations/en/app.json.tpl +7 -7
- package/templates/minimal-app/apps/webapp/translations/en/{{APP_SLUG}}.json.tpl +193 -135
- package/templates/minimal-app/apps/webapp/tsconfig.json.tpl +17 -17
- package/templates/minimal-app/apps/webapp/vite.config.ts.tpl +31 -31
- package/templates/minimal-app/biome.json.tpl +17 -12
- package/templates/minimal-app/package.json.tpl +6 -2
- package/templates/minimal-app/pnpm-workspace.yaml.tpl +14 -7
- package/templates/minimal-app/template.manifest.json +92 -2
- package/templates/minimal-app/apps/server/drizzle/db.ts.tpl +0 -26
- package/templates/minimal-app/apps/server/drizzle/guard.ts.tpl +0 -36
- package/templates/minimal-app/apps/server/drizzle/reset.ts.tpl +0 -40
- 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,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: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/
|
|
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
|
+
});
|
package/templates/minimal-app/apps/shared/src/modules/conversation/conversation.constants.ts.tpl
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const STARTER_ASSISTANT_AGENT_ID = "assistant" as const;
|
package/templates/minimal-app/apps/shared/src/modules/notification/notification.schema.ts.tpl
CHANGED
|
@@ -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
|
+
});
|