@voltro/cli 0.2.2 → 0.3.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 (61) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/THIRD-PARTY-NOTICES.md +234 -1
  3. package/bin/voltro.mjs +71 -1
  4. package/dist/apiBuild-DdgYydVJ.js +193 -0
  5. package/dist/apiBuild-cadmH8ca.js +2 -0
  6. package/dist/bin.js +2 -2
  7. package/dist/{commands-DQy4812j.js → commands-CXESev-z.js} +2182 -1780
  8. package/dist/dev-DemiMSSl.js +2 -0
  9. package/dist/{dev--jHe1vcu.js → dev-x_VqbV_8.js} +1469 -1458
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.js +1 -1
  12. package/dist/{serveCommand-93rRdEp0.js → serveCommand-Dttqe5Ms.js} +2 -2
  13. package/dist/serveEntry.js +2 -2
  14. package/package.json +22 -19
  15. package/templates/AGENTS.core.md +14 -0
  16. package/templates/AGENTS.md +14 -0
  17. package/templates/agent-docs/_manifest.json +1 -1
  18. package/templates/agent-docs/cli.md +59 -0
  19. package/templates/agent-docs/data.md +39 -0
  20. package/templates/agent-docs/database/advancedqueries.md +27 -0
  21. package/templates/agent-docs/deployment.md +3 -1
  22. package/templates/apps/api-ai/package.json +7 -7
  23. package/templates/apps/api-auth/package.json +8 -8
  24. package/templates/apps/api-backend/package.json +7 -7
  25. package/templates/apps/api-backend-deactivation/package.json +7 -7
  26. package/templates/apps/api-backend-mail/package.json +8 -8
  27. package/templates/apps/api-backend-mariadb/package.json +9 -9
  28. package/templates/apps/api-backend-storage/package.json +8 -8
  29. package/templates/apps/api-data-advanced/package.json +8 -8
  30. package/templates/apps/api-durable/package.json +8 -8
  31. package/templates/apps/api-feature-flags/package.json +9 -9
  32. package/templates/apps/api-governance/package.json +8 -8
  33. package/templates/apps/api-kv/package.json +8 -8
  34. package/templates/apps/api-moderation/package.json +8 -8
  35. package/templates/apps/api-observability/package.json +8 -8
  36. package/templates/apps/api-ratelimit/package.json +8 -8
  37. package/templates/apps/api-rbac/package.json +8 -8
  38. package/templates/apps/api-rest/package.json +7 -7
  39. package/templates/apps/api-saas/package.json +11 -11
  40. package/templates/apps/api-search/package.json +8 -8
  41. package/templates/apps/api-versioning/package.json +8 -8
  42. package/templates/apps/api-webhooks/package.json +8 -8
  43. package/templates/apps/changelog/package.json +6 -6
  44. package/templates/apps/edge-functions/package.json +2 -2
  45. package/templates/apps/frontend-admin/package.json +8 -8
  46. package/templates/apps/frontend-app/package.json +8 -8
  47. package/templates/apps/frontend-blank/package.json +7 -7
  48. package/templates/apps/frontend-contact/package.json +7 -7
  49. package/templates/apps/frontend-dashboard/package.json +7 -7
  50. package/templates/apps/frontend-docs/package.json +7 -7
  51. package/templates/apps/frontend-i18n/package.json +6 -6
  52. package/templates/apps/frontend-landing/package.json +7 -7
  53. package/templates/apps/frontend-spa/package.json +7 -7
  54. package/templates/apps/frontend-ssr/package.json +7 -7
  55. package/templates/apps/frontend-ssr-api/package.json +8 -8
  56. package/templates/apps/frontend-static-blog/package.json +6 -6
  57. package/templates/baselines/compose/docker/api.Dockerfile +10 -5
  58. package/templates/baselines/compose-mariadb/docker/api.Dockerfile +10 -5
  59. package/dist/apiBuild-OpZROja5.js +0 -2
  60. package/dist/apiBuild-o70rjpVJ.js +0 -184
  61. package/dist/dev-BKkZglQV.js +0 -2
package/CHANGELOG.md CHANGED
@@ -39,6 +39,26 @@ _Changes staged for the next release accumulate here (rolled up from
39
39
 
40
40
  ---
41
41
 
42
+ ## [0.3.0] — 2026-07-18
43
+
44
+ ### ⚠ BREAKING
45
+
46
+ - **@voltro/cli** — `voltro build` precompiles the whole serve path (framework + effect + `@voltro` inlined, app modules as lazy chunks) into a single **serve bundle**, and `voltro serve` boots from it in-process — cutting `serve: ready` from ~1000 ms to ~180 ms (5–6×; the win is larger on a cold scale-to-zero container). The app's declared SQL driver is inlined (only the native leaf like `pg` stays external, resolved at runtime so it survives the deploy relocation). **Production now serves ONLY from the bundle and NEVER transpiles on demand:** the bundle build externalises unresolvable optional peers (e.g. `@react-email/render` behind `@voltro/plugin-mail`) so it always builds; a bundle-build failure is **fatal** (`voltro build` exits non-zero); and an unbuilt production `voltro serve` fails loud instead of falling back to tsx. The build toolchain (`tsx`, `esbuild`, `vite`, `@vitejs/plugin-react`, `@tailwindcss/vite` + their native tree: rolldown/lightningcss/postcss/jiti) moves to **`optionalDependencies`** of `@voltro/cli`, so `pnpm --prod --no-optional deploy` yields a serve image with none of it — a prod API image's `node_modules` drops ~305 MB → ~131 MB, structurally, with no fragile prune list. `voltro dev` and a non-production local `voltro serve` are unchanged (still tsx). **Migration:** in production (`NODE_ENV=production`) run `voltro build` before `voltro serve`. The generated Dockerfiles already do; a custom Dockerfile / start script adds a `voltro build .` step before `voltro serve .` (`voltro update` prints this — see the 0.3.0 codemod note).
47
+
48
+ ### Added
49
+
50
+ - **@voltro/protocol, @voltro/runtime, @voltro/plugin-rbac** — Declarative authorization `guards:` on `defineMutation` / `defineQuery` / `defineAction`. The framework enforces the declared scope(s) in the dispatch spine BEFORE the executor (for a mutation, before the transaction opens), fails with a typed `ScopeError`, and auto-merges `ScopeError` into the wire error union so the client decodes the denial typed. Guards are browser-safe DATA (scope strings + a pure `resource: (input) => id` extractor). Checks run against the caller's EFFECTIVE scope set — raw subject scopes ∪ `@voltro/plugin-rbac` role-derived scopes — via a new canonical effective-scope seam in `@voltro/protocol` (`effectiveScopes` / `setEffectiveScopes` / `checkGuards`), which rbac now publishes to (so a role-granted scope satisfies a `guards:` entry and the in-handler `permission()` identically). Adds `ctx.access` (`has` / `hasAny` / `require` / `scopes`) — the cast-free typed authorization slice on every handler context. Enforcement is single-sourced in the shared serve pipeline, so `voltro dev` and `voltro serve` can't drift.
51
+ - **@voltro/protocol, @voltro/client** — Nested / path-targeted auto-optimistic. A mutation `target` can now patch a nested array INSIDE a query's value — a JSON array column (`snapshot.projects`) or a computed/shaped result — at item granularity, via `path` (dot-path to the array), `by` (item key, default `id`), and `match` (a pure predicate that scopes the patch to the entries whose current value satisfies it, preventing a patch bleeding across sibling subscriptions that share a source table). Previously auto-optimistic only patched the flat top-level row array keyed by `id`; nested values needed a hand-written `.withOptimistic` reducer. `path`/`by`/`match` are browser-safe descriptor data (a dot-path string + pure predicate), same discipline as `identify`/`shape`. A path insert is applied even on a computed entry (it targets a known document, not a blind top-level add).
52
+ - **@voltro/runtime** — `ctx.store.applyDefined(input, keys)` (and a standalone `applyDefined` export from `@voltro/runtime`) — builds a partial-update patch keeping only the listed keys whose value the caller actually provided (`!== undefined`; a defined falsy value like `0`/`''`/`false` is kept). Collapses the per-field `if (input.x !== undefined) patch.x = input.x` idiom every partial-update mutation hand-writes.
53
+ - **@voltro/database** — `.uniqueActive([cols], opts?)` on the table builder — a portable partial-UNIQUE constraint that holds only among the rows matching a predicate (default `"deletedAt" IS NULL`, pairing with `.softDelete()`). Emits `CREATE UNIQUE INDEX … WHERE` on postgres / sqlite / mssql, so a soft-deleted row leaves the active set and a NEW row with the same key inserts cleanly — no hand-written `generatedAs("CASE WHEN …")` column and no resurrection footgun. On mysql / mariadb (no partial-index support) it FAILS LOUDLY at migrate time rather than silently emitting a full unique index that would forbid re-creating a soft-deleted key — the generated-STORED-column lowering for those dialects is a follow-up. Kept out of the declarative index snapshot (the incremental planner is predicate-blind and would misclassify a unique+partial index as a full constraint), so the fresh-schema DDL path is its sole emitter and there is no re-diff churn. Live-verified against postgres.
54
+ - **@voltro/cli** — `voltro update` upgrades an app to the latest framework: it bumps every `@voltro/*` dependency, installs with the detected package manager, and runs the codemods shipped with the target version. Codemods are authored with `defineCodemod` + an import-scoped ts-morph helper toolkit (`renameImport`, `renameModuleSpecifier`, `renameJsxProp`, `renameObjectKey`, `add`/`removeImport`, structural `changeCallArgs`/`wrapCall`, `annotate`) and run against the app source; a `manual` kind surfaces written steps for changes that can't be automated. Breaking public-API changes now ship a codemod (or an explicit `codemod: none`), enforced by the changelog gate. Framework-owned `_voltro_*` table changes continue to ride the declarative differ on `voltro db apply` / `voltro dev` boot — `update` does not touch the database.
55
+
56
+ ### Fixed
57
+
58
+ - **@voltro/database** — The core-table registry (`registerCoreTables` / `requireActors` / `requireTenants`) now stores its state on a process-global `Symbol.for` singleton, the same mechanism the main table registry already uses — instead of module-local `let` bindings. Module-local state splits when the `@voltro/database` module is duplicated in a process (e.g. resolved through both the `.` and `./sql` entry points, or a bundled framework copy alongside an externally-resolved one): one instance's `registerCoreTables` becomes invisible to the instance that reads it, surfacing as a spurious `core 'actors' table not registered` at store construction. Pinning it to `globalThis` makes every copy share one store, matching the table registry's already-global behaviour.
59
+
60
+ ---
61
+
42
62
  ## [0.2.2] — 2026-07-17
43
63
 
44
64
  ### Added
@@ -5,7 +5,7 @@ property of its respective copyright holders and is used under the terms of
5
5
  its license. This file is provided for attribution; it grants no rights in
6
6
  @voltro/cli itself, which is proprietary (see LICENSE).
7
7
 
8
- Generated from the resolved runtime dependency closure (191 packages).
8
+ Generated from the resolved runtime dependency closure (198 packages).
9
9
 
10
10
  ---
11
11
 
@@ -10139,6 +10139,34 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
10139
10139
  SOFTWARE.
10140
10140
  ```
10141
10141
 
10142
+ ## @ts-morph/common@0.29.0
10143
+
10144
+ License: MIT
10145
+
10146
+ ```
10147
+ The MIT License (MIT)
10148
+
10149
+ Copyright (c) 2017 David Sherret
10150
+
10151
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10152
+ of this software and associated documentation files (the "Software"), to deal
10153
+ in the Software without restriction, including without limitation the rights
10154
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10155
+ copies of the Software, and to permit persons to whom the Software is
10156
+ furnished to do so, subject to the following conditions:
10157
+
10158
+ The above copyright notice and this permission notice shall be included in all
10159
+ copies or substantial portions of the Software.
10160
+
10161
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10162
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10163
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10164
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
10165
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
10166
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
10167
+ SOFTWARE.
10168
+ ```
10169
+
10142
10170
  ## @typespec/ts-http-runtime@0.3.5
10143
10171
 
10144
10172
  License: MIT
@@ -10490,6 +10518,36 @@ See the License for the specific language governing permissions and
10490
10518
  limitations under the License.
10491
10519
  ```
10492
10520
 
10521
+ ## balanced-match@4.0.4
10522
+
10523
+ License: MIT
10524
+
10525
+ ```
10526
+ (MIT)
10527
+
10528
+ Original code Copyright Julian Gruber <julian@juliangruber.com>
10529
+
10530
+ Port to TypeScript Copyright Isaac Z. Schlueter <i@izs.me>
10531
+
10532
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
10533
+ this software and associated documentation files (the "Software"), to deal in
10534
+ the Software without restriction, including without limitation the rights to
10535
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10536
+ of the Software, and to permit persons to whom the Software is furnished to do
10537
+ so, subject to the following conditions:
10538
+
10539
+ The above copyright notice and this permission notice shall be included in all
10540
+ copies or substantial portions of the Software.
10541
+
10542
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10543
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10544
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10545
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
10546
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
10547
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
10548
+ SOFTWARE.
10549
+ ```
10550
+
10493
10551
  ## base64-js@1.5.1
10494
10552
 
10495
10553
  License: MIT
@@ -10564,6 +10622,36 @@ programs and associated documentation files created by the
10564
10622
  Original Author, when distributed with the Software.
10565
10623
  ```
10566
10624
 
10625
+ ## brace-expansion@5.0.6
10626
+
10627
+ License: MIT
10628
+
10629
+ ```
10630
+ MIT License
10631
+
10632
+ Copyright Julian Gruber <julian@juliangruber.com>
10633
+
10634
+ TypeScript port Copyright Isaac Z. Schlueter <i@izs.me>
10635
+
10636
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10637
+ of this software and associated documentation files (the "Software"), to deal
10638
+ in the Software without restriction, including without limitation the rights
10639
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10640
+ copies of the Software, and to permit persons to whom the Software is
10641
+ furnished to do so, subject to the following conditions:
10642
+
10643
+ The above copyright notice and this permission notice shall be included in all
10644
+ copies or substantial portions of the Software.
10645
+
10646
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10647
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10648
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10649
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
10650
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
10651
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
10652
+ SOFTWARE.
10653
+ ```
10654
+
10567
10655
  ## buffer@5.6.0
10568
10656
 
10569
10657
  License: MIT
@@ -10640,6 +10728,34 @@ See the License for the specific language governing permissions and
10640
10728
  limitations under the License.
10641
10729
  ```
10642
10730
 
10731
+ ## code-block-writer@13.0.3
10732
+
10733
+ License: MIT
10734
+
10735
+ ```
10736
+ The MIT License (MIT)
10737
+
10738
+ Copyright (c) 2015-2024 David Sherret
10739
+
10740
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10741
+ of this software and associated documentation files (the "Software"), to deal
10742
+ in the Software without restriction, including without limitation the rights
10743
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10744
+ copies of the Software, and to permit persons to whom the Software is
10745
+ furnished to do so, subject to the following conditions:
10746
+
10747
+ The above copyright notice and this permission notice shall be included in all
10748
+ copies or substantial portions of the Software.
10749
+
10750
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10751
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10752
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10753
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
10754
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
10755
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
10756
+ SOFTWARE.
10757
+ ```
10758
+
10643
10759
  ## debug@4.4.3
10644
10760
 
10645
10761
  License: MIT
@@ -12590,6 +12706,68 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
12590
12706
  THE SOFTWARE.
12591
12707
  ```
12592
12708
 
12709
+ ## minimatch@10.2.3
12710
+
12711
+ License: BlueOak-1.0.0
12712
+
12713
+ ```
12714
+ # Blue Oak Model License
12715
+
12716
+ Version 1.0.0
12717
+
12718
+ ## Purpose
12719
+
12720
+ This license gives everyone as much permission to work with
12721
+ this software as possible, while protecting contributors
12722
+ from liability.
12723
+
12724
+ ## Acceptance
12725
+
12726
+ In order to receive this license, you must agree to its
12727
+ rules. The rules of this license are both obligations
12728
+ under that agreement and conditions to your license.
12729
+ You must not do anything with this software that triggers
12730
+ a rule that you cannot or will not follow.
12731
+
12732
+ ## Copyright
12733
+
12734
+ Each contributor licenses you to do everything with this
12735
+ software that would otherwise infringe that contributor's
12736
+ copyright in it.
12737
+
12738
+ ## Notices
12739
+
12740
+ You must ensure that everyone who gets a copy of
12741
+ any part of this software from you, with or without
12742
+ changes, also gets the text of this license or a link to
12743
+ <https://blueoakcouncil.org/license/1.0.0>.
12744
+
12745
+ ## Excuse
12746
+
12747
+ If anyone notifies you in writing that you have not
12748
+ complied with [Notices](#notices), you can keep your
12749
+ license by taking all practical steps to comply within 30
12750
+ days after the notice. If you do not do so, your license
12751
+ ends immediately.
12752
+
12753
+ ## Patent
12754
+
12755
+ Each contributor licenses you to do everything with this
12756
+ software that would otherwise infringe any patent claims
12757
+ they can license or become able to license.
12758
+
12759
+ ## Reliability
12760
+
12761
+ No contributor can revoke this license.
12762
+
12763
+ ## No Liability
12764
+
12765
+ **_As far as the law allows, this software comes as is,
12766
+ without any warranty or condition, and no contributor
12767
+ will be liable to anyone for any damages related to this
12768
+ software or this license, under any kind of legal claim._**
12769
+ ```
12770
+
12593
12771
  ## ms@2.1.3
12594
12772
 
12595
12773
  License: MIT
@@ -12823,6 +13001,33 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
12823
13001
  USE OR OTHER DEALINGS IN THE SOFTWARE.
12824
13002
  ```
12825
13003
 
13004
+ ## path-browserify@1.0.1
13005
+
13006
+ License: MIT
13007
+
13008
+ ```
13009
+ MIT License
13010
+
13011
+ Copyright (c) 2013 James Halliday
13012
+
13013
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
13014
+ this software and associated documentation files (the "Software"), to deal in
13015
+ the Software without restriction, including without limitation the rights to
13016
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
13017
+ the Software, and to permit persons to whom the Software is furnished to do so,
13018
+ subject to the following conditions:
13019
+
13020
+ The above copyright notice and this permission notice shall be included in all
13021
+ copies or substantial portions of the Software.
13022
+
13023
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13024
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
13025
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
13026
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
13027
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
13028
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13029
+ ```
13030
+
12826
13031
  ## path-expression-matcher@1.5.0
12827
13032
 
12828
13033
  License: MIT
@@ -13982,6 +14187,34 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13982
14187
  SOFTWARE.
13983
14188
  ```
13984
14189
 
14190
+ ## ts-morph@28.0.0
14191
+
14192
+ License: MIT
14193
+
14194
+ ```
14195
+ The MIT License (MIT)
14196
+
14197
+ Copyright (c) 2017 David Sherret
14198
+
14199
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14200
+ of this software and associated documentation files (the "Software"), to deal
14201
+ in the Software without restriction, including without limitation the rights
14202
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14203
+ copies of the Software, and to permit persons to whom the Software is
14204
+ furnished to do so, subject to the following conditions:
14205
+
14206
+ The above copyright notice and this permission notice shall be included in all
14207
+ copies or substantial portions of the Software.
14208
+
14209
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14210
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14211
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14212
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14213
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
14214
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
14215
+ SOFTWARE.
14216
+ ```
14217
+
13985
14218
  ## tslib@2.8.1
13986
14219
 
13987
14220
  License: 0BSD
package/bin/voltro.mjs CHANGED
@@ -1,4 +1,16 @@
1
1
  #!/usr/bin/env node
2
+ // `framework` executable. `pnpm install` symlinks this into every
3
+ // dependent package's `node_modules/.bin/framework`, so user apps invoke
4
+ // it from their npm scripts as just `voltro dev` / `voltro serve`
5
+ // — same UX as `next dev` or `vite`, without the user knowing the bin
6
+ // lives in our monorepo's framework/ tree.
7
+ //
8
+ // Prefer the bundled, minified `dist/bin.js` (the published artifact);
9
+ // fall back to the TypeScript `src/bin.ts` when running from the
10
+ // monorepo before a build (dev). The tsx ESM loader stays registered in
11
+ // BOTH modes because the CLI imports the USER's `app.config.ts` (and
12
+ // other `.ts` app files) at runtime — Node cannot import those without
13
+ // it. The public bin interface (`voltro <cmd>`) is unchanged.
2
14
 
3
15
  import { spawn } from 'node:child_process'
4
16
  import { existsSync } from 'node:fs'
@@ -6,6 +18,16 @@ import { createRequire } from 'node:module'
6
18
  import { fileURLToPath, pathToFileURL } from 'node:url'
7
19
  import { dirname, join, resolve } from 'node:path'
8
20
 
21
+ // FAST PATH — `voltro serve <app>` with a precompiled SERVE BUNDLE boots from that
22
+ // ONE artefact IN THIS PROCESS: no child `node --import tsx`, no CLI command
23
+ // graph. `voltro build` esbuilds the whole serve path (framework + effect +
24
+ // @voltro inlined) into `.framework/dist-api/serveBundle/serveEntry.js` with the
25
+ // app modules as lazy .js chunks — so tsx is UNNECESSARY (nothing loads `.ts` at
26
+ // runtime) and the second node startup + the tsx loader hook are pure overhead on
27
+ // a cold scale-to-zero boot. The bundle's exported `runServe` loads `.env` itself
28
+ // (idempotent) so the boot is fully self-contained here. In PRODUCTION the bundle
29
+ // is MANDATORY — a missing/unusable bundle fails loud (below), never on-demand
30
+ // tsx. Only non-production falls through to the tsx spawn path (a dev convenience).
9
31
  const argv = process.argv.slice(2)
10
32
  if (argv[0] === 'serve') {
11
33
  const appArg = argv.slice(1).find((a) => !a.startsWith('-'))
@@ -13,6 +35,11 @@ if (argv[0] === 'serve') {
13
35
  const serveBundle = join(serveRoot, '.framework', 'dist-api', 'serveBundle', 'serveEntry.js')
14
36
  if (existsSync(serveBundle)) {
15
37
  try {
38
+ // chdir to the app root BEFORE importing the bundle: the bundle's generated
39
+ // entry registers its app-module keys from `process.cwd()` at import time, so
40
+ // cwd must already be the app root (the serve runtime resolves the same keys
41
+ // by walking the app tree). This is what makes the bundle relocation-safe —
42
+ // the Docker deploy moves the app from `apps/x/api` to the image root.
16
43
  if (serveRoot !== process.cwd()) process.chdir(serveRoot)
17
44
  const mod = await import(pathToFileURL(serveBundle).href)
18
45
  if (typeof mod.runServe === 'function') {
@@ -20,17 +47,42 @@ if (argv[0] === 'serve') {
20
47
  process.exit(typeof code === 'number' ? code : 0)
21
48
  }
22
49
  } catch (error) {
23
- console.warn('[voltro] serve bundle failed to load — using the standard serve path:', error?.message ?? error)
50
+ // Unusable bundle degrade to the standard tsx path, never crash the boot.
51
+ console.warn('[voltro] serve bundle failed to load:', error?.message ?? error)
24
52
  }
25
53
  }
54
+ // PRODUCTION MUST serve from the precompiled bundle — never transpile on demand.
55
+ // If we reach here in production the bundle is missing or unusable: FAIL LOUD
56
+ // instead of silently spawning the slow tsx path (which re-transpiles the app on
57
+ // every cold start, defeating the whole precompiled-boot optimisation). `voltro
58
+ // build` produces the bundle and treats a build failure as fatal, so a healthy
59
+ // deploy never hits this. Non-production (dev / an unbuilt local serve) falls
60
+ // through to the tsx path below — on-demand transpile is a DEV-only convenience.
61
+ if (process.env.NODE_ENV === 'production') {
62
+ console.error(
63
+ `[voltro] FATAL: production \`voltro serve\` requires a precompiled serve bundle at\n ${serveBundle}\n` +
64
+ 'but it is missing or failed to load. Run `voltro build` before serving — a bundle-build ' +
65
+ 'failure is fatal by design; production never transpiles on demand.',
66
+ )
67
+ process.exit(1)
68
+ }
26
69
  }
27
70
 
28
71
  const here = dirname(fileURLToPath(import.meta.url))
29
72
  const built = resolve(here, '..', 'dist', 'bin.js')
30
73
  const entry = existsSync(built) ? built : resolve(here, '..', 'src', 'bin.ts')
31
74
 
75
+ // Resolve the tsx ESM loader RELATIVE TO @voltro/cli (which depends on it) —
76
+ // NOT the user app's CWD. As a bare `--import tsx`, Node resolves the specifier
77
+ // from the current working directory; strict pnpm nests tsx inside @voltro/cli,
78
+ // so from a consuming app's dir it isn't reachable and a standalone (non-
79
+ // monorepo) install fails at `voltro dev` / `voltro build` with "Cannot find
80
+ // package 'tsx'". Resolving to an absolute URL here sidesteps CWD resolution
81
+ // entirely (the monorepo happened to hoist tsx, which hid this).
32
82
  let tsxImport = 'tsx'
33
83
  try {
84
+ // import.meta.resolve applies tsx's export map (the ESM auto-register entry);
85
+ // synchronous since Node 20.6.
34
86
  tsxImport = import.meta.resolve('tsx')
35
87
  } catch {
36
88
  try {
@@ -46,6 +98,11 @@ const child = spawn(
46
98
  { stdio: 'inherit' },
47
99
  )
48
100
 
101
+ // Forward signals the parent receives to the child. Without this the
102
+ // shim catches SIGTERM (e.g. from `kill $PID` in test-all.sh) and exits,
103
+ // leaving the spawned Node child as an orphan still bound to its port.
104
+ // Subsequent harness stages then fail to start their own server because
105
+ // the port is held by the orphan.
49
106
  const forward = (signal) => () => {
50
107
  if (!child.killed) child.kill(signal)
51
108
  }
@@ -61,12 +118,25 @@ child.on('exit', (code, signal) => {
61
118
  process.exit(code ?? 0)
62
119
  })
63
120
 
121
+ // Parent-death guard. When invoked from a smoke (or any wrapper) that
122
+ // dies ungracefully — `process.exit(1)` skipping `finally`, kill -9,
123
+ // IDE termination — we'd otherwise be reparented to init and keep our
124
+ // tsx child alive holding the port. We watch our parent pid; when it
125
+ // flips to 1 we forward SIGTERM to the child and exit. The 2s interval
126
+ // gives the harness fast-enough orphan cleanup without burning a watch
127
+ // loop per shim. The TSX child has its own ppid watch on US (the shim),
128
+ // so this layer covers the case where the shim itself outlives its
129
+ // parent; the inner layer covers the unusual case of the shim dying
130
+ // while the tsx child somehow survives.
64
131
  const initialPpid = process.ppid
65
132
  if (initialPpid > 1) {
66
133
  const ppidWatch = setInterval(() => {
67
134
  if (process.ppid !== initialPpid || process.ppid === 1) {
68
135
  clearInterval(ppidWatch)
69
136
  if (!child.killed) child.kill('SIGTERM')
137
+ // Give the child a brief window to clean up + exit; if it doesn't,
138
+ // we exit anyway. The child's `on('exit')` above will route us out
139
+ // cleanly if it does.
70
140
  setTimeout(() => process.exit(0), 1500).unref()
71
141
  }
72
142
  }, 2000)
@@ -0,0 +1,193 @@
1
+ import { Yt as e, d as t, et as n } from "./dev-x_VqbV_8.js";
2
+ import { isAbsolute as r, join as i, relative as a, resolve as o } from "node:path";
3
+ import { createLogger as s } from "@voltro/logger";
4
+ import { existsSync as c, promises as l } from "node:fs";
5
+ import { createRequire as u } from "node:module";
6
+ //#region src/apiBuild.ts
7
+ var d = s({ scope: "voltro:build" }), f = async (e) => {
8
+ let t = i(e, "package.json");
9
+ if (!c(t)) return [];
10
+ let r = {};
11
+ try {
12
+ let e = JSON.parse(await l.readFile(t, "utf8"));
13
+ r = {
14
+ ...e.dependencies,
15
+ ...e.devDependencies,
16
+ ...e.optionalDependencies
17
+ };
18
+ } catch {
19
+ return [];
20
+ }
21
+ let a = u(t);
22
+ return n.filter((e) => {
23
+ if (r[e] === void 0) return !1;
24
+ try {
25
+ return a.resolve(e), !0;
26
+ } catch {
27
+ return !1;
28
+ }
29
+ });
30
+ }, p = /(?:\.(?:query|mutation|action|stream)(?:\.server)?\.ts|\.workflow(?:\.server)?\.tsx|\.(?:subscribe|reaction|aggregate|agent|tool|webhook|cron|trigger)(?:\.server)?\.tsx?|\.(?:startup|seed)\.tsx?|\.(?:entity|schema|relations)\.ts|(?:^|\/)schema\.ts)$/, m = (e) => /\.(ts|tsx)$/.test(e) && !/\.(test|spec)\.[tj]sx?$/.test(e), h = async (e) => (await t(e)).filter((t) => p.test(t) && m(t) && !t.includes(`${e}/.framework/`)), g = {
31
+ name: "externalize-bare",
32
+ setup(e) {
33
+ e.onResolve({ filter: /.*/ }, (e) => {
34
+ let t = e.path;
35
+ if (!(t.startsWith(".") || r(t))) return {
36
+ path: t,
37
+ external: !0
38
+ };
39
+ });
40
+ }
41
+ }, _ = async (t) => {
42
+ let n = await e("esbuild", () => import("esbuild")), r = i(t, ".framework"), s = i(r, "dist-api");
43
+ await l.mkdir(r, { recursive: !0 });
44
+ let u = [...await h(t)], f = i(t, "app.config.ts");
45
+ if (c(f) && u.push(f), u.length === 0) return d.warn("api build: no handlers or app.config found — nothing to precompile", { root: t }), null;
46
+ let p = i(t, "tsconfig.json"), _ = {
47
+ bundle: !0,
48
+ format: "esm",
49
+ platform: "node",
50
+ target: "node22",
51
+ logLevel: "silent",
52
+ jsx: "automatic",
53
+ jsxImportSource: "react",
54
+ absWorkingDir: t,
55
+ ...c(p) ? { tsconfig: p } : {},
56
+ plugins: [g]
57
+ }, v = await n.build({
58
+ ..._,
59
+ entryPoints: u,
60
+ write: !1,
61
+ metafile: !0,
62
+ outdir: s
63
+ }), y = Object.keys(v.metafile.inputs).map((e) => o(t, e)).filter((e) => e.startsWith(`${t}/`) && !e.includes("/node_modules/") && !e.includes("/.framework/") && m(e)), b = [
64
+ "// GENERATED BY `voltro build` — do not edit; overwritten on next build.",
65
+ "// Precompiled api entry: the single bundle the serve runtime loads instead",
66
+ "// of importing every handler/schema source at boot. See apiBuild.ts /",
67
+ "// appModuleLoader.ts.",
68
+ ""
69
+ ], x = [];
70
+ y.forEach((e, n) => {
71
+ b.push(`import * as m${n} from ${JSON.stringify(e)}`), x.push(` ${JSON.stringify(a(t, e))}: m${n},`);
72
+ }), b.push("", "export const modules: Record<string, unknown> = {", ...x, "}", "");
73
+ let S = i(r, "apiEntry.ts");
74
+ return await l.writeFile(S, b.join("\n"), "utf8"), await n.build({
75
+ ..._,
76
+ entryPoints: [S],
77
+ outfile: i(s, "apiEntry.js"),
78
+ metafile: !1,
79
+ write: !0
80
+ }), d.info("api build: precompiled bundle written", {
81
+ modules: y.length,
82
+ out: i(".framework", "dist-api", "apiEntry.js")
83
+ }), i(s, "apiEntry.js");
84
+ }, v = (e) => i(e, ".framework", "dist-api", "apiEntry.js"), y = [
85
+ "better-sqlite3",
86
+ "mysql2",
87
+ "tedious",
88
+ "pg",
89
+ "pg-native",
90
+ "@tursodatabase/*",
91
+ "@libsql/*",
92
+ "ioredis",
93
+ "@vlasky/zongji",
94
+ "nodemailer"
95
+ ], b = [
96
+ "tsx",
97
+ "esbuild",
98
+ "vite",
99
+ "@vitejs/*",
100
+ "@tailwindcss/*",
101
+ "rolldown",
102
+ "lightningcss"
103
+ ], x = (e, t) => t.some((t) => t.endsWith("/*") ? e === t.slice(0, -2) || e.startsWith(t.slice(0, -1)) : e === t || e.startsWith(`${t}/`)), S = (e, t) => `const { createRequire } = require('node:module')
104
+ const { join } = require('node:path')
105
+ const reqApp = createRequire(join(process.cwd(), 'package.json'))
106
+ const drivers = ${JSON.stringify(t)}\nlet mod\nfor (const d of drivers) {\n try { mod = createRequire(reqApp.resolve(d + '/package.json'))(${JSON.stringify(e)}); break } catch (e) {}\n}\nif (!mod) { try { mod = reqApp(${JSON.stringify(e)}) } catch (e) {} }\nif (!mod) mod = require(${JSON.stringify(e)})\nmodule.exports = mod\n`, C = (e, t, n) => ({
107
+ name: "native-leaf-runtime-resolver",
108
+ setup(a) {
109
+ a.onResolve({ filter: /.*/ }, (e) => {
110
+ let a = e.path;
111
+ if (!(a.startsWith(".") || r(a) || a.startsWith("node:"))) {
112
+ if (x(a, y)) return {
113
+ path: a,
114
+ namespace: "native-leaf"
115
+ };
116
+ if (x(a, b)) return {
117
+ path: a,
118
+ external: !0
119
+ };
120
+ for (let n of [e.resolveDir, t]) if (n) try {
121
+ u(i(n, "_.js")).resolve(a);
122
+ return;
123
+ } catch {}
124
+ return n.add(a), {
125
+ path: a,
126
+ external: !0
127
+ };
128
+ }
129
+ }), a.onLoad({
130
+ filter: /.*/,
131
+ namespace: "native-leaf"
132
+ }, (t) => ({
133
+ contents: S(t.path, e),
134
+ loader: "js"
135
+ }));
136
+ }
137
+ }), w = (e) => i(e, ".framework", "dist-api", "serveBundle", "serveEntry.js"), T = async (t) => {
138
+ let n = await e("esbuild", () => import("esbuild")), r = i(t, ".framework"), s = i(r, "dist-api", "serveBundle");
139
+ await l.mkdir(r, { recursive: !0 });
140
+ let u = [...await h(t)], p = i(t, "app.config.ts");
141
+ if (c(p) && u.push(p), u.length === 0) return null;
142
+ let _ = i(t, "tsconfig.json"), v = await n.build({
143
+ entryPoints: u,
144
+ bundle: !0,
145
+ write: !1,
146
+ metafile: !0,
147
+ format: "esm",
148
+ platform: "node",
149
+ absWorkingDir: t,
150
+ jsx: "automatic",
151
+ jsxImportSource: "react",
152
+ logLevel: "silent",
153
+ plugins: [g],
154
+ outdir: i(s, ".probe"),
155
+ ...c(_) ? { tsconfig: _ } : {}
156
+ }), y = Object.keys(v.metafile.inputs).map((e) => o(t, e)).filter((e) => e.startsWith(`${t}/`) && !e.includes("/node_modules/") && !e.includes("/.framework/") && m(e)), b = await f(t), x = /* @__PURE__ */ new Set(), S = [
157
+ "// GENERATED BY `voltro build` — do not edit; overwritten on next build.",
158
+ "import { join as __join } from 'node:path'",
159
+ "import { runServe as __runServe, registerAppModules, registerDriver, loadDotEnv } from '@voltro/cli/serveEntry'",
160
+ ...b.map((e, t) => `import * as __drv${t} from ${JSON.stringify(e)}`),
161
+ "const __root = process.cwd()",
162
+ "registerAppModules({",
163
+ ...y.map((e) => ` [__join(__root, ${JSON.stringify(a(t, e))})]: () => import(${JSON.stringify(e)}),`),
164
+ "})",
165
+ ...b.map((e, t) => `registerDriver(${JSON.stringify(e)}, __drv${t})`),
166
+ "export const runServe = (args = []) => { loadDotEnv([]); return __runServe(args) }",
167
+ ""
168
+ ], w = i(r, "serveEntry.ts");
169
+ return await l.writeFile(w, S.join("\n"), "utf8"), await n.build({
170
+ entryPoints: [w],
171
+ bundle: !0,
172
+ format: "esm",
173
+ platform: "node",
174
+ target: "node22",
175
+ outdir: s,
176
+ splitting: !0,
177
+ write: !0,
178
+ plugins: [C(b, t, x)],
179
+ banner: { js: "import { createRequire as __voltroRequire } from 'node:module'; const require = __voltroRequire(import.meta.url);" },
180
+ jsx: "automatic",
181
+ jsxImportSource: "react",
182
+ logLevel: "silent",
183
+ absWorkingDir: t,
184
+ ...c(_) ? { tsconfig: _ } : {}
185
+ }), d.info("api build: serve bundle written", {
186
+ appModules: y.length,
187
+ drivers: b.length > 0 ? b.join(", ") : "none (memory)",
188
+ ...x.size > 0 ? { externalizedOptionalPeers: [...x].join(", ") } : {},
189
+ out: i(".framework", "dist-api", "serveBundle", "serveEntry.js")
190
+ }), i(s, "serveEntry.js");
191
+ };
192
+ //#endregion
193
+ export { w as a, T as i, h as n, _ as r, v as t };
@@ -0,0 +1,2 @@
1
+ import { i as e, r as t } from "./apiBuild-DdgYydVJ.js";
2
+ export { t as runApiBuild, e as runServeBundleBuild };
package/dist/bin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { At as e } from "./dev--jHe1vcu.js";
3
- import { n as t } from "./commands-DQy4812j.js";
2
+ import { At as e } from "./dev-x_VqbV_8.js";
3
+ import { n as t } from "./commands-CXESev-z.js";
4
4
  import { createLogger as n } from "@voltro/logger";
5
5
  //#region src/bin.ts
6
6
  var r = n({ scope: "voltro:cli" }), i = process.argv.slice(2);