@voltro/cli 0.20.1 → 0.21.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/CHANGELOG.md +171 -0
- package/THIRD-PARTY-NOTICES.md +61 -61
- package/dist/{apiBuild-qZBEu59d.js → apiBuild-BHOtsQkp.js} +93 -80
- package/dist/apiBuild-K7M6Rt0U.js +2 -0
- package/dist/bin.js +41 -20
- package/dist/{commands-CA7jwejs.js → commands-BZoimvvS.js} +2752 -2633
- package/dist/{dbCommand-DFw2UoC-.js → dbCommand-CDBbZ-Ac.js} +26 -12
- package/dist/dbCommand-oADAj6ez.js +2 -0
- package/dist/dev-CZQ73Yoh.js +3 -0
- package/dist/{dev-OHLsAq4K.js → dev-DKb92NYX.js} +1537 -1527
- package/dist/index.js +1 -1
- package/dist/{inspectMetrics-DvPNXmGA.js → inspectMetrics-DMjWBQif.js} +576 -567
- package/dist/serveCommand-qHJCs0cX.js +1162 -0
- package/dist/serveEntry.js +2 -2
- package/dist/{start-CXQ7WL1W.js → start-B1x6Z60h.js} +389 -389
- package/dist/startEntry.js +2 -2
- package/package.json +27 -27
- package/templates/AGENTS.md +1 -1
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/cli.md +63 -0
- package/templates/agent-docs/database/migrations.md +33 -11
- package/templates/agent-docs/database/schema.md +7 -0
- package/templates/agent-docs/database/seedsdialects.md +3 -3
- package/templates/agent-docs/whats-new.md +93 -81
- package/templates/apps/api-ai/package.json +12 -12
- package/templates/apps/api-auth/package.json +13 -13
- package/templates/apps/api-backend/package.json +12 -12
- package/templates/apps/api-backend-deactivation/package.json +12 -12
- package/templates/apps/api-backend-mail/package.json +11 -11
- package/templates/apps/api-backend-mariadb/package.json +14 -14
- package/templates/apps/api-backend-storage/package.json +11 -11
- package/templates/apps/api-data-advanced/package.json +13 -13
- package/templates/apps/api-durable/package.json +13 -13
- package/templates/apps/api-feature-flags/package.json +14 -14
- package/templates/apps/api-governance/package.json +13 -13
- package/templates/apps/api-kv/package.json +13 -13
- package/templates/apps/api-moderation/package.json +13 -13
- package/templates/apps/api-observability/package.json +13 -13
- package/templates/apps/api-ratelimit/package.json +13 -13
- package/templates/apps/api-rbac/package.json +13 -13
- package/templates/apps/api-rest/package.json +12 -12
- package/templates/apps/api-saas/package.json +16 -16
- package/templates/apps/api-search/package.json +13 -13
- package/templates/apps/api-versioning/package.json +13 -13
- package/templates/apps/api-webhooks/package.json +14 -14
- package/templates/apps/changelog/package.json +8 -8
- package/templates/apps/edge-functions/package.json +5 -5
- package/templates/apps/frontend-admin/package.json +10 -10
- package/templates/apps/frontend-app/package.json +10 -10
- package/templates/apps/frontend-blank/package.json +9 -9
- package/templates/apps/frontend-contact/package.json +11 -11
- package/templates/apps/frontend-dashboard/package.json +9 -9
- package/templates/apps/frontend-docs/package.json +9 -9
- package/templates/apps/frontend-i18n/package.json +8 -8
- package/templates/apps/frontend-landing/package.json +9 -9
- package/templates/apps/frontend-spa/package.json +9 -9
- package/templates/apps/frontend-ssr/package.json +9 -9
- package/templates/apps/frontend-ssr-api/package.json +10 -10
- package/templates/apps/frontend-static-blog/package.json +8 -8
- package/dist/apiBuild-C9aHz4Yh.js +0 -2
- package/dist/dbCommand-DA_RFj5K.js +0 -2
- package/dist/dev-2AvdzDg2.js +0 -3
- package/dist/serveCommand-BKqTKWTX.js +0 -1136
- /package/templates/patches/{@effect__cluster@0.59.0.patch → @effect__cluster@0.60.0.patch} +0 -0
package/dist/startEntry.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t } from "./start-
|
|
1
|
+
import { X as e } from "./inspectMetrics-DMjWBQif.js";
|
|
2
|
+
import { t } from "./start-B1x6Z60h.js";
|
|
3
3
|
export { e as loadDotEnv, t as runStartCommand };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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",
|
|
@@ -59,42 +59,42 @@
|
|
|
59
59
|
"node": ">=24.0.0"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@effect/platform-node": "^0.
|
|
63
|
-
"@effect/sql": "^0.
|
|
64
|
-
"@effect/workflow": "^0.
|
|
65
|
-
"@voltro/ai": "0.
|
|
66
|
-
"@voltro/cache": "0.
|
|
67
|
-
"@voltro/data-transfer": "0.
|
|
68
|
-
"@voltro/database": "0.
|
|
69
|
-
"@voltro/env": "0.
|
|
70
|
-
"@voltro/kv": "0.
|
|
71
|
-
"@voltro/logger": "0.
|
|
72
|
-
"@voltro/plugin-auth": "0.
|
|
73
|
-
"@voltro/plugin-broadcast": "0.
|
|
74
|
-
"@voltro/plugin-mail": "0.
|
|
75
|
-
"@voltro/plugin-storage": "0.
|
|
76
|
-
"@voltro/plugin-webhooks": "0.
|
|
77
|
-
"@voltro/protocol": "0.
|
|
78
|
-
"@voltro/runtime": "0.
|
|
79
|
-
"@voltro/serverless": "0.
|
|
80
|
-
"@voltro/workflow": "0.
|
|
62
|
+
"@effect/platform-node": "^0.108.0",
|
|
63
|
+
"@effect/sql": "^0.52.0",
|
|
64
|
+
"@effect/workflow": "^0.19.0",
|
|
65
|
+
"@voltro/ai": "0.21.0",
|
|
66
|
+
"@voltro/cache": "0.21.0",
|
|
67
|
+
"@voltro/data-transfer": "0.21.0",
|
|
68
|
+
"@voltro/database": "0.21.0",
|
|
69
|
+
"@voltro/env": "0.21.0",
|
|
70
|
+
"@voltro/kv": "0.21.0",
|
|
71
|
+
"@voltro/logger": "0.21.0",
|
|
72
|
+
"@voltro/plugin-auth": "0.21.0",
|
|
73
|
+
"@voltro/plugin-broadcast": "0.21.0",
|
|
74
|
+
"@voltro/plugin-mail": "0.21.0",
|
|
75
|
+
"@voltro/plugin-storage": "0.21.0",
|
|
76
|
+
"@voltro/plugin-webhooks": "0.21.0",
|
|
77
|
+
"@voltro/protocol": "0.21.0",
|
|
78
|
+
"@voltro/runtime": "0.21.0",
|
|
79
|
+
"@voltro/serverless": "0.21.0",
|
|
80
|
+
"@voltro/workflow": "0.21.0",
|
|
81
81
|
"chokidar": "^5.0.0",
|
|
82
82
|
"ioredis": "^5.11.1",
|
|
83
83
|
"tinyglobby": "^0.2.17",
|
|
84
84
|
"ts-morph": "^28.0.0"
|
|
85
85
|
},
|
|
86
86
|
"optionalDependencies": {
|
|
87
|
-
"@tailwindcss/vite": "^4.3.
|
|
87
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
88
88
|
"@vercel/nft": "^1.10.2",
|
|
89
|
-
"@vitejs/plugin-react": "^6.0.
|
|
89
|
+
"@vitejs/plugin-react": "^6.0.4",
|
|
90
90
|
"esbuild": "^0.28.0",
|
|
91
|
-
"tsx": "^4.23.
|
|
92
|
-
"vite": "^8.1.
|
|
91
|
+
"tsx": "^4.23.1",
|
|
92
|
+
"vite": "^8.1.5"
|
|
93
93
|
},
|
|
94
94
|
"peerDependencies": {
|
|
95
|
-
"@effect/platform": "^0.
|
|
96
|
-
"@effect/rpc": "^0.
|
|
97
|
-
"effect": "^3.
|
|
95
|
+
"@effect/platform": "^0.97.0",
|
|
96
|
+
"@effect/rpc": "^0.76.0",
|
|
97
|
+
"effect": "^3.22.0"
|
|
98
98
|
},
|
|
99
99
|
"publishConfig": {
|
|
100
100
|
"access": "public"
|
package/templates/AGENTS.md
CHANGED
|
@@ -595,7 +595,7 @@ each plugin's own README.
|
|
|
595
595
|
|
|
596
596
|
| Topic | Open | Summary |
|
|
597
597
|
|---|---|---|
|
|
598
|
-
| **What's new in 0.
|
|
598
|
+
| **What's new in 0.21.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. |
|
|
599
599
|
| 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. |
|
|
600
600
|
| 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. |
|
|
601
601
|
| 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.
|
|
12
|
+
| **What's new in 0.21.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. |
|
|
@@ -544,6 +544,45 @@ compile. Already-compiled (warm) pages are never throttled, so a hot app stays
|
|
|
544
544
|
fully concurrent. Tune the cap with `VOLTRO_DEV_SSR_COMPILE_CONCURRENCY` (below) —
|
|
545
545
|
drop it on a low-memory box, raise it on a big machine.
|
|
546
546
|
|
|
547
|
+
### A failed server render fails the request
|
|
548
|
+
|
|
549
|
+
If the server render throws, `voltro dev` answers **500** with the error and its
|
|
550
|
+
stack, marks the response `x-voltro-rendered-by: ssr-dev-failed`, and logs it at
|
|
551
|
+
`error`. It does **not** fall back to a client-rendered shell.
|
|
552
|
+
|
|
553
|
+
That is deliberate, and it is the same outcome `voltro start` produces in
|
|
554
|
+
production. A fallback would hand you a page that renders in the browser and a
|
|
555
|
+
500 in production from the identical code — and because an empty `<div
|
|
556
|
+
id="root">` is what a client-only app looks like, the usual conclusion is "the
|
|
557
|
+
framework does not server-render", not "my page threw". The failure is loud so
|
|
558
|
+
the cause is the thing you see.
|
|
559
|
+
|
|
560
|
+
The practical consequence: anything that only misbehaves under
|
|
561
|
+
`renderToPipeableStream` — a component that suspends with no `<Suspense>`
|
|
562
|
+
boundary above it, a loader that throws, a hydration-unsafe value — surfaces in
|
|
563
|
+
`voltro dev` at the moment you hit the route.
|
|
564
|
+
|
|
565
|
+
**Suspending is fine here, and does not need a boundary you add.** A component
|
|
566
|
+
that suspends during a streamed server render — a lazily-loaded translation
|
|
567
|
+
catalog, a `React.lazy` component, `react-i18next` with `useSuspense: true` —
|
|
568
|
+
renders normally: `renderToPipeableStream` treats the root as an implicit
|
|
569
|
+
boundary, so a suspend delays the shell flush instead of failing. Measured, not
|
|
570
|
+
assumed; a regression test pins it.
|
|
571
|
+
|
|
572
|
+
Do **not** add a blanket `<Suspense>` at the root to "fix" a suspend. It makes
|
|
573
|
+
things worse in a way that is hard to see: React downgrades an errored boundary
|
|
574
|
+
to client rendering, so a page that THROWS starts answering 200 with
|
|
575
|
+
`<template data-msg="Switched to client rendering">` instead of failing. You lose
|
|
576
|
+
the hard failure above and gain nothing — the suspend already worked. Mount
|
|
577
|
+
boundaries where you want a *fallback* (`loading.tsx` per route, `<Await>` for
|
|
578
|
+
deferred loader values), not to make suspending legal.
|
|
579
|
+
|
|
580
|
+
Two places where a suspend genuinely is not supported, both by React rather than
|
|
581
|
+
by choice: `renderToString`, which backs the static prerender
|
|
582
|
+
(`renderMode: 'static'`), emits the fallback instead of waiting — so a suspending
|
|
583
|
+
component in a prerendered page needs its own boundary or a resolved value; and
|
|
584
|
+
the client render after hydration, which follows React's own rules.
|
|
585
|
+
|
|
547
586
|
|
|
548
587
|
Under `VOLTRO_LOG_LEVEL=debug` each cold compile logs its own duration, so a slow
|
|
549
588
|
first paint can be attributed to a specific module:
|
|
@@ -1470,6 +1509,30 @@ has never run is not "failing".
|
|
|
1470
1509
|
Pair it with `voltro logs --level error`: a failing handler now logs at **error**
|
|
1471
1510
|
level, so the two surfaces agree.
|
|
1472
1511
|
|
|
1512
|
+
## `voltro schedule run <name>` — fire one job, now
|
|
1513
|
+
|
|
1514
|
+
```sh
|
|
1515
|
+
voltro schedule run nightly-reconcile
|
|
1516
|
+
voltro schedule run nightly-reconcile --process billing --format json
|
|
1517
|
+
voltro schedule run nightly-reconcile --url https://api.example.com # a deployed app
|
|
1518
|
+
```
|
|
1519
|
+
|
|
1520
|
+
For the normal case: a nightly job that corrects business data, and you want to
|
|
1521
|
+
run it once and watch. It reports the run id, and `voltro inspect schedules`
|
|
1522
|
+
shows the outcome.
|
|
1523
|
+
|
|
1524
|
+
A run id of `null` is not a failure and is reported as its own outcome: the run
|
|
1525
|
+
was **coordinated away** — another replica holds the lock, or the previous run is
|
|
1526
|
+
still going and this schedule's `onOverlap` is `'skip'`. Printing "ok" there
|
|
1527
|
+
would claim work that never started.
|
|
1528
|
+
|
|
1529
|
+
`--trigger external` records the run as externally triggered instead of manual,
|
|
1530
|
+
for schedules that are normally fired by an outside scheduler.
|
|
1531
|
+
|
|
1532
|
+
This works against `voltro serve` as well as `voltro dev`. It did not before —
|
|
1533
|
+
production mounted no inspect surface at all, which also meant the post-deploy
|
|
1534
|
+
gate below could only ever be run against a dev server.
|
|
1535
|
+
|
|
1473
1536
|
## Targeting a deployed app
|
|
1474
1537
|
|
|
1475
1538
|
Every command in this family resolves its target from the local runtime registry — the apps running on *this* machine. Pass `--url` to point one at a **deployed** app instead:
|
|
@@ -89,7 +89,7 @@ voltro db plan --against <url> # diff vs a remote env's inspect endpoint (see
|
|
|
89
89
|
voltro db apply # execute (dev only — refuses on NODE_ENV=production)
|
|
90
90
|
voltro db apply --note '...' # apply with a freeform note recorded in history
|
|
91
91
|
voltro db plans [--limit 20] # history from _voltro_migration_plans, newest first
|
|
92
|
-
voltro db drift # live-vs-
|
|
92
|
+
voltro db drift # live-vs-baseline check — exit 0 match, 3 no baseline, 4 drift
|
|
93
93
|
voltro db squash --before <date> # consolidate history into one snapshot
|
|
94
94
|
voltro db restore-snapshot <id> # restore VOLTRO_SOFT_DROP=1 columns from a plan
|
|
95
95
|
|
|
@@ -2009,7 +2009,7 @@ Drift = the live database's schema doesn't match the fingerprint of the last app
|
|
|
2009
2009
|
|
|
2010
2010
|
Three trigger paths:
|
|
2011
2011
|
|
|
2012
|
-
1. **Manual** — `voltro db drift` runs the check on demand,
|
|
2012
|
+
1. **Manual** — `voltro db drift` runs the check on demand: exit 0 match, 3 no baseline, 4 drift
|
|
2013
2013
|
2. **On boot (dev)** — every `voltro dev` boot runs the planner, which detects drift implicitly (the plan will be non-empty)
|
|
2014
2014
|
3. **Periodic (cloud)** — the cloud dashboard polls each app's `/_voltro/inspect/migrations` endpoint; drift state is in the response
|
|
2015
2015
|
|
|
@@ -2018,16 +2018,39 @@ All three paths produce the same `DriftSnapshot` shape:
|
|
|
2018
2018
|
```ts
|
|
2019
2019
|
{
|
|
2020
2020
|
isDrifted: boolean,
|
|
2021
|
-
liveFingerprint: string, // current introspected
|
|
2022
|
-
lastAppliedFingerprint?: string, // newest
|
|
2021
|
+
liveFingerprint: string, // current introspected schema fingerprint
|
|
2022
|
+
lastAppliedFingerprint?: string, // the BASELINE the newest row recorded
|
|
2023
2023
|
lastAppliedAt?: string, // when it was applied
|
|
2024
2024
|
lastAppliedId?: string, // plan id
|
|
2025
2025
|
}
|
|
2026
2026
|
```
|
|
2027
2027
|
|
|
2028
|
-
`isDrifted: false` ↔ `liveFingerprint === lastAppliedFingerprint
|
|
2028
|
+
`isDrifted: false` ↔ `liveFingerprint === lastAppliedFingerprint`, **or** no baseline was recorded — nothing compared is not the same as nothing changed, and it is never reported as drift.
|
|
2029
2029
|
|
|
2030
|
-
|
|
2030
|
+
### What it compares, and what it does not
|
|
2031
|
+
|
|
2032
|
+
The baseline is `_voltro_migration_plans.liveFingerprint`: the fingerprint of the **live** schema as it was immediately after the last `voltro db apply`. Not `fingerprint`, which is the **declared** snapshot's hash — introspection cannot recover everything a declaration carries (generated expressions, `maxLength`, sensitivity markers), so a live hash and a declared hash never agree and comparing them reports drift on every clean database.
|
|
2033
|
+
|
|
2034
|
+
Both sides hash the **whole** live schema, framework tables included. A framework upgrade that adds a `_voltro_*` column therefore shows as drift until the next `db apply` records a new baseline — honest, since the live schema did change, and it self-heals on the apply the upgrade needs anyway.
|
|
2035
|
+
|
|
2036
|
+
### No baseline yet
|
|
2037
|
+
|
|
2038
|
+
A ledger row written before `liveFingerprint` existed has no baseline, and so does a database whose schema was already current when it upgraded. `voltro db drift` says so and **exits 3**:
|
|
2039
|
+
|
|
2040
|
+
```sh
|
|
2041
|
+
$ voltro db drift
|
|
2042
|
+
db drift: no drift baseline recorded yet — cannot compare
|
|
2043
|
+
```
|
|
2044
|
+
|
|
2045
|
+
Run `voltro db apply` once — a no-op apply backfills the baseline too. Until then use `voltro db plan`, which compares declared against live directly.
|
|
2046
|
+
|
|
2047
|
+
**Exit 3 is deliberately not 0.** "Did not compare" is not "clean", and a CI gate on the exit code has to be able to tell them apart — otherwise it passes vacuously on a stable schema, which is the failure drift detection exists to prevent.
|
|
2048
|
+
|
|
2049
|
+
| exit | meaning |
|
|
2050
|
+
|---|---|
|
|
2051
|
+
| 0 | compared, live matches the baseline |
|
|
2052
|
+
| 3 | no baseline — did NOT compare |
|
|
2053
|
+
| 4 | compared, live diverged |
|
|
2031
2054
|
|
|
2032
2055
|
## Common causes
|
|
2033
2056
|
|
|
@@ -2038,12 +2061,11 @@ Someone ran `ALTER TABLE ...` or `CREATE INDEX ...` via psql / DataGrip / Admine
|
|
|
2038
2061
|
```sh
|
|
2039
2062
|
$ voltro db drift
|
|
2040
2063
|
db drift: live schema DIVERGED from last applied state
|
|
2041
|
-
|
|
2042
|
-
live now:
|
|
2064
|
+
baseline: 8f507ba1e1aadad5 at 2026-06-15 14:32:00 (plan_mig_5k78)
|
|
2065
|
+
live now: a8f2c9d10b3f4e62
|
|
2043
2066
|
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
- someone applied a plan but the row never made it into _voltro_migration_plans
|
|
2067
|
+
Something changed the live schema after the last apply. This command can see
|
|
2068
|
+
THAT it changed, not what or who — the fingerprints are hashes, not a diff.
|
|
2047
2069
|
|
|
2048
2070
|
To reconcile, run `voltro db plan` to see what your code expects vs the live DB.
|
|
2049
2071
|
```
|
|
@@ -73,6 +73,13 @@ Plain `text()` is **unbounded** — `TEXT` on Postgres/SQLite, `LONGTEXT` on MyS
|
|
|
73
73
|
|
|
74
74
|
Reach for **`.maxLength(n)`** when the column is a short identifier (a kind, slug, external id, resource discriminator) that participates in a `.unique([...])` or composite `.index([...])`: an unbounded column can only back a HASH long-unique constraint (MariaDB) or a prefix index, whereas a bounded `VARCHAR(n)` takes a plain BTREE key. It's cross-dialect (`VARCHAR(n)` on Postgres/MySQL/MariaDB, `NVARCHAR(n)` on MSSQL, `TEXT` on SQLite) — unlike `raw('varchar(20)')`, which hardcodes one dialect's DDL.
|
|
75
75
|
|
|
76
|
+
**Changing the bound on an EXISTING column is a real migration.** The differ compares `maxLength` on both sides, so `text()` → `text().maxLength(64)`, or 64 → 128, produces an operation:
|
|
77
|
+
|
|
78
|
+
- **widening** (a larger bound, or dropping the bound) is `safe` — no value can be lost;
|
|
79
|
+
- **narrowing** is `needs-backfill`: the `ALTER` fails at the database for any existing value longer than the new bound. The plan says so and gives you the query to run first.
|
|
80
|
+
|
|
81
|
+
Not compared on SQLite, which has no length-enforced type — a declared bound there is real, the live column is always `TEXT`, and comparing them would re-plan the same ALTER on every boot.
|
|
82
|
+
|
|
76
83
|
For **value** constraints (length ranges, regex, email, numeric min/max, cross-field invariants), use table-level `table().validate(Schema)` (see below) — that's the single validation surface, not a pile of per-column modifiers. There are no `.minLength()` / `.pattern()` column modifiers by design.
|
|
77
84
|
|
|
78
85
|
### `integer()`
|
|
@@ -996,7 +996,7 @@ If you write hand-rolled queries that read JSON columns through `unsafe()`, you'
|
|
|
996
996
|
> then applies it. Idempotent, and a no-op on every other dialect. Without it,
|
|
997
997
|
> workflow message/runner storage misbehaves on SQL Server.
|
|
998
998
|
|
|
999
|
-
`@effect/cluster`'s mssql branch uses `sp_getapplock` for shard claims and `MERGE … WHEN NOT MATCHED THEN INSERT … OUTPUT INSERTED` for runner upserts. Four bugs in `@effect/cluster@0.
|
|
999
|
+
`@effect/cluster`'s mssql branch uses `sp_getapplock` for shard claims and `MERGE … WHEN NOT MATCHED THEN INSERT … OUTPUT INSERTED` for runner upserts. Four bugs in `@effect/cluster@0.60.0` mssql code paths fail under the framework's workflow stack; the framework carries a `pnpm patch` (shipped in the CLI at `packages/cli/templates/patches/@effect__cluster@0.60.0.patch`, written into your project by `voltro add mssql`):
|
|
1000
1000
|
|
|
1001
1001
|
### Patch 1 — SqlRunnerStorage shard-lock MERGE alias
|
|
1002
1002
|
|
|
@@ -1024,7 +1024,7 @@ MSSQL forbids subqueries inside OUTPUT clauses (Msg 10705). The original `insert
|
|
|
1024
1024
|
|
|
1025
1025
|
The cluster's `deliver_at` column is BIGINT (storing millisecond epoch). Tedious binds JS `number` parameters as INT — which overflows for any post-2001 timestamp. The patch casts `deliver_at` to `BigInt` once at the top of `envelopeToRow` so all three message-kind switch arms emit `bigint | null`; other dialects accept bigint fine.
|
|
1026
1026
|
|
|
1027
|
-
All four patches are dialect-keyed (touch only the `mssql:` branch of `sql.onDialectOrElse`) so postgres / mysql / sqlite paths are bit-identical to upstream. The framework carries them and ships them to your project via `voltro add mssql` (see the note at the top of this section) —
|
|
1027
|
+
All four patches are dialect-keyed (touch only the `mssql:` branch of `sql.onDialectOrElse`) so postgres / mysql / sqlite paths are bit-identical to upstream. The framework carries them and ships them to your project via `voltro add mssql` (see the note at the top of this section) — the patch is how the fix reaches you. Upstream still carries these bugs on the current release — the framework re-verifies that on every bump (`git apply --check` against the new tarball) and re-keys the patch, because a `patchedDependencies` key is version-exact and a stale one fails the install.
|
|
1028
1028
|
|
|
1029
1029
|
## Read replicas — Always-On Availability Groups
|
|
1030
1030
|
|
|
@@ -1088,7 +1088,7 @@ For a full before-image or per-op fidelity, use **mariadb** (binlog CDC). For ze
|
|
|
1088
1088
|
- `voltro/packages/sql-mssql/src/replicationAdapter.ts` — `end_of_log_lsn` / `last_hardened_lsn` compare for RYW
|
|
1089
1089
|
- `voltro/packages/database/src/sqlCompiler.ts` — TOP / FETCH NEXT dispatch
|
|
1090
1090
|
- `voltro/packages/database/src/migrate.ts` — mssql DDL branch (line 72)
|
|
1091
|
-
- `packages/cli/templates/patches/@effect__cluster@0.
|
|
1091
|
+
- `packages/cli/templates/patches/@effect__cluster@0.60.0.patch` — the four upstream patches (shipped in the CLI; `voltro add mssql` writes it into your project)
|
|
1092
1092
|
|
|
1093
1093
|
|
|
1094
1094
|
|