@voltro/cli 0.27.0 → 0.28.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 (58) hide show
  1. package/CHANGELOG.md +235 -0
  2. package/dist/apiBuild-DgBS9ayv.js +2 -0
  3. package/dist/{apiBuild-D22_EpoR.js → apiBuild-eUM32r1u.js} +2 -2
  4. package/dist/bin.js +2 -2
  5. package/dist/{commands-jBX8no1I.js → commands-CSkrUI1h.js} +2430 -2238
  6. package/dist/{dbCommand-uuNCrFAb.js → dbCommand-CpYgmSw4.js} +1 -1
  7. package/dist/dbCommand-DvguqlzF.js +2 -0
  8. package/dist/{dev-DcbIJrWg.js → dev-CEZwJhmb.js} +2774 -2548
  9. package/dist/dev-DlBWWnJQ.js +3 -0
  10. package/dist/index.js +1 -1
  11. package/dist/{seedRunner-D6eu-u5U.js → seedRunner-Bqxgp7HZ.js} +60 -59
  12. package/dist/serveCommand-ZTn-dPFa.js +1425 -0
  13. package/dist/serveEntry.js +2 -2
  14. package/package.json +17 -17
  15. package/templates/AGENTS.md +1 -1
  16. package/templates/agent-docs/_index.md +1 -1
  17. package/templates/agent-docs/deployment.md +56 -0
  18. package/templates/agent-docs/testing.md +42 -0
  19. package/templates/agent-docs/whats-new.md +172 -119
  20. package/templates/apps/api-ai/package.json +7 -7
  21. package/templates/apps/api-auth/package.json +8 -8
  22. package/templates/apps/api-backend/package.json +7 -7
  23. package/templates/apps/api-backend-deactivation/package.json +7 -7
  24. package/templates/apps/api-backend-mail/package.json +8 -8
  25. package/templates/apps/api-backend-mariadb/package.json +9 -9
  26. package/templates/apps/api-backend-storage/package.json +8 -8
  27. package/templates/apps/api-data-advanced/package.json +8 -8
  28. package/templates/apps/api-durable/package.json +8 -8
  29. package/templates/apps/api-feature-flags/package.json +9 -9
  30. package/templates/apps/api-governance/package.json +8 -8
  31. package/templates/apps/api-kv/package.json +8 -8
  32. package/templates/apps/api-moderation/package.json +8 -8
  33. package/templates/apps/api-observability/package.json +8 -8
  34. package/templates/apps/api-ratelimit/package.json +8 -8
  35. package/templates/apps/api-rbac/package.json +8 -8
  36. package/templates/apps/api-rest/package.json +7 -7
  37. package/templates/apps/api-saas/package.json +11 -11
  38. package/templates/apps/api-search/package.json +8 -8
  39. package/templates/apps/api-versioning/package.json +8 -8
  40. package/templates/apps/api-webhooks/package.json +9 -9
  41. package/templates/apps/changelog/package.json +6 -6
  42. package/templates/apps/edge-functions/package.json +2 -2
  43. package/templates/apps/frontend-admin/package.json +8 -8
  44. package/templates/apps/frontend-app/package.json +8 -8
  45. package/templates/apps/frontend-blank/package.json +7 -7
  46. package/templates/apps/frontend-contact/package.json +7 -7
  47. package/templates/apps/frontend-dashboard/package.json +7 -7
  48. package/templates/apps/frontend-docs/package.json +7 -7
  49. package/templates/apps/frontend-i18n/package.json +6 -6
  50. package/templates/apps/frontend-landing/package.json +7 -7
  51. package/templates/apps/frontend-spa/package.json +7 -7
  52. package/templates/apps/frontend-ssr/package.json +7 -7
  53. package/templates/apps/frontend-ssr-api/package.json +8 -8
  54. package/templates/apps/frontend-static-blog/package.json +6 -6
  55. package/dist/apiBuild-BrjrVJJh.js +0 -2
  56. package/dist/dbCommand-DrzXimKf.js +0 -2
  57. package/dist/dev-DNkso403.js +0 -3
  58. package/dist/serveCommand-DfkisVWP.js +0 -1310
@@ -1,5 +1,5 @@
1
1
  import { Z as e } from "./inspectMetrics-BU90mvJN.js";
2
- import { c as t } from "./seedRunner-D6eu-u5U.js";
2
+ import { c as t } from "./seedRunner-Bqxgp7HZ.js";
3
3
  import { r as n } from "./appModuleLoader-C9r9mxZt.js";
4
- import { t as r } from "./serveCommand-DfkisVWP.js";
4
+ import { t as r } from "./serveCommand-ZTn-dPFa.js";
5
5
  export { e as loadDotEnv, n as registerAppModules, t as registerDriver, r as runServe };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/cli",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "The `voltro` CLI — dev server, codegen, migrations, project scaffolding, agent-docs seeding, and production serve.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -62,22 +62,22 @@
62
62
  "@effect/platform-node": "^0.108.0",
63
63
  "@effect/sql": "^0.52.0",
64
64
  "@effect/workflow": "^0.19.0",
65
- "@voltro/ai": "0.27.0",
66
- "@voltro/cache": "0.27.0",
67
- "@voltro/data-transfer": "0.27.0",
68
- "@voltro/database": "0.27.0",
69
- "@voltro/env": "0.27.0",
70
- "@voltro/kv": "0.27.0",
71
- "@voltro/logger": "0.27.0",
72
- "@voltro/plugin-auth": "0.27.0",
73
- "@voltro/plugin-broadcast": "0.27.0",
74
- "@voltro/plugin-mail": "0.27.0",
75
- "@voltro/plugin-storage": "0.27.0",
76
- "@voltro/plugin-webhooks": "0.27.0",
77
- "@voltro/protocol": "0.27.0",
78
- "@voltro/runtime": "0.27.0",
79
- "@voltro/serverless": "0.27.0",
80
- "@voltro/workflow": "0.27.0",
65
+ "@voltro/ai": "0.28.0",
66
+ "@voltro/cache": "0.28.0",
67
+ "@voltro/data-transfer": "0.28.0",
68
+ "@voltro/database": "0.28.0",
69
+ "@voltro/env": "0.28.0",
70
+ "@voltro/kv": "0.28.0",
71
+ "@voltro/logger": "0.28.0",
72
+ "@voltro/plugin-auth": "0.28.0",
73
+ "@voltro/plugin-broadcast": "0.28.0",
74
+ "@voltro/plugin-mail": "0.28.0",
75
+ "@voltro/plugin-storage": "0.28.0",
76
+ "@voltro/plugin-webhooks": "0.28.0",
77
+ "@voltro/protocol": "0.28.0",
78
+ "@voltro/runtime": "0.28.0",
79
+ "@voltro/serverless": "0.28.0",
80
+ "@voltro/workflow": "0.28.0",
81
81
  "chokidar": "^5.0.0",
82
82
  "ioredis": "^5.11.1",
83
83
  "tinyglobby": "^0.2.17",
@@ -602,7 +602,7 @@ each plugin's own README.
602
602
 
603
603
  | Topic | Open | Summary |
604
604
  |---|---|---|
605
- | **What's new in 0.26.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
605
+ | **What's new in 0.27.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
606
606
  | AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
607
607
  | Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
608
608
  | Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
@@ -9,7 +9,7 @@ each plugin's own README.
9
9
 
10
10
  | Topic | Open | Summary |
11
11
  |---|---|---|
12
- | **What's new in 0.26.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
12
+ | **What's new in 0.27.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
13
13
  | AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
14
14
  | Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
15
15
  | Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
@@ -1150,6 +1150,59 @@ KV_BACKEND=redis
1150
1150
 
1151
1151
  Schedules and aggregates auto-coordinate via an advisory lock on SQL stores — no extra config to keep them from double-firing across replicas.
1152
1152
 
1153
+ ### The prerequisites you learn at the SECOND pod
1154
+
1155
+ Every item below is invisible on one replica and breaks on two. They are
1156
+ collected here because an operator reported each of them separately, each found
1157
+ the same way: the first pod proved the configuration worked.
1158
+
1159
+ **The connection pool multiplies, the database limit does not.**
1160
+
1161
+ ```sh
1162
+ DB_MAX_CONNECTIONS=10 # per replica — the fleet opens up to this × replicaCount
1163
+ ```
1164
+
1165
+ The framework opens ONE pool per process. At 4 replicas a pool of 10 is 40
1166
+ connections against a database that still allows whatever it allowed before you
1167
+ scaled. An operator's second pod died on `Connection timed out` for exactly
1168
+ this. `voltro serve` now prints the number and the arithmetic at boot:
1169
+
1170
+ ```
1171
+ db pool: max=10 per replica (DB_MAX_CONNECTIONS) × 4 replicas = up to 40 connections.
1172
+ ```
1173
+
1174
+ Set `REPLICA_COUNT` from your deployment (Helm: `{{ .Values.replicaCount }}`) and
1175
+ the line does the multiplication for you; without it the line still names the
1176
+ formula. `voltro dev` deliberately does not print it — one process, no replicas.
1177
+
1178
+ **`POD_IP` is each replica's identity, not only a workflow setting.**
1179
+
1180
+ ```yaml
1181
+ env:
1182
+ - name: POD_IP
1183
+ valueFrom:
1184
+ fieldRef:
1185
+ fieldPath: status.podIP
1186
+ ```
1187
+
1188
+ Without it every replica registers under the same host, so they are one runner
1189
+ as far as the cluster is concerned. The boot warning for it fires only on SQL
1190
+ cluster storage, so a deployment that has not adopted durable workflows yet gets
1191
+ no signal at all — inject it as a matter of course.
1192
+
1193
+ **Derive the broadcast namespace from something that cannot be forgotten.**
1194
+
1195
+ ```yaml
1196
+ - name: VOLTRO_BROADCAST_NAMESPACE
1197
+ value: {{ .Release.Namespace }}
1198
+ ```
1199
+
1200
+ Staging and production of the same app share a name, code and fingerprint, so
1201
+ the auto-derived namespace does NOT separate them — only this variable does. An
1202
+ operator's own guidance, and better than ours was: a value taken from the
1203
+ release namespace cannot be left out of one environment's config file, because
1204
+ there is no file to forget.
1205
+
1153
1206
  ### Workflow failover across replicas
1154
1207
 
1155
1208
  On a **SQL store** (postgres / mysql / mariadb / mssql), durable workflows survive a replica crash: completed `step({...})` activities are checkpointed in the cluster journal, so when a replica dies mid-run, a **surviving replica takes over the run and continues it from the last completed step** — it replays the completed steps rather than re-running them. (On sqlite the engine is single-process — durable within one replica, no cross-replica failover.) Two requirements:
@@ -1181,6 +1234,9 @@ Lower the lease for **faster failover**, at the cost of **false-positive reclaim
1181
1234
  - [ ] `VOLTRO_LOG_FORMAT=json`; `sentryPlugin()` + `SENTRY_DSN` for errors
1182
1235
  - [ ] `terminationGracePeriodSeconds` generous for graceful drain; `VOLTRO_SHUTDOWN_GRACE_MS` set just under it (minus the preStop sleep)
1183
1236
  - [ ] `CACHE_BACKEND` / `KV_BACKEND` + a cross-replica change bus when running >1 replica
1237
+ - [ ] `DB_MAX_CONNECTIONS` set so `pool × replicaCount` fits your database's limit — read the `db pool:` boot line before raising `replicaCount`
1238
+ - [ ] `POD_IP` injected via the downward API on EVERY multi-replica deployment, not only for durable workflows
1239
+ - [ ] `VOLTRO_BROADCAST_NAMESPACE` derived from the release namespace — staging and production do not separate themselves
1184
1240
  - [ ] For durable workflows on >1 replica: SQL store + `POD_IP` injected; tune `VOLTRO_WORKFLOW_FAILOVER_LEASE` if a 35s takeover is too slow; step side effects idempotent
1185
1241
 
1186
1242
 
@@ -372,6 +372,48 @@ What it fills and what it leaves alone:
372
372
 
373
373
  There is no flag to turn the validation off — a test store that accepts rows production rejects is a fake testing itself. `fixtureRow` is a runtime filler for the loose `store.insert(name, row)` path. For **compile-time** payload typing (a missing required column caught as a type error at the call), use [`insertRow` / `upsertRow`](/docs/data/mutations) from `@voltro/database`, which check against `InferInsertRow<T>`.
374
374
 
375
+ ## Subscribers — `makeSubscribeContext` + the change constructors
376
+
377
+ A `*.subscribe.ts` handler does not receive an `AppContext`. It receives a
378
+ change EVENT and a `SubscribeContext`, and both have constructors:
379
+
380
+ ```ts
381
+ import { changeInsert, changeUpdate, changeDelete, changeSoftDelete, makeSubscribeContext, makeTestContext } from '@voltro/testing'
382
+ import subscriber from '../src/attendance.subscribe'
383
+
384
+ const app = makeTestContext({ store: { attendance: [{ id: 'a1', employeeId: 'e1' }] } })
385
+ const ctx = makeSubscribeContext({ id: 'attendance', store: app.store })
386
+
387
+ await subscriber.handler(changeUpdate('attendance', { id: 'a1', state: 'in' }, { id: 'a1', state: 'out' }), ctx)
388
+ ```
389
+
390
+ **Build the event with a constructor, not with an object literal.** The
391
+ constructors put the semantics in the name, which is the half a literal cannot
392
+ give you:
393
+
394
+ | | `op` | `old` | `new` |
395
+ | --- | --- | --- | --- |
396
+ | `changeInsert(t, row)` | `insert` | `null` | the row |
397
+ | `changeUpdate(t, before, after)` | `update` | before | after |
398
+ | `changeDelete(t, row)` | `delete` | the row | `null` |
399
+ | `changeSoftDelete(t, row)` | **`update`** | `deletedAt: null` | `deletedAt` set |
400
+
401
+ `changeSoftDelete` is the reason this exists. There is no `op: 'softDelete'` and
402
+ there never will be — a soft delete is an ordinary update that sets `deletedAt`
403
+ — so a test author who does not know that writes a delete, and the test passes
404
+ against a stream the framework never emits. We shipped exactly that defect: a
405
+ feature that keys off soft deletes was inert in production while its own tests
406
+ were green, because they asserted against an event shape that does not exist.
407
+
408
+ `ctx.store` has **no default and throws when touched**. Pass
409
+ `makeTestContext().store` so the subscriber and the code under test share one; a
410
+ silent empty store would let a subscriber reading the wrong table pass its test,
411
+ which is the same silent-nothing the constructors exist to remove.
412
+
413
+ `ctx.publish` is absent unless you pass one, mirroring the real context — where
414
+ it is optional precisely so that reaching for it in an app that declares no
415
+ event is a type error.
416
+
375
417
  ## Subject + tenant re-scoping
376
418
 
377
419
  `withSubject` and `withTenant` re-scope to a different principal for one block, sharing the **same** underlying data — so cross-subject reads exercise real tenant scoping, not a closure stub. This is how you prove isolation: write as one tenant, then assert another tenant can't see the row.