@mesh-tech/mesh-cli 0.1.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 (140) hide show
  1. package/dist/bin/mesh.d.ts +16 -0
  2. package/dist/bin/mesh.d.ts.map +1 -0
  3. package/dist/bin/mesh.js +40 -0
  4. package/dist/bin/mesh.js.map +1 -0
  5. package/dist/src/commands/create-app.d.ts +10 -0
  6. package/dist/src/commands/create-app.d.ts.map +1 -0
  7. package/dist/src/commands/create-app.js +176 -0
  8. package/dist/src/commands/create-app.js.map +1 -0
  9. package/dist/src/commands/db/connect.d.ts +13 -0
  10. package/dist/src/commands/db/connect.d.ts.map +1 -0
  11. package/dist/src/commands/db/connect.js +68 -0
  12. package/dist/src/commands/db/connect.js.map +1 -0
  13. package/dist/src/commands/db/credentials.d.ts +16 -0
  14. package/dist/src/commands/db/credentials.d.ts.map +1 -0
  15. package/dist/src/commands/db/credentials.js +56 -0
  16. package/dist/src/commands/db/credentials.js.map +1 -0
  17. package/dist/src/commands/db/env.d.ts +15 -0
  18. package/dist/src/commands/db/env.d.ts.map +1 -0
  19. package/dist/src/commands/db/env.js +50 -0
  20. package/dist/src/commands/db/env.js.map +1 -0
  21. package/dist/src/commands/db/exec.d.ts +54 -0
  22. package/dist/src/commands/db/exec.d.ts.map +1 -0
  23. package/dist/src/commands/db/exec.js +331 -0
  24. package/dist/src/commands/db/exec.js.map +1 -0
  25. package/dist/src/commands/db/index.d.ts +12 -0
  26. package/dist/src/commands/db/index.d.ts.map +1 -0
  27. package/dist/src/commands/db/index.js +62 -0
  28. package/dist/src/commands/db/index.js.map +1 -0
  29. package/dist/src/commands/db/psql.d.ts +16 -0
  30. package/dist/src/commands/db/psql.d.ts.map +1 -0
  31. package/dist/src/commands/db/psql.js +133 -0
  32. package/dist/src/commands/db/psql.js.map +1 -0
  33. package/dist/src/commands/deploy.d.ts +24 -0
  34. package/dist/src/commands/deploy.d.ts.map +1 -0
  35. package/dist/src/commands/deploy.js +228 -0
  36. package/dist/src/commands/deploy.js.map +1 -0
  37. package/dist/src/commands/dev.d.ts +17 -0
  38. package/dist/src/commands/dev.d.ts.map +1 -0
  39. package/dist/src/commands/dev.js +1248 -0
  40. package/dist/src/commands/dev.js.map +1 -0
  41. package/dist/src/commands/devbox.d.ts +22 -0
  42. package/dist/src/commands/devbox.d.ts.map +1 -0
  43. package/dist/src/commands/devbox.js +463 -0
  44. package/dist/src/commands/devbox.js.map +1 -0
  45. package/dist/src/commands/login.d.ts +47 -0
  46. package/dist/src/commands/login.d.ts.map +1 -0
  47. package/dist/src/commands/login.js +344 -0
  48. package/dist/src/commands/login.js.map +1 -0
  49. package/dist/src/commands/registry.d.ts +20 -0
  50. package/dist/src/commands/registry.d.ts.map +1 -0
  51. package/dist/src/commands/registry.js +336 -0
  52. package/dist/src/commands/registry.js.map +1 -0
  53. package/dist/src/commands/secrets/exec.d.ts +17 -0
  54. package/dist/src/commands/secrets/exec.d.ts.map +1 -0
  55. package/dist/src/commands/secrets/exec.js +132 -0
  56. package/dist/src/commands/secrets/exec.js.map +1 -0
  57. package/dist/src/commands/secrets/index.d.ts +10 -0
  58. package/dist/src/commands/secrets/index.d.ts.map +1 -0
  59. package/dist/src/commands/secrets/index.js +35 -0
  60. package/dist/src/commands/secrets/index.js.map +1 -0
  61. package/dist/src/commands/secrets/set.d.ts +22 -0
  62. package/dist/src/commands/secrets/set.d.ts.map +1 -0
  63. package/dist/src/commands/secrets/set.js +234 -0
  64. package/dist/src/commands/secrets/set.js.map +1 -0
  65. package/dist/src/commands/stack.d.ts +17 -0
  66. package/dist/src/commands/stack.d.ts.map +1 -0
  67. package/dist/src/commands/stack.js +175 -0
  68. package/dist/src/commands/stack.js.map +1 -0
  69. package/dist/src/commands/tunnel/index.d.ts +14 -0
  70. package/dist/src/commands/tunnel/index.d.ts.map +1 -0
  71. package/dist/src/commands/tunnel/index.js +217 -0
  72. package/dist/src/commands/tunnel/index.js.map +1 -0
  73. package/dist/src/index.d.ts +7 -0
  74. package/dist/src/index.d.ts.map +1 -0
  75. package/dist/src/index.js +7 -0
  76. package/dist/src/index.js.map +1 -0
  77. package/dist/src/utils/bastion.d.ts +43 -0
  78. package/dist/src/utils/bastion.d.ts.map +1 -0
  79. package/dist/src/utils/bastion.js +77 -0
  80. package/dist/src/utils/bastion.js.map +1 -0
  81. package/dist/src/utils/context.d.ts +29 -0
  82. package/dist/src/utils/context.d.ts.map +1 -0
  83. package/dist/src/utils/context.js +132 -0
  84. package/dist/src/utils/context.js.map +1 -0
  85. package/dist/src/utils/credentials.d.ts +61 -0
  86. package/dist/src/utils/credentials.d.ts.map +1 -0
  87. package/dist/src/utils/credentials.js +145 -0
  88. package/dist/src/utils/credentials.js.map +1 -0
  89. package/dist/src/utils/index.d.ts +9 -0
  90. package/dist/src/utils/index.d.ts.map +1 -0
  91. package/dist/src/utils/index.js +9 -0
  92. package/dist/src/utils/index.js.map +1 -0
  93. package/dist/src/utils/log.d.ts +8 -0
  94. package/dist/src/utils/log.d.ts.map +1 -0
  95. package/dist/src/utils/log.js +17 -0
  96. package/dist/src/utils/log.js.map +1 -0
  97. package/dist/src/utils/pulumi.d.ts +24 -0
  98. package/dist/src/utils/pulumi.d.ts.map +1 -0
  99. package/dist/src/utils/pulumi.js +63 -0
  100. package/dist/src/utils/pulumi.js.map +1 -0
  101. package/package.json +50 -0
  102. package/templates/api-web-db/api/.dockerignore +2 -0
  103. package/templates/api-web-db/api/Dockerfile +16 -0
  104. package/templates/api-web-db/api/package.json.hbs +21 -0
  105. package/templates/api-web-db/api/src/index.ts.hbs +123 -0
  106. package/templates/api-web-db/api/tsconfig.json +13 -0
  107. package/templates/api-web-db/package.json.hbs +27 -0
  108. package/templates/api-web-db/sst.config.ts.hbs +151 -0
  109. package/templates/api-web-db/tsconfig.json +11 -0
  110. package/templates/api-web-db/web/.dockerignore +3 -0
  111. package/templates/api-web-db/web/Dockerfile +17 -0
  112. package/templates/api-web-db/web/app/lib/db.server.ts +36 -0
  113. package/templates/api-web-db/web/app/root.tsx +29 -0
  114. package/templates/api-web-db/web/app/routes/home.tsx.hbs +114 -0
  115. package/templates/api-web-db/web/app/routes.ts +3 -0
  116. package/templates/api-web-db/web/package.json.hbs +30 -0
  117. package/templates/api-web-db/web/prisma/schema.prisma +25 -0
  118. package/templates/api-web-db/web/prisma.config.ts +18 -0
  119. package/templates/api-web-db/web/react-router.config.ts +5 -0
  120. package/templates/api-web-db/web/tsconfig.json +19 -0
  121. package/templates/api-web-db/web/vite.config.ts +9 -0
  122. package/templates/temporal-api-worker/README.md.hbs +133 -0
  123. package/templates/temporal-api-worker/api/.dockerignore +4 -0
  124. package/templates/temporal-api-worker/api/Dockerfile +13 -0
  125. package/templates/temporal-api-worker/api/package.json.hbs +23 -0
  126. package/templates/temporal-api-worker/api/src/encryption-codec.ts +133 -0
  127. package/templates/temporal-api-worker/api/src/index.ts.hbs +145 -0
  128. package/templates/temporal-api-worker/api/tsconfig.json +14 -0
  129. package/templates/temporal-api-worker/package.json.hbs +29 -0
  130. package/templates/temporal-api-worker/sst.config.ts.hbs +161 -0
  131. package/templates/temporal-api-worker/tsconfig.json +10 -0
  132. package/templates/temporal-api-worker/worker/.dockerignore +4 -0
  133. package/templates/temporal-api-worker/worker/Dockerfile +11 -0
  134. package/templates/temporal-api-worker/worker/package.json.hbs +25 -0
  135. package/templates/temporal-api-worker/worker/src/activities.ts.hbs +49 -0
  136. package/templates/temporal-api-worker/worker/src/bundle-workflows.ts +38 -0
  137. package/templates/temporal-api-worker/worker/src/encryption-codec.ts +156 -0
  138. package/templates/temporal-api-worker/worker/src/worker.ts.hbs +93 -0
  139. package/templates/temporal-api-worker/worker/src/workflows.ts.hbs +60 -0
  140. package/templates/temporal-api-worker/worker/tsconfig.json +14 -0
@@ -0,0 +1,151 @@
1
+ /// <reference path="./.sst/platform/config.d.ts" />
2
+
3
+ export default $config({
4
+ app(input) {
5
+ return {
6
+ name: "{{name}}",
7
+ removal: input?.stage === "prod" ? "retain" : "remove",
8
+ protect: ["prod"].includes(input?.stage ?? ""),
9
+ home: "aws",
10
+ providers: {
11
+ aws: {
12
+ region: "us-east-1",
13
+ },
14
+ },
15
+ };
16
+ },
17
+ async run() {
18
+ // Dynamic imports (SST requirement for non-SST packages)
19
+ const { mesh } = await import("@mesh-tech/infra-components");
20
+ const { config, getPlatform } = await import("../config.js");
21
+
22
+ const stage = $app.stage;
23
+ const tenant = config.tenant;
24
+ const platform = getPlatform(stage);
25
+
26
+ // Create tenant environment (isolated per-app namespace)
27
+ // DevCommand enables unified dev tunnel (mesh tunnel dev) for DB access
28
+ const env = new mesh.apps.AppEnvironment("env", {
29
+ tenant,
30
+ platform,
31
+ stage,
32
+ appName: "{{name}}",
33
+ dev: $dev,
34
+ namespace: true,
35
+ DevCommand: sst.x.DevCommand,
36
+ });
37
+
38
+ // Create database
39
+ // Credentials are injected at runtime via envFromSecrets:
40
+ // - Dev mode: mesh db exec fetches from Secrets Manager
41
+ // - K8s mode: ExternalSecrets syncs to K8s Secret, envFrom injects
42
+ const db = new mesh.apps.Database("db", {
43
+ env,
44
+ });
45
+
46
+ // Prisma DevCommand for migrations and Studio
47
+ // Usage: npx sst shell --target Prisma -- npx prisma <command>
48
+ // Uses mesh db exec to load DATABASE_URL from secret (same as web/api)
49
+ new sst.x.DevCommand("Prisma", {
50
+ dev: {
51
+ title: "prisma",
52
+ command: $interpolate`pnpm exec mesh db exec --secret=${db.secretArn} --ssl=require -- npx prisma studio`,
53
+ directory: "./web",
54
+ autostart: false,
55
+ },
56
+ });
57
+
58
+ // ==========================================================================
59
+ // API Service (Hono)
60
+ // ==========================================================================
61
+ const api = new mesh.apps.Service(
62
+ "api",
63
+ {
64
+ dev: $dev,
65
+ tenant,
66
+ stage,
67
+ appName: "{{name}}",
68
+ namespace: env.namespace!,
69
+ rootDir: $cli.paths.root,
70
+ src: "./api",
71
+ port: 3001,
72
+ devCommand: ["pnpm", "dev"],
73
+ environment: {
74
+ APP_NAME: "api",
75
+ STAGE: stage,
76
+ NODE_ENV: $dev ? "development" : "production",
77
+ },
78
+ // Inject DB credentials via envFromSecrets (works in both dev and K8s mode)
79
+ // Dev: mesh db exec fetches from Secrets Manager at runtime
80
+ // K8s: ExternalSecret syncs to K8s Secret, envFrom injects
81
+ envFromSecrets: $dev ? [db.secretArn] : [db.k8sSecretName!],
82
+ ingress: env.buildIngress({
83
+ subdomain: "{{name}}-api",
84
+ healthCheckPath: "/health",
85
+ }),
86
+ waitForDns: true,
87
+ replicas: 1,
88
+ resources: {
89
+ cpu: $dev ? "50m" : "100m",
90
+ memory: "128Mi",
91
+ },
92
+ },
93
+ {
94
+ providers: env.providers,
95
+ parent: env,
96
+ },
97
+ );
98
+
99
+ // ==========================================================================
100
+ // Web Service (React Router v7)
101
+ // ==========================================================================
102
+ const web = new mesh.apps.Service(
103
+ "web",
104
+ {
105
+ dev: $dev,
106
+ tenant,
107
+ stage,
108
+ appName: "{{name}}",
109
+ namespace: env.namespace!,
110
+ rootDir: $cli.paths.root,
111
+ src: "./web",
112
+ port: 3000,
113
+ devCommand: ["pnpm", "dev"],
114
+ environment: {
115
+ APP_NAME: "web",
116
+ STAGE: stage,
117
+ NODE_ENV: $dev ? "development" : "production",
118
+ API_URL: api.serviceEndpoint ?? api.url,
119
+ PUBLIC_API_URL: api.url,
120
+ },
121
+ // Inject DB credentials via envFromSecrets (works in both dev and K8s mode)
122
+ // Dev: mesh db exec fetches from Secrets Manager at runtime
123
+ // K8s: ExternalSecret syncs to K8s Secret, envFrom injects
124
+ envFromSecrets: $dev ? [db.secretArn] : [db.k8sSecretName!],
125
+ ingress: env.buildIngress({
126
+ subdomain: "{{name}}",
127
+ healthCheckPath: "/",
128
+ }),
129
+ waitForDns: true,
130
+ replicas: 1,
131
+ resources: {
132
+ cpu: $dev ? "50m" : "200m",
133
+ memory: $dev ? "128Mi" : "256Mi",
134
+ },
135
+ },
136
+ {
137
+ providers: env.providers,
138
+ parent: env,
139
+ dependsOn: [api],
140
+ },
141
+ );
142
+
143
+ return {
144
+ namespace: env.namespace,
145
+ apiUrl: api.url,
146
+ webUrl: web.url,
147
+ databaseUrl: db.url,
148
+ databaseName: db.name,
149
+ };
150
+ },
151
+ });
@@ -0,0 +1,11 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "skipLibCheck": true
9
+ },
10
+ "include": ["sst.config.ts"]
11
+ }
@@ -0,0 +1,3 @@
1
+ node_modules
2
+ build
3
+ .react-router
@@ -0,0 +1,17 @@
1
+ FROM node:22-alpine AS builder
2
+ WORKDIR /app
3
+ RUN corepack enable && corepack prepare pnpm@latest --activate
4
+ COPY package.json pnpm-lock.yaml* ./
5
+ RUN pnpm install
6
+ COPY . .
7
+ RUN pnpm build
8
+
9
+ FROM node:22-alpine
10
+ WORKDIR /app
11
+ RUN corepack enable && corepack prepare pnpm@latest --activate
12
+ COPY package.json pnpm-lock.yaml* ./
13
+ RUN pnpm install --prod --frozen-lockfile || pnpm install --prod
14
+ COPY --from=builder /app/build ./build
15
+ ENV NODE_ENV=production PORT=3000
16
+ EXPOSE 3000
17
+ CMD ["pnpm", "start"]
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Database connection for server-side code (loaders/actions)
3
+ *
4
+ * Uses Prisma v7 adapter pattern with DATABASE_URL from environment.
5
+ * - Dev mode: DATABASE_URL includes password (set by mesh db exec from SecretImport)
6
+ * - K8s mode: DATABASE_URL comes from ExternalSecret via envFrom
7
+ *
8
+ * This pattern eliminates runtime Secrets Manager calls.
9
+ */
10
+
11
+ import { Pool } from "pg";
12
+ import { PrismaClient } from "../../src/generated/prisma/client";
13
+ import { PrismaPg } from "@prisma/adapter-pg";
14
+
15
+ let prisma: PrismaClient | null = null;
16
+
17
+ export function getDb(): PrismaClient {
18
+ if (!prisma) {
19
+ // Create pg Pool with SSL for RDS (required even through SSM tunnel)
20
+ const pool = new Pool({
21
+ connectionString: process.env.DATABASE_URL,
22
+ ssl: { rejectUnauthorized: false },
23
+ });
24
+ const adapter = new PrismaPg(pool);
25
+ prisma = new PrismaClient({ adapter });
26
+ }
27
+ return prisma;
28
+ }
29
+
30
+ // Cleanup function for graceful shutdown
31
+ export async function closeDb(): Promise<void> {
32
+ if (prisma) {
33
+ await prisma.$disconnect();
34
+ prisma = null;
35
+ }
36
+ }
@@ -0,0 +1,29 @@
1
+ import {
2
+ Links,
3
+ Meta,
4
+ Outlet,
5
+ Scripts,
6
+ ScrollRestoration,
7
+ } from "react-router";
8
+
9
+ export function Layout({ children }: { children: React.ReactNode }) {
10
+ return (
11
+ <html lang="en">
12
+ <head>
13
+ <meta charSet="utf-8" />
14
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
15
+ <Meta />
16
+ <Links />
17
+ </head>
18
+ <body style={{ fontFamily: "system-ui, sans-serif", margin: 0 }}>
19
+ {children}
20
+ <ScrollRestoration />
21
+ <Scripts />
22
+ </body>
23
+ </html>
24
+ );
25
+ }
26
+
27
+ export default function App() {
28
+ return <Outlet />;
29
+ }
@@ -0,0 +1,114 @@
1
+ import type { Route } from "./+types/home";
2
+
3
+ export function meta({}: Route.MetaArgs) {
4
+ return [
5
+ { title: "{{titleCase name}}" },
6
+ { name: "description", content: "{{titleCase name}} - Mesh App" },
7
+ ];
8
+ }
9
+
10
+ export async function loader({}: Route.LoaderArgs) {
11
+ const apiUrl = process.env.API_URL || "http://localhost:3001";
12
+
13
+ // Fetch API hello endpoint
14
+ let apiData = { message: "API unavailable", timestamp: null };
15
+ try {
16
+ const response = await fetch(`${apiUrl}/hello`);
17
+ apiData = await response.json();
18
+ } catch (error) {
19
+ console.error("API fetch error:", error);
20
+ }
21
+
22
+ // Fetch database status
23
+ let dbData = { status: "unknown", database: null, user: null, version: null };
24
+ try {
25
+ const response = await fetch(`${apiUrl}/db`);
26
+ dbData = await response.json();
27
+ } catch (error) {
28
+ console.error("DB fetch error:", error);
29
+ dbData = { status: "error", database: null, user: null, version: null };
30
+ }
31
+
32
+ return {
33
+ message: apiData.message,
34
+ timestamp: apiData.timestamp,
35
+ stage: process.env.STAGE || "unknown",
36
+ db: dbData,
37
+ };
38
+ }
39
+
40
+ export default function Home({ loaderData }: Route.ComponentProps) {
41
+ const { message, timestamp, stage, db } = loaderData;
42
+ const dbConnected = db?.status === "connected";
43
+
44
+ return (
45
+ <div style=\{{ padding: "2rem", maxWidth: "600px", margin: "0 auto" }}>
46
+ <h1 style=\{{ fontSize: "2rem", marginBottom: "1rem" }}>{{titleCase name}}</h1>
47
+
48
+ <div
49
+ style=\{{
50
+ background: "#f0f9ff",
51
+ border: "1px solid #0ea5e9",
52
+ borderRadius: "8px",
53
+ padding: "1rem",
54
+ marginBottom: "1rem",
55
+ }}
56
+ >
57
+ <h2 style=\{{ margin: "0 0 0.5rem 0", fontSize: "1rem" }}>
58
+ Web (React Router v7)
59
+ </h2>
60
+ <p style=\{{ margin: 0, color: "#666" }}>Stage: {stage}</p>
61
+ </div>
62
+
63
+ <div
64
+ style=\{{
65
+ background: "#f0fdf4",
66
+ border: "1px solid #22c55e",
67
+ borderRadius: "8px",
68
+ padding: "1rem",
69
+ marginBottom: "1rem",
70
+ }}
71
+ >
72
+ <h2 style=\{{ margin: "0 0 0.5rem 0", fontSize: "1rem" }}>
73
+ API Response (Hono)
74
+ </h2>
75
+ <p style=\{{ margin: "0 0 0.25rem 0" }}>Message: {message}</p>
76
+ {timestamp && (
77
+ <p style=\{{ margin: 0, color: "#666", fontSize: "0.875rem" }}>
78
+ Timestamp: {timestamp}
79
+ </p>
80
+ )}
81
+ </div>
82
+
83
+ <div
84
+ style=\{{
85
+ background: dbConnected ? "#fdf4ff" : "#fef2f2",
86
+ border: `1px solid ${dbConnected ? "#a855f7" : "#ef4444"}`,
87
+ borderRadius: "8px",
88
+ padding: "1rem",
89
+ }}
90
+ >
91
+ <h2 style=\{{ margin: "0 0 0.5rem 0", fontSize: "1rem" }}>
92
+ Database (RDS)
93
+ </h2>
94
+ <p style=\{{ margin: "0 0 0.25rem 0" }}>
95
+ Status: {dbConnected ? "Connected" : `${db?.status || "Unknown"}`}
96
+ </p>
97
+ {dbConnected && (
98
+ <>
99
+ <p style=\{{ margin: "0 0 0.25rem 0", color: "#666", fontSize: "0.875rem" }}>
100
+ Database: {db.database}
101
+ </p>
102
+ <p style=\{{ margin: 0, color: "#666", fontSize: "0.875rem" }}>
103
+ User: {db.user}
104
+ </p>
105
+ </>
106
+ )}
107
+ </div>
108
+
109
+ <p style=\{{ marginTop: "1.5rem", color: "#666", fontSize: "0.875rem" }}>
110
+ Edit files in <code>web/</code> or <code>api/</code> to see live reload!
111
+ </p>
112
+ </div>
113
+ );
114
+ }
@@ -0,0 +1,3 @@
1
+ import { type RouteConfig, index } from "@react-router/dev/routes";
2
+
3
+ export default [index("routes/home.tsx")] satisfies RouteConfig;
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "{{name}}-web",
3
+ "type": "module",
4
+ "scripts": {
5
+ "dev": "react-router dev",
6
+ "build": "react-router build",
7
+ "start": "react-router-serve ./build/server/index.js"
8
+ },
9
+ "dependencies": {
10
+ "@prisma/adapter-pg": "^7.0.0",
11
+ "@prisma/client": "^7.0.0",
12
+ "@react-router/node": "^7.1.1",
13
+ "@react-router/serve": "^7.1.1",
14
+ "isbot": "^5.1.17",
15
+ "pg": "^8.13.0",
16
+ "prisma": "^7.0.0",
17
+ "react": "^19.0.0",
18
+ "react-dom": "^19.0.0",
19
+ "react-router": "^7.1.1"
20
+ },
21
+ "devDependencies": {
22
+ "@react-router/dev": "^7.1.1",
23
+ "@types/node": "^22.10.5",
24
+ "@types/pg": "^8.11.0",
25
+ "@types/react": "^19.0.2",
26
+ "@types/react-dom": "^19.0.2",
27
+ "typescript": "^5.7.2",
28
+ "vite": "^6.0.7"
29
+ }
30
+ }
@@ -0,0 +1,25 @@
1
+ // Prisma v7 Schema
2
+ // Docs: https://www.prisma.io/docs
3
+
4
+ generator client {
5
+ provider = "prisma-client"
6
+ output = "../src/generated/prisma"
7
+ }
8
+
9
+ datasource db {
10
+ provider = "postgresql"
11
+ // Note: url is configured in prisma.config.ts (Prisma v7 pattern)
12
+ }
13
+
14
+ // =============================================================================
15
+ // Example Model - Replace with your domain models
16
+ // =============================================================================
17
+
18
+ model Example {
19
+ id String @id @default(cuid())
20
+ name String
21
+ createdAt DateTime @default(now())
22
+ updatedAt DateTime @updatedAt
23
+
24
+ @@map("examples")
25
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Prisma v7 configuration for CLI commands (migrations, db push, etc.)
3
+ *
4
+ * Usage with SST:
5
+ * npx sst shell --target Prisma -- npx prisma db push
6
+ * npx sst shell --target Prisma -- npx prisma migrate dev --name <name>
7
+ * npx sst shell --target Prisma -- npx prisma studio
8
+ */
9
+
10
+ import { defineConfig } from "prisma/config";
11
+
12
+ export default defineConfig({
13
+ schema: "prisma/schema.prisma",
14
+
15
+ datasource: {
16
+ url: process.env.DATABASE_URL!,
17
+ },
18
+ });
@@ -0,0 +1,5 @@
1
+ import type { Config } from "@react-router/dev/config";
2
+
3
+ export default {
4
+ ssr: true,
5
+ } satisfies Config;
@@ -0,0 +1,19 @@
1
+ {
2
+ "include": [
3
+ "**/*.ts",
4
+ "**/*.tsx",
5
+ ".react-router/types/**/*"
6
+ ],
7
+ "compilerOptions": {
8
+ "target": "ES2022",
9
+ "lib": ["DOM", "DOM.Iterable", "ES2022"],
10
+ "types": ["node", "@react-router/node", "vite/client"],
11
+ "module": "ESNext",
12
+ "moduleResolution": "bundler",
13
+ "jsx": "react-jsx",
14
+ "strict": true,
15
+ "skipLibCheck": true,
16
+ "noEmit": true,
17
+ "rootDirs": [".", "./.react-router/types"]
18
+ }
19
+ }
@@ -0,0 +1,9 @@
1
+ import { reactRouter } from "@react-router/dev/vite";
2
+ import { defineConfig } from "vite";
3
+
4
+ export default defineConfig({
5
+ plugins: [reactRouter()],
6
+ server: {
7
+ port: parseInt(process.env.PORT || "3000", 10),
8
+ },
9
+ });
@@ -0,0 +1,133 @@
1
+ # {{titleCase name}}
2
+
3
+ Temporal workflow application for {{tenant}}.
4
+
5
+ ## Overview
6
+
7
+ This app demonstrates Temporal workflow orchestration with:
8
+ - **Worker**: Executes workflows and activities
9
+ - **API**: HTTP endpoints to trigger workflows
10
+
11
+ ## Prerequisites
12
+
13
+ - MeshPlatform deployed with `temporal: { ... }` enabled
14
+ - AWS credentials with apps-deployer role
15
+
16
+ ## Development
17
+
18
+ ### 1. Start Dev Server
19
+
20
+ ```bash
21
+ pnpm mesh dev
22
+ # Auto-starts unified tunnel (Temporal gRPC:7233, Web UI:8080, DB:5432)
23
+ ```
24
+
25
+ ### 2. Start Worker Manually
26
+
27
+ Workers don't auto-start because they need the Temporal namespace to exist first.
28
+ After `sst dev` is ready:
29
+ 1. Open SST dev UI
30
+ 2. Find the worker service
31
+ 3. Click to start it
32
+
33
+ ### 4. Test Workflows
34
+
35
+ ```bash
36
+ # Trigger greeting workflow
37
+ curl -X POST http://localhost:3000/greet \
38
+ -H "Content-Type: application/json" \
39
+ -d '{"name": "World"}'
40
+
41
+ # Trigger task workflow
42
+ curl -X POST http://localhost:3000/task \
43
+ -H "Content-Type: application/json" \
44
+ -d '{"taskId": "TASK-123", "userId": "user-456"}'
45
+
46
+ # Check workflow status
47
+ curl http://localhost:3000/workflow/task-TASK-123
48
+ ```
49
+
50
+ ### 5. View in Web UI
51
+
52
+ Open http://localhost:8080 and navigate to your namespace.
53
+
54
+ ## Deployment
55
+
56
+ ```bash
57
+ pnpm deploy --stage dev
58
+ ```
59
+
60
+ ## Project Structure
61
+
62
+ ```
63
+ {{name}}/
64
+ ├── sst.config.ts # SST config with AppEnvironment + TemporalWorker
65
+ ├── worker/
66
+ │ ├── src/
67
+ │ │ ├── worker.ts # Worker entry point
68
+ │ │ ├── workflows.ts # Workflow definitions
69
+ │ │ ├── activities.ts # Activity implementations
70
+ │ │ └── encryption-codec.ts # Payload encryption
71
+ │ ├── Dockerfile
72
+ │ └── package.json
73
+ ├── api/
74
+ │ ├── src/
75
+ │ │ ├── index.ts # Express API
76
+ │ │ └── encryption-codec.ts # Payload encryption
77
+ │ ├── Dockerfile
78
+ │ └── package.json
79
+ └── README.md
80
+ ```
81
+
82
+ ## Customization
83
+
84
+ ### Adding Workflows
85
+
86
+ 1. Define workflow in `worker/src/workflows.ts`
87
+ 2. Add activities in `worker/src/activities.ts`
88
+ 3. Add API endpoint in `api/src/index.ts`
89
+
90
+ ### Workflow Patterns
91
+
92
+ ```typescript
93
+ // Simple workflow
94
+ export async function myWorkflow(input: string): Promise<string> {
95
+ const result = await myActivity(input);
96
+ return result;
97
+ }
98
+
99
+ // Workflow with timer
100
+ export async function timedWorkflow(): Promise<void> {
101
+ await doSomething();
102
+ await sleep("1 hour"); // Durable timer
103
+ await doSomethingElse();
104
+ }
105
+
106
+ // Workflow with error handling
107
+ export async function safeWorkflow(): Promise<string> {
108
+ try {
109
+ return await riskyActivity();
110
+ } catch (err) {
111
+ return await fallbackActivity();
112
+ }
113
+ }
114
+ ```
115
+
116
+ ## Troubleshooting
117
+
118
+ ### Worker Can't Connect
119
+
120
+ 1. Ensure tunnel is running in SST dev (check "tunnel" pane)
121
+ 2. Check `TEMPORAL_ADDRESS` in worker logs
122
+ 3. Or start tunnel manually: `pnpm mesh tunnel dev`
123
+
124
+ ### Encrypted Payloads in Web UI
125
+
126
+ Configure codec server in Web UI settings:
127
+ - Dev: `http://localhost:8083`
128
+ - Deploy: Check `codecServerUrl` output
129
+
130
+ ### Workflow Not Found
131
+
132
+ 1. Ensure worker is running
133
+ 2. Check task queue name matches between API and worker
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ dist
3
+ *.log
4
+ .env*
@@ -0,0 +1,13 @@
1
+ FROM node:20-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY package*.json ./
6
+ RUN npm ci --omit=dev
7
+
8
+ COPY . .
9
+ RUN npm run build
10
+
11
+ EXPOSE 3000
12
+
13
+ CMD ["node", "dist/index.js"]
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@{{tenant}}/{{name}}-api",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "tsx watch src/index.ts",
8
+ "start": "node dist/index.js",
9
+ "build": "tsc"
10
+ },
11
+ "dependencies": {
12
+ "@temporalio/client": "^1.11.7",
13
+ "@temporalio/common": "^1.11.7",
14
+ "@temporalio/proto": "^1.11.7",
15
+ "express": "^4.21.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/express": "^4.17.21",
19
+ "@types/node": "^20.0.0",
20
+ "tsx": "^4.0.0",
21
+ "typescript": "^5.0.0"
22
+ }
23
+ }