@plumbus/core 0.1.1 → 0.1.3

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 (146) hide show
  1. package/README.md +163 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/ai/ai-service.d.ts.map +1 -1
  4. package/dist/ai/ai-service.js +1 -0
  5. package/dist/ai/ai-service.js.map +1 -1
  6. package/dist/api/index.d.ts +1 -1
  7. package/dist/api/index.d.ts.map +1 -1
  8. package/dist/api/route-generator.d.ts +5 -1
  9. package/dist/api/route-generator.d.ts.map +1 -1
  10. package/dist/api/route-generator.js +2 -1
  11. package/dist/api/route-generator.js.map +1 -1
  12. package/dist/cli/__tests__/cli.test.js +90 -1
  13. package/dist/cli/__tests__/cli.test.js.map +1 -1
  14. package/dist/cli/__tests__/create.test.js +22 -0
  15. package/dist/cli/__tests__/create.test.js.map +1 -1
  16. package/dist/cli/__tests__/generate.test.js +219 -1
  17. package/dist/cli/__tests__/generate.test.js.map +1 -1
  18. package/dist/cli/__tests__/init.test.js +9 -0
  19. package/dist/cli/__tests__/init.test.js.map +1 -1
  20. package/dist/cli/__tests__/start.test.d.ts +2 -0
  21. package/dist/cli/__tests__/start.test.d.ts.map +1 -0
  22. package/dist/cli/__tests__/start.test.js +91 -0
  23. package/dist/cli/__tests__/start.test.js.map +1 -0
  24. package/dist/cli/__tests__/utils.test.js +70 -2
  25. package/dist/cli/__tests__/utils.test.js.map +1 -1
  26. package/dist/cli/cli.d.ts.map +1 -1
  27. package/dist/cli/cli.js +16 -1
  28. package/dist/cli/cli.js.map +1 -1
  29. package/dist/cli/commands/create.d.ts.map +1 -1
  30. package/dist/cli/commands/create.js +28 -2
  31. package/dist/cli/commands/create.js.map +1 -1
  32. package/dist/cli/commands/generate.d.ts +26 -1
  33. package/dist/cli/commands/generate.d.ts.map +1 -1
  34. package/dist/cli/commands/generate.js +195 -4
  35. package/dist/cli/commands/generate.js.map +1 -1
  36. package/dist/cli/commands/index.d.ts +3 -0
  37. package/dist/cli/commands/index.d.ts.map +1 -1
  38. package/dist/cli/commands/index.js +5 -0
  39. package/dist/cli/commands/index.js.map +1 -1
  40. package/dist/cli/commands/init.d.ts.map +1 -1
  41. package/dist/cli/commands/init.js +20 -21
  42. package/dist/cli/commands/init.js.map +1 -1
  43. package/dist/cli/commands/run.d.ts +3 -0
  44. package/dist/cli/commands/run.d.ts.map +1 -0
  45. package/dist/cli/commands/run.js +147 -0
  46. package/dist/cli/commands/run.js.map +1 -0
  47. package/dist/cli/commands/start.d.ts +25 -0
  48. package/dist/cli/commands/start.d.ts.map +1 -0
  49. package/dist/cli/commands/start.js +125 -0
  50. package/dist/cli/commands/start.js.map +1 -0
  51. package/dist/cli/commands/translation.d.ts +3 -0
  52. package/dist/cli/commands/translation.d.ts.map +1 -0
  53. package/dist/cli/commands/translation.js +277 -0
  54. package/dist/cli/commands/translation.js.map +1 -0
  55. package/dist/cli/commands/ui.d.ts +8 -2
  56. package/dist/cli/commands/ui.d.ts.map +1 -1
  57. package/dist/cli/commands/ui.js +13 -5
  58. package/dist/cli/commands/ui.js.map +1 -1
  59. package/dist/cli/discover.d.ts +2 -0
  60. package/dist/cli/discover.d.ts.map +1 -1
  61. package/dist/cli/discover.js +11 -1
  62. package/dist/cli/discover.js.map +1 -1
  63. package/dist/cli/index.d.ts +3 -3
  64. package/dist/cli/index.d.ts.map +1 -1
  65. package/dist/cli/index.js +3 -3
  66. package/dist/cli/index.js.map +1 -1
  67. package/dist/cli/templates/resources.d.ts +1 -0
  68. package/dist/cli/templates/resources.d.ts.map +1 -1
  69. package/dist/cli/templates/resources.js +16 -0
  70. package/dist/cli/templates/resources.js.map +1 -1
  71. package/dist/cli/utils.d.ts +15 -0
  72. package/dist/cli/utils.d.ts.map +1 -1
  73. package/dist/cli/utils.js +47 -0
  74. package/dist/cli/utils.js.map +1 -1
  75. package/dist/config/loader.d.ts +2 -0
  76. package/dist/config/loader.d.ts.map +1 -1
  77. package/dist/config/loader.js.map +1 -1
  78. package/dist/data/__tests__/registry.test.js +15 -0
  79. package/dist/data/__tests__/registry.test.js.map +1 -1
  80. package/dist/data/__tests__/repository.test.js +33 -0
  81. package/dist/data/__tests__/repository.test.js.map +1 -1
  82. package/dist/data/registry.d.ts +2 -0
  83. package/dist/data/registry.d.ts.map +1 -1
  84. package/dist/data/registry.js +1 -0
  85. package/dist/data/registry.js.map +1 -1
  86. package/dist/data/repository.d.ts +2 -0
  87. package/dist/data/repository.d.ts.map +1 -1
  88. package/dist/data/repository.js +3 -3
  89. package/dist/data/repository.js.map +1 -1
  90. package/dist/define/__tests__/defineTranslation.test.d.ts +2 -0
  91. package/dist/define/__tests__/defineTranslation.test.d.ts.map +1 -0
  92. package/dist/define/__tests__/defineTranslation.test.js +90 -0
  93. package/dist/define/__tests__/defineTranslation.test.js.map +1 -0
  94. package/dist/define/defineTranslation.d.ts +21 -0
  95. package/dist/define/defineTranslation.d.ts.map +1 -0
  96. package/dist/define/defineTranslation.js +67 -0
  97. package/dist/define/defineTranslation.js.map +1 -0
  98. package/dist/define/index.d.ts +1 -0
  99. package/dist/define/index.d.ts.map +1 -1
  100. package/dist/define/index.js +1 -0
  101. package/dist/define/index.js.map +1 -1
  102. package/dist/execution/context-factory.d.ts +2 -0
  103. package/dist/execution/context-factory.d.ts.map +1 -1
  104. package/dist/execution/context-factory.js +5 -0
  105. package/dist/execution/context-factory.js.map +1 -1
  106. package/dist/fields/index.d.ts +1 -0
  107. package/dist/fields/index.d.ts.map +1 -1
  108. package/dist/fields/index.js +1 -0
  109. package/dist/fields/index.js.map +1 -1
  110. package/dist/index.d.ts +3 -1
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +15 -6
  113. package/dist/index.js.map +1 -1
  114. package/dist/server/bootstrap.d.ts.map +1 -1
  115. package/dist/server/bootstrap.js +7 -2
  116. package/dist/server/bootstrap.js.map +1 -1
  117. package/dist/testing/context.js +2 -2
  118. package/dist/testing/context.js.map +1 -1
  119. package/dist/testing/index.d.ts +3 -3
  120. package/dist/testing/index.d.ts.map +1 -1
  121. package/dist/testing/index.js +1 -1
  122. package/dist/testing/index.js.map +1 -1
  123. package/dist/translations/__tests__/translation-resolver.test.d.ts +2 -0
  124. package/dist/translations/__tests__/translation-resolver.test.d.ts.map +1 -0
  125. package/dist/translations/__tests__/translation-resolver.test.js +140 -0
  126. package/dist/translations/__tests__/translation-resolver.test.js.map +1 -0
  127. package/dist/translations/index.d.ts +40 -0
  128. package/dist/translations/index.d.ts.map +1 -0
  129. package/dist/translations/index.js +144 -0
  130. package/dist/translations/index.js.map +1 -0
  131. package/dist/types/context.d.ts +13 -7
  132. package/dist/types/context.d.ts.map +1 -1
  133. package/dist/types/fields.d.ts +1 -0
  134. package/dist/types/fields.d.ts.map +1 -1
  135. package/dist/types/flow.d.ts +2 -0
  136. package/dist/types/flow.d.ts.map +1 -1
  137. package/dist/types/index.d.ts +1 -0
  138. package/dist/types/index.d.ts.map +1 -1
  139. package/dist/types/translation.d.ts +28 -0
  140. package/dist/types/translation.d.ts.map +1 -0
  141. package/dist/types/translation.js +6 -0
  142. package/dist/types/translation.js.map +1 -0
  143. package/instructions/cli.md +24 -0
  144. package/instructions/deployment.md +332 -0
  145. package/instructions/translations.md +78 -0
  146. package/package.json +2 -2
@@ -0,0 +1,332 @@
1
+ # Deployment
2
+
3
+ Plumbus applications deploy as up to three services — a **backend** (Fastify API server), a **frontend** (Next.js), and optionally an **admin dashboard** (Next.js) — backed by PostgreSQL and Redis. This document covers production configuration, containerization, and operational readiness.
4
+
5
+ ## Service Topology
6
+
7
+ ```
8
+ +-----------+ +-----------+ +-----------+
9
+ | frontend | | admin | | backend |
10
+ | Next.js | | Next.js | | Fastify |
11
+ | :3001 |---->| :3002 |---->| :3000 |
12
+ +-----------+ +-----------+ +-----+-----+
13
+ |
14
+ +-----+-----+
15
+ | |
16
+ +---v---+ +----v--+
17
+ | PG | | Redis |
18
+ | :5432 | | :6379 |
19
+ +-------+ +-------+
20
+ ```
21
+
22
+ - **Frontend** and **admin** proxy API calls to the backend via server-side route handlers (`app/api/[...path]/route.ts`). Browsers never connect directly to the backend.
23
+ - **Backend** connects to PostgreSQL (data) and Redis (job queue, event dispatch).
24
+
25
+ ## Backend Deployment
26
+
27
+ ### Production Command
28
+
29
+ Use `plumbus start` for production. It forces production environment, binds to `0.0.0.0`, and requires `AUTH_SECRET`:
30
+
31
+ | Behavior | `plumbus dev` | `plumbus start` |
32
+ |----------|---------------|------------------|
33
+ | Environment | `development` | `production` |
34
+ | Default host | `localhost` | `0.0.0.0` |
35
+ | Log level | `debug` | `info` |
36
+ | DB SSL | off | on |
37
+ | `AUTH_SECRET` | optional (uses default) | **required** (throws on startup) |
38
+ | Cookies | `secure: false` | `secure: true` |
39
+
40
+ ```bash
41
+ npx plumbus start --port 3000
42
+ ```
43
+
44
+ ### Runtime File Requirements
45
+
46
+ The backend uses dynamic resource discovery at startup — it scans the source tree for capabilities, entities, flows, events, and prompts. The following directories **must be present** in the production image:
47
+
48
+ | Directory | Purpose |
49
+ |-----------|---------|
50
+ | `app/` | Capabilities, entities, flows, events, prompts (scanned at startup) |
51
+ | `config/` | `app.config.ts`, `ai.config.ts` |
52
+ | `.plumbus/` | Generated manifests and type definitions |
53
+ | `drizzle/` | Database migration SQL files |
54
+ | `node_modules/` | Runtime dependencies including `@plumbus/core` |
55
+ | `dist/` | Compiled TypeScript output |
56
+
57
+ ### Entrypoint Pattern
58
+
59
+ **CRITICAL**: Do **not** run migrations automatically in the container entrypoint. In multi-replica deployments (Kubernetes, ECS), concurrent migration runs can corrupt data or cause extended downtime. Run migrations as a separate, deliberate step before deploying new containers.
60
+
61
+ ```sh
62
+ # Run migrations BEFORE deploying new containers (CI/CD step or one-off job):
63
+ npx plumbus migrate apply
64
+
65
+ # Then deploy. Container entrypoint only starts the server:
66
+ #!/bin/sh
67
+ set -e
68
+ exec npx plumbus start --port 3000
69
+ ```
70
+
71
+ ### Health Endpoints
72
+
73
+ | Endpoint | Purpose | Healthy response |
74
+ |----------|---------|------------------|
75
+ | `GET /health` | Liveness — server is running | `{ "status": "ok" }` |
76
+ | `GET /ready` | Readiness — database connected | `{ "status": "ready" }` |
77
+
78
+ Use `/ready` for container health checks and load balancer probes.
79
+
80
+ ## Frontend Deployment
81
+
82
+ ### Standalone Output
83
+
84
+ **CRITICAL**: Add `output: "standalone"` to `next.config.mjs` for containerized or self-hosted deployment. Without this, the build requires the full `node_modules` at runtime.
85
+
86
+ ```js
87
+ const nextConfig = {
88
+ output: "standalone",
89
+ // ... existing config
90
+ };
91
+ ```
92
+
93
+ ### Build and Run
94
+
95
+ ```bash
96
+ # Build (inlines NEXT_PUBLIC_* at compile time)
97
+ npx next build
98
+
99
+ # Run standalone server
100
+ node .next/standalone/<app-dir>/server.js
101
+ ```
102
+
103
+ Set `PORT` and `HOSTNAME` environment variables to control the listening address:
104
+
105
+ ```bash
106
+ PORT=3001 HOSTNAME=0.0.0.0 node .next/standalone/frontend/server.js
107
+ ```
108
+
109
+ ### Static Assets
110
+
111
+ After a standalone build, static files live in `.next/static/`. Copy them to the correct path in your deployment:
112
+
113
+ ```
114
+ .next/standalone/ # Server code
115
+ <app-dir>/.next/static/ # Static assets (must be alongside server)
116
+ ```
117
+
118
+ ### Environment Variables
119
+
120
+ - `NEXT_PUBLIC_*` variables are inlined at **build time**. Set them before `next build`, not at runtime.
121
+ - `API_BASE_URL` (server-only) controls the backend URL for server-side proxy routes. Set this at runtime to the internal Docker network address (e.g., `http://backend:3000`).
122
+ - `AUTH_SECRET` is required for cookie signing.
123
+
124
+ ## Environment Variables Reference
125
+
126
+ ### Backend
127
+
128
+ | Variable | Required | Default | Description |
129
+ |----------|----------|---------|-------------|
130
+ | `NODE_ENV` | Yes | `development` | Set to `production` for production |
131
+ | `DB_HOST` | Yes | `localhost` | PostgreSQL host |
132
+ | `DB_PORT` | No | `5432` | PostgreSQL port |
133
+ | `DB_NAME` | No | App name | Database name |
134
+ | `DB_USER` | Yes | `postgres` | Database user |
135
+ | `DB_PASSWORD` | Yes | — | Database password |
136
+ | `QUEUE_HOST` | Yes | `localhost` | Redis host |
137
+ | `QUEUE_PORT` | No | `6379` | Redis port |
138
+ | `AUTH_SECRET` | **Prod** | — | JWT signing secret (required in production) |
139
+ | `AI_DEFAULT_PROVIDER` | No | `openai` | AI provider name |
140
+ | `AI_OPENAI_API_KEY` | If using OpenAI | — | OpenAI API key |
141
+ | `AI_OPENAI_MODEL` | No | `gpt-4o-mini` | Default OpenAI model |
142
+ | `AI_DEFAULT_MODEL` | No | `gpt-4o` | Fallback model |
143
+
144
+ ### Frontend / Admin
145
+
146
+ | Variable | Build/Runtime | Description |
147
+ |----------|---------------|-------------|
148
+ | `NEXT_PUBLIC_API_BASE_URL` | Build | Public API URL (empty string if using server-side proxy) |
149
+ | `API_BASE_URL` | Runtime | Backend URL for server-side proxy (e.g., `http://backend:3000`) |
150
+ | `AUTH_SECRET` | Runtime | Cookie signing secret |
151
+
152
+ ## Docker
153
+
154
+ ### Backend Dockerfile (multi-stage)
155
+
156
+ ```dockerfile
157
+ # ---- Base ----
158
+ FROM node:22-alpine AS base
159
+ RUN corepack enable && corepack prepare pnpm@latest --activate
160
+ WORKDIR /app
161
+
162
+ # ---- Dependencies ----
163
+ FROM base AS deps
164
+ # Do NOT copy pnpm-workspace.yaml — it may contain local dev overrides
165
+ # (link:../Plumbus/...) that don't exist in Docker. Packages resolve from npm.
166
+ COPY package.json pnpm-lock.yaml ./
167
+ RUN pnpm install
168
+
169
+ # ---- Build ----
170
+ FROM base AS build
171
+ COPY --from=deps /app/node_modules ./node_modules
172
+ COPY package.json tsconfig.json ./
173
+ COPY app/ ./app/
174
+ COPY config/ ./config/
175
+ COPY .plumbus/ ./.plumbus/
176
+ COPY drizzle/ ./drizzle/
177
+ RUN pnpm build
178
+
179
+ # ---- Runner ----
180
+ FROM node:22-alpine AS runner
181
+ RUN corepack enable && corepack prepare pnpm@latest --activate
182
+ WORKDIR /app
183
+ ENV NODE_ENV=production
184
+ RUN addgroup --system --gid 1001 plumbus && \
185
+ adduser --system --uid 1001 plumbus
186
+ COPY --from=deps /app/node_modules ./node_modules
187
+ COPY --from=build /app/dist ./dist
188
+ COPY package.json ./
189
+ COPY app/ ./app/
190
+ COPY config/ ./config/
191
+ COPY .plumbus/ ./.plumbus/
192
+ COPY drizzle/ ./drizzle/
193
+ # Entrypoint starts the production server
194
+ USER plumbus
195
+ EXPOSE 3000
196
+ HEALTHCHECK CMD curl -f http://localhost:3000/ready || exit 1
197
+ ENTRYPOINT ["./entrypoint.sh"]
198
+ ```
199
+
200
+ ### Frontend Dockerfile (Next.js standalone)
201
+
202
+ ```dockerfile
203
+ FROM node:22-alpine AS base
204
+ RUN corepack enable && corepack prepare pnpm@latest --activate
205
+ WORKDIR /app
206
+
207
+ FROM base AS deps
208
+ COPY package.json pnpm-lock.yaml ./
209
+ COPY frontend/package.json ./frontend/
210
+ # Generate a clean workspace config — don't copy the host's pnpm-workspace.yaml
211
+ RUN printf 'packages:\n - "frontend"\n' > pnpm-workspace.yaml
212
+ RUN pnpm install
213
+
214
+ FROM base AS build
215
+ COPY --from=deps /app/node_modules ./node_modules
216
+ COPY --from=deps /app/frontend/node_modules ./frontend/node_modules
217
+ COPY frontend/ ./frontend/
218
+ WORKDIR /app/frontend
219
+ RUN npx next build
220
+
221
+ FROM node:22-alpine AS runner
222
+ WORKDIR /app
223
+ ENV NODE_ENV=production
224
+ RUN addgroup --system --gid 1001 nextjs && \
225
+ adduser --system --uid 1001 nextjs
226
+ COPY --from=build /app/frontend/.next/standalone ./
227
+ COPY --from=build /app/frontend/.next/static ./frontend/.next/static
228
+ USER nextjs
229
+ EXPOSE 3001
230
+ CMD ["node", "frontend/server.js"]
231
+ ```
232
+
233
+ ### Docker Compose Topology
234
+
235
+ ```yaml
236
+ services:
237
+ postgres:
238
+ image: postgres:16-alpine
239
+ volumes: [postgres_data:/var/lib/postgresql/data]
240
+ redis:
241
+ image: redis:7-alpine
242
+ backend:
243
+ build: { dockerfile: Dockerfile.backend }
244
+ depends_on: [postgres, redis]
245
+ volumes: [uploads_data:/app/uploads]
246
+ frontend:
247
+ build: { dockerfile: Dockerfile.frontend }
248
+ depends_on: [backend]
249
+ admin:
250
+ build: { dockerfile: Dockerfile.admin }
251
+ depends_on: [backend]
252
+ ```
253
+
254
+ ### Key Docker Considerations
255
+
256
+ - **Do NOT copy `pnpm-workspace.yaml`** into Docker images. It may contain local dev overrides (`link:../Plumbus/...`) that don't exist in the build context. The backend Dockerfile doesn't need it at all. Frontend Dockerfiles generate a minimal workspace config inline (`printf 'packages: ["frontend"]' > pnpm-workspace.yaml`) to declare the sub-package.
257
+ - **Dependency resolution**: `@plumbus/core` and `@plumbus/ui` are declared as normal dependencies in `package.json`. Without the workspace overrides, `pnpm install` resolves them from the npm registry.
258
+ - **Volumes**: Mount persistent volumes for PostgreSQL data and the `uploads/` directory (user-generated media and exports).
259
+ - **Internal networking**: Frontends connect to backend via Docker service name (e.g., `http://backend:3000`). Set `API_BASE_URL` accordingly.
260
+ - **`NEXT_PUBLIC_API_BASE_URL`**: Set to empty string (`""`) if the frontend uses server-side proxy routes. The proxy route handler reads `API_BASE_URL` at runtime.
261
+ - **File layout**: Place all Docker files (Dockerfiles, compose, entrypoint, env template) in a `docker/` subdirectory. The `.dockerignore` stays at the project root (the build context). Set `context: ..` (parent) in compose `build:` sections so COPY paths resolve from the project root.
262
+
263
+ ## Kubernetes / Helm
264
+
265
+ The Docker images are self-contained and orchestrator-agnostic. When deploying to Kubernetes:
266
+
267
+ ### Required Resources
268
+
269
+ | Resource | Purpose |
270
+ |----------|---------|
271
+ | `Deployment` (backend) + `Service` | Plumbus API server — stateless, scales horizontally |
272
+ | `Deployment` (frontend) + `Service` | Next.js user-facing app — stateless |
273
+ | `Deployment` (admin) + `Service` | Next.js admin dashboard — stateless |
274
+ | `Ingress` | Routes traffic: `/api` -> backend, `/` -> frontend, `/admin` -> admin |
275
+ | `Job` (migrations) | One-off pre-deploy step — runs `npx plumbus migrate apply` |
276
+ | `Secret` | `DB_PASSWORD`, `AUTH_SECRET`, AI provider keys |
277
+ | `ConfigMap` | Non-sensitive config: `DB_HOST`, `AI_DEFAULT_PROVIDER`, etc. |
278
+
279
+ ### Migrations in Kubernetes — Critical
280
+
281
+ **NEVER** run migrations in the container entrypoint or as an init container that runs on every pod replica. In a rolling deployment with multiple pods starting concurrently, parallel migration runs against the same database can corrupt data or cause extended downtime.
282
+
283
+ Run migrations as a **separate Kubernetes Job** before rolling out new pods:
284
+
285
+ ```yaml
286
+ apiVersion: batch/v1
287
+ kind: Job
288
+ metadata:
289
+ name: app-migrate
290
+ spec:
291
+ template:
292
+ spec:
293
+ containers:
294
+ - name: migrate
295
+ image: your-registry/app-backend:v1.0.0
296
+ command: ["npx", "plumbus", "migrate", "apply"]
297
+ envFrom:
298
+ - secretRef:
299
+ name: app-backend-secrets
300
+ restartPolicy: Never
301
+ backoffLimit: 1
302
+ ```
303
+
304
+ ### Health Probes
305
+
306
+ ```yaml
307
+ livenessProbe:
308
+ httpGet: { path: /health, port: 3000 }
309
+ periodSeconds: 30
310
+ readinessProbe:
311
+ httpGet: { path: /ready, port: 3000 }
312
+ periodSeconds: 10
313
+ ```
314
+
315
+ ### Scaling Notes
316
+
317
+ - **Backend**: Stateless — safe to scale. All replicas must share the same `AUTH_SECRET`.
318
+ - **Frontends**: Stateless — scale freely behind a load balancer.
319
+ - **Uploads**: Replace the local `uploads/` directory with object storage (S3, GCS) or a `ReadWriteMany` PersistentVolumeClaim.
320
+ - **PostgreSQL / Redis**: Use managed services (RDS, ElastiCache, etc.) or official Helm charts — not the Docker Compose images.
321
+
322
+ ## Production Checklist
323
+
324
+ - [ ] `plumbus start` used as production server command
325
+ - [ ] `AUTH_SECRET` set to a strong random value (32+ characters)
326
+ - [ ] Database SSL enabled (automatic with `plumbus start`)
327
+ - [ ] `plumbus migrate apply` run as a separate step before deployment (NOT in entrypoint)
328
+ - [ ] Health checks configured for `/ready` endpoint
329
+ - [ ] `output: "standalone"` in all `next.config.mjs` files
330
+ - [ ] Persistent volumes for PostgreSQL data and uploads
331
+ - [ ] AI provider API keys configured
332
+ - [ ] Redis available for job queue and event dispatch
@@ -0,0 +1,78 @@
1
+ # Translations
2
+
3
+ A translation is a type-safe i18n message catalog defined with `defineTranslation()`.
4
+
5
+ ## Defining a Translation
6
+
7
+ ```ts
8
+ import { defineTranslation } from "@plumbus/core";
9
+
10
+ export const commonTranslation = defineTranslation({
11
+ name: "common",
12
+ defaultLocale: "en",
13
+ locales: ["en", "he"],
14
+ messages: {
15
+ en: {
16
+ "nav.overview": "Overview",
17
+ "greeting": "Hello {name}",
18
+ "items": "{count, plural, one {# item} other {# items}}",
19
+ },
20
+ he: {
21
+ "nav.overview": "סקירה",
22
+ "greeting": "שלום {name}",
23
+ "items": "{count, plural, one {פריט #} other {# פריטים}}",
24
+ },
25
+ },
26
+ });
27
+ ```
28
+
29
+ ## Rules
30
+
31
+ - Place translation files in `app/translations/<name>.translation.ts`
32
+ - Each file exports one `defineTranslation()` result
33
+ - All locales **must** have the same key set — mismatched keys throw at import time
34
+ - Messages use ICU MessageFormat: `{name}` for interpolation, `{count, plural, ...}` for plurals, `{gender, select, ...}` for select
35
+ - The `name` field is the namespace: keys resolve as `<namespace>.<key>` (e.g., `common.nav.overview`)
36
+
37
+ ## Server-Side Usage
38
+
39
+ In capability handlers, use `ctx.translations.t()`:
40
+
41
+ ```ts
42
+ throw ctx.errors.notFound(ctx.translations.t("errors.projectNotFound"));
43
+ ```
44
+
45
+ The locale is resolved from the request context.
46
+
47
+ ## CLI Commands
48
+
49
+ | Command | Description |
50
+ |---------|-------------|
51
+ | `plumbus translation new <name>` | Scaffold a new translation file |
52
+ | `plumbus translation export` | Export to JSON or XLIFF 2.0 for professional translation |
53
+ | `plumbus translation import` | Import translated JSON/XLIFF files back into source |
54
+ | `plumbus translation status` | Report per-locale completion percentage |
55
+
56
+ ## Frontend
57
+
58
+ When translations exist, `plumbus ui generate` produces `generated/i18n/` with next-intl integration:
59
+
60
+ ```tsx
61
+ import { useTranslations } from "next-intl";
62
+
63
+ function Nav() {
64
+ const t = useTranslations("common");
65
+ return <a>{t("nav.overview")}</a>;
66
+ }
67
+ ```
68
+
69
+ ## Adding a Locale
70
+
71
+ 1. Add the locale to `locales` array in each `defineTranslation()`
72
+ 2. Add the message catalog with all keys
73
+ 3. Run `plumbus translation status` to verify 100%
74
+ 4. Run `plumbus ui generate` to regenerate frontend i18n
75
+
76
+ ## Translation Sync
77
+
78
+ `defineTranslation()` validates key consistency at import time. If locale "he" is missing keys that "en" has, the app throws immediately. This ensures tests and CI catch translation drift.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumbus/core",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Plumbus framework core — types, SDK, runtime, CLI, test utilities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -58,6 +58,7 @@
58
58
  },
59
59
  "dependencies": {
60
60
  "commander": "^12.1.0",
61
+ "drizzle-kit": "^0.31.9",
61
62
  "drizzle-orm": "^0.45.1",
62
63
  "fastify": "^5.8.2",
63
64
  "playwright": "^1.52.0",
@@ -70,7 +71,6 @@
70
71
  "@biomejs/biome": "^2.4.6",
71
72
  "@types/node": "^25.4.0",
72
73
  "@vitest/browser": "^3.0.0",
73
- "drizzle-kit": "^0.31.9",
74
74
  "typescript": "^5.7.0"
75
75
  }
76
76
  }