@voltro/cli 0.50.1 → 0.51.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 (103) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/dist/{apiBuild-D3s7lx57.js → apiBuild-CPDHXF72.js} +2 -2
  3. package/dist/apiBuild-GDKuGOMV.js +2 -0
  4. package/dist/appGraph-CFmFKHb2.js +2 -0
  5. package/dist/appGraph-yRuKjOba.js +111 -0
  6. package/dist/bin.js +1 -1
  7. package/dist/{build-CFmqF39c.js → build-DETLZAFt.js} +271 -240
  8. package/dist/{checkCommand-DomvYDi1.js → checkCommand-CWcnDArJ.js} +1 -1
  9. package/dist/{checkCommand-DsKIjL6k.js → checkCommand-DNuPiWMc.js} +29 -28
  10. package/dist/{codegenCommand-C0S27nbB.js → codegenCommand-BNBHcNNj.js} +2 -2
  11. package/dist/{codemodRunner-DwY8Rjbl.js → codemodRunner-BDVixlSw.js} +278 -191
  12. package/dist/{commands-Brp4DI8u.js → commands-B1OiS9bX.js} +24 -23
  13. package/dist/{dashboardCommand-bbLfc9FW.js → dashboardCommand-C-vvPY1B.js} +5 -5
  14. package/dist/{dataCommand-k9mXTXin.js → dataCommand-C1GxXW5q.js} +279 -233
  15. package/dist/dbCommand-C27HIsGE.js +2 -0
  16. package/dist/{dbCommand-Bf7Cy0mn.js → dbCommand-If4Y1xQ-.js} +1 -1
  17. package/dist/{dev-Dz9GqA6P.js → dev-CK522MV5.js} +1 -1
  18. package/dist/{dev-C0cbqysu.js → dev-kdAg9Q7l.js} +1246 -1246
  19. package/dist/doctorCommand-BK4l18eG.js +2 -0
  20. package/dist/{doctorCommand-DyViaaCP.js → doctorCommand-nKmeW78u.js} +16 -16
  21. package/dist/{dormancyCommand-DMZPA_HW.js → dormancyCommand-CY3wa_SW.js} +1 -1
  22. package/dist/{embeddingsCommand-CMxRPkg6.js → embeddingsCommand-BDLIgje_.js} +1 -1
  23. package/dist/{envCommand-CDLr-yD4.js → envCommand-C6V_xVlT.js} +13 -13
  24. package/dist/{evolveCommand-Ct4NesL3.js → evolveCommand-D3c4DSfN.js} +2 -2
  25. package/dist/{generateCommand-BDdOxhnv.js → generateCommand-DTNPGjxo.js} +1 -1
  26. package/dist/index.js +1 -1
  27. package/dist/{infoCommand-orsKJKUy.js → infoCommand-BnRFEF1o.js} +1 -1
  28. package/dist/{migrate-D3MK9BpK.js → migrate-DehuBakM.js} +1 -1
  29. package/dist/{runtimeTrace-Bo8jWsNe.js → runtimeTrace-ZsBU7Tkx.js} +1 -1
  30. package/dist/{sdkgen-OZhiT2vz.js → sdkgen-O4XqWOjM.js} +1 -1
  31. package/dist/serveCommand-BRnPCxVd.js +2 -0
  32. package/dist/{serveCommand-YbD5VgfV.js → serveCommand-DdiYNBBu.js} +3 -3
  33. package/dist/serveEntry.js +1 -1
  34. package/dist/{start-B1nH9ahf.js → start-BLNmWkLa.js} +331 -327
  35. package/dist/{start-7b_bL8Uc.js → start-Dzicuyw8.js} +1 -1
  36. package/dist/startEntry.js +1 -1
  37. package/dist/{updateCommand-BgH6vTtq.js → updateCommand-Bs322Q78.js} +1 -1
  38. package/dist/updateCommand-eXB35SEv.js +2 -0
  39. package/dist/{webDev-BbdkzMbd.js → webDev-B-ubQEMX.js} +282 -223
  40. package/dist/webDev-DposiF3j.js +2 -0
  41. package/dist/{webhooksCommand-DjE6u0r4.js → webhooksCommand-FLYY9IXh.js} +1 -1
  42. package/package.json +35 -17
  43. package/templates/AGENTS.core.md +15 -5
  44. package/templates/AGENTS.md +16 -6
  45. package/templates/agent-docs/_index.md +1 -1
  46. package/templates/agent-docs/cli.md +29 -0
  47. package/templates/agent-docs/internationalization.md +131 -13
  48. package/templates/agent-docs/whats-new.md +75 -27
  49. package/templates/apps/api-ai/package.json +7 -7
  50. package/templates/apps/api-auth/package.json +8 -8
  51. package/templates/apps/api-backend/package.json +7 -7
  52. package/templates/apps/api-backend-deactivation/package.json +7 -7
  53. package/templates/apps/api-backend-mail/package.json +8 -8
  54. package/templates/apps/api-backend-mariadb/package.json +9 -9
  55. package/templates/apps/api-backend-sqlite/package.json +8 -8
  56. package/templates/apps/api-backend-storage/package.json +8 -8
  57. package/templates/apps/api-cms/package.json +10 -10
  58. package/templates/apps/api-collab/package.json +8 -8
  59. package/templates/apps/api-data-advanced/package.json +8 -8
  60. package/templates/apps/api-durable/package.json +8 -8
  61. package/templates/apps/api-feature-flags/package.json +9 -9
  62. package/templates/apps/api-governance/package.json +8 -8
  63. package/templates/apps/api-kv/package.json +8 -8
  64. package/templates/apps/api-moderation/package.json +8 -8
  65. package/templates/apps/api-observability/package.json +8 -8
  66. package/templates/apps/api-ratelimit/package.json +8 -8
  67. package/templates/apps/api-rbac/package.json +8 -8
  68. package/templates/apps/api-rest/package.json +7 -7
  69. package/templates/apps/api-saas/package.json +11 -11
  70. package/templates/apps/api-saas-starter/package.json +10 -10
  71. package/templates/apps/api-search/package.json +8 -8
  72. package/templates/apps/api-status/package.json +8 -8
  73. package/templates/apps/api-versioning/package.json +8 -8
  74. package/templates/apps/api-webhooks/package.json +9 -9
  75. package/templates/apps/changelog/package.json +6 -6
  76. package/templates/apps/edge-functions/package.json +2 -2
  77. package/templates/apps/frontend-admin/package.json +8 -8
  78. package/templates/apps/frontend-app/package.json +9 -9
  79. package/templates/apps/frontend-auth/package.json +8 -8
  80. package/templates/apps/frontend-blank/package.json +7 -7
  81. package/templates/apps/frontend-cms/package.json +9 -9
  82. package/templates/apps/frontend-collab/package.json +10 -10
  83. package/templates/apps/frontend-contact/package.json +7 -7
  84. package/templates/apps/frontend-dashboard/package.json +7 -7
  85. package/templates/apps/frontend-docs/package.json +7 -7
  86. package/templates/apps/frontend-i18n/package.json +6 -6
  87. package/templates/apps/frontend-landing/package.json +7 -7
  88. package/templates/apps/frontend-portal/package.json +8 -8
  89. package/templates/apps/frontend-saas/package.json +8 -8
  90. package/templates/apps/frontend-spa/package.json +7 -7
  91. package/templates/apps/frontend-ssr/package.json +7 -7
  92. package/templates/apps/frontend-ssr-api/package.json +8 -8
  93. package/templates/apps/frontend-static-blog/package.json +6 -6
  94. package/templates/apps/frontend-status/package.json +8 -8
  95. package/templates/apps/mobile-app/package.json +4 -4
  96. package/dist/apiBuild-BZ-plaQN.js +0 -2
  97. package/dist/appGraph-CM9wvUmD.js +0 -2
  98. package/dist/appGraph-Cv2W_wO5.js +0 -111
  99. package/dist/dbCommand-Bnl7MSGC.js +0 -2
  100. package/dist/doctorCommand-BlciWUJb.js +0 -2
  101. package/dist/serveCommand-Bbgg7Paq.js +0 -2
  102. package/dist/updateCommand-BweY17z-.js +0 -2
  103. package/dist/webDev-BcRFTsMA.js +0 -2
@@ -5626,7 +5626,209 @@ var C = (e, t, n) => {
5626
5626
  }
5627
5627
  });
5628
5628
  return t;
5629
- }, bi = [
5629
+ }, bi = T({
5630
+ version: "0.49.0",
5631
+ id: "0.49.0/02_import-run-symbols",
5632
+ title: "`ImportRun` / `describeImportRun` / `IMPORT_RUNS_TABLE` → their `…TransferRun` names",
5633
+ kind: "transform",
5634
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
5635
+ let t = e.getFullText();
5636
+ return t.includes(J) ? !0 : gi.some((e) => t.includes(e)) ? _i.some(([e]) => t.includes(e)) : !1;
5637
+ }),
5638
+ transform: ({ project: e, helpers: t, log: n }) => {
5639
+ let r = 0;
5640
+ for (let n of gi) for (let [i, a] of _i) r += t.renameImport(e, {
5641
+ module: n,
5642
+ from: i,
5643
+ to: a
5644
+ });
5645
+ let i = yi(e);
5646
+ if (r + i === 0) return;
5647
+ let a = [];
5648
+ r > 0 && a.push(`${r} import(s) renamed to their \`…TransferRun\` names`), i > 0 && a.push(`${i} hand-spelled \`${J}\` → \`${vi}\``), n(`data-transfer run history: ${a.join("; ")}. An export writes to this record now, so the old names described half of what the table holds. The table itself carries a rename marker — \`voltro db apply\` (or a \`voltro dev\` boot) moves it with its rows on every dialect, so no data is lost and nothing is required of you there.`);
5649
+ }
5650
+ }), xi = T({
5651
+ version: "0.50.0",
5652
+ id: "0.50.0/01_transfer-runs-endpoint",
5653
+ title: "`GET /_voltro/admin/imports` → `GET /_voltro/admin/transfers`",
5654
+ kind: "manual",
5655
+ reach: "beyond-source",
5656
+ appliesTo: ({ text: e }) => e.find("/_voltro/admin/imports").length > 0,
5657
+ note: [
5658
+ "The read-only endpoint that reports data-transfer runs has moved:",
5659
+ "",
5660
+ " GET /_voltro/admin/imports → GET /_voltro/admin/transfers",
5661
+ "",
5662
+ "Same secret, same query parameters, same response shape. Only the path changed.",
5663
+ "",
5664
+ "It answers for four directions now — import, export, native backup, native",
5665
+ "restore — and it named one of them. The subcommand and the table were renamed",
5666
+ "for that reason in 0.49.0; this path was left behind, which is worse than not",
5667
+ "renaming any of them: a reader who followed the rename tries the matching path,",
5668
+ "gets a 404, and concludes the instance is too old.",
5669
+ "",
5670
+ "`voltro data transfers --target api` uses the new path for you. What needs your",
5671
+ "hand is anything that calls it directly — a monitoring check, an uptime probe,",
5672
+ "a curl in a runbook, a dashboard datasource."
5673
+ ].join("\n")
5674
+ }), Si = T({
5675
+ version: "0.50.0",
5676
+ id: "0.50.0/02_reissue-data-imports-rename",
5677
+ title: "RE-CHECK: `voltro data imports` → `voltro data transfers` (0.49.0's note could not see your scripts)",
5678
+ kind: "manual",
5679
+ reach: "beyond-source",
5680
+ note: [
5681
+ "This is a re-check of a rename that shipped in 0.49.0, not a new change.",
5682
+ "",
5683
+ " voltro data imports → voltro data transfers",
5684
+ "",
5685
+ "The 0.49.0 note that announced it only searched `.ts` / `.tsx` files — while",
5686
+ "the command it is about lives in shell scripts, CI job definitions, Makefiles",
5687
+ "and runbooks. So a project whose only occurrence was in, say, `.gitlab-ci.yml`",
5688
+ "was told there was nothing to apply.",
5689
+ "",
5690
+ "A codemod runs once, on the jump that crosses its version, so that note cannot",
5691
+ "be corrected for anyone already past 0.49.0. Hence this one. Search your",
5692
+ "pipelines, deploy jobs and runbooks for `voltro data imports` now:",
5693
+ "",
5694
+ " grep -rn \"voltro data imports\" .",
5695
+ "",
5696
+ "The search `voltro update` runs is wider from 0.50.0 on (every text file git",
5697
+ "tracks, not just source), but it still only reaches THIS repository — a step",
5698
+ "defined in a CI runner's own UI, a CronJob spec in another repo, or a wiki",
5699
+ "page is out of its reach by construction.",
5700
+ "",
5701
+ "Nothing else is required: the four renamed `@voltro/database` exports were",
5702
+ "rewritten for you in 0.49.0, and the table carries a rename marker, so",
5703
+ "`voltro db apply` moved `_voltro_data_imports` → `_voltro_data_transfers`",
5704
+ "with its rows."
5705
+ ].join("\n")
5706
+ }), Ci = T({
5707
+ version: "0.50.0",
5708
+ id: "0.50.0/03_interrupted-replace-tables-nullable",
5709
+ title: "`InterruptedReplace.tables` is `number | null`",
5710
+ kind: "manual",
5711
+ appliesTo: ({ text: e }) => e.find("InterruptedReplace").length > 0,
5712
+ note: [
5713
+ "`InterruptedReplace.tables` is now `number | null`:",
5714
+ "",
5715
+ " readonly tables: number → readonly tables: number | null",
5716
+ "",
5717
+ "If you read it, `tsc` will point at the site. `null` means a native `voltro",
5718
+ "data restore` wrote the marker: it replaces the whole database from an",
5719
+ "artefact, so a table count is neither known up front nor meaningful after.",
5720
+ "Read `kind` (a new field, `'replace'` | `'restore'`) when you need to tell",
5721
+ "the two apart.",
5722
+ "",
5723
+ "The framework's own refusal text handles it by saying \"this database\" instead",
5724
+ "of a count. If you render your own, that is the shape to copy — the one thing",
5725
+ "not to do is coerce it, because `Number(null)` is `0` and the sentence then",
5726
+ "reports a measurement that was never taken."
5727
+ ].join("\n")
5728
+ }), wi = T({
5729
+ version: "0.51.0",
5730
+ id: "0.51.0/01_computed-source-keeps-its-names",
5731
+ title: "A COMPUTED `source:` can keep its typed names — do not widen the whole registry",
5732
+ kind: "manual",
5733
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
5734
+ let t = e.getFullText();
5735
+ return /\bsource\s*:/.test(t) || t.includes("ReactivitySourceValue");
5736
+ }),
5737
+ note: [
5738
+ "A follow-up to the 0.45.0 note about `source:` being typed against your",
5739
+ "tables. Nothing changed in this release — this covers a case that note did",
5740
+ "not, and it can only be sent once, so it is sent to everyone.",
5741
+ "",
5742
+ "THE CASE",
5743
+ "",
5744
+ "A GENERIC reader — the table is chosen by the caller out of a registry, or",
5745
+ "arrives in `input` — is computed, and yet every name it can produce is known:",
5746
+ "",
5747
+ " source: Object.keys(JUNCTION_REGISTRY), // string[] — not assignable",
5748
+ "",
5749
+ "The 0.45.0 note offers two remedies and neither fits. There is no misspelled",
5750
+ "name to fix. And widening — `ReactivitySourceValue`, or `as TableName[]` —",
5751
+ "compiles at once while removing that entire registry from the check. It is",
5752
+ "the easiest thing to reach for and it opts out wholesale, silently.",
5753
+ "",
5754
+ "WHAT TO DO INSTEAD",
5755
+ "",
5756
+ "Keep the literal’s keys instead of recovering them afterwards:",
5757
+ "",
5758
+ " const REGISTRY = { … } as const satisfies Readonly<Record<string, JunctionMeta>>",
5759
+ "",
5760
+ " // consumers that index with a plain string still get the wide type",
5761
+ " export const JUNCTION_REGISTRY: Readonly<Record<string, JunctionMeta>> = REGISTRY",
5762
+ "",
5763
+ " // … and the source list keeps its literal names",
5764
+ " export const JUNCTION_TABLE_NAMES = Object.keys(REGISTRY) as ReadonlyArray<keyof typeof REGISTRY>",
5765
+ "",
5766
+ "THE PART THAT IS EASY TO GET WRONG",
5767
+ "",
5768
+ "An ANNOTATION widens the keys back, even when the literal carries",
5769
+ "`as const`. A registry written as",
5770
+ "",
5771
+ " export const R: Readonly<Record<string, Meta>> = { … } as const",
5772
+ "",
5773
+ "has `keyof typeof R === string`, and nothing about it looks wrong: the",
5774
+ "annotation is checked against the literal and then replaces its type.",
5775
+ "`satisfies` checks WITHOUT replacing. That is the whole difference, and it",
5776
+ "is why the two-line split above exists rather than one declaration.",
5777
+ "",
5778
+ "`ReactivitySourceValue` stays correct for a source that genuinely is not a",
5779
+ "literal — a name read from a config file, or one assembled at runtime. Use it",
5780
+ "when there is no key set to keep, not when recovering one takes two lines."
5781
+ ].join("\n")
5782
+ }), Ti = T({
5783
+ version: "0.51.0",
5784
+ id: "0.51.0/02_restore-refuses-without-marker",
5785
+ title: "`voltro data restore` refuses when it cannot write the in-progress marker",
5786
+ kind: "manual",
5787
+ reach: "beyond-source",
5788
+ appliesTo: ({ text: e }) => e.find("voltro data restore").length > 0,
5789
+ note: (/* @__PURE__ */ "`voltro data restore` now REFUSES when it cannot write the in-progress(marker, instead of restoring without one. A run that used to exit 0 can now(exit 1.((WHY((The marker is one row written before the first destructive statement, so a(restore that dies half-way refuses the next boot instead of serving a(half-loaded database. When it could not be written, the restore proceeded —(and then reported \"the next boot will REFUSE, by design\" over a database(with no marker at all. The next boot did not refuse.((WHICH RUNS CHANGE(( • the bookkeeping store will not open — wrong credentials, an unreachable( database, a missing env var, a server configuration the driver refuses;( • there is no `app.config.ts` from the working directory.((The second one surprises people: `restore` reads its TARGET from the(environment, so it looks like it needs no project. It does not, for the dump.(It needs one for the marker.((WHAT TO DO((Check every place you invoke it — cron entries, CI jobs, Makefiles, runbooks:(( grep -rn \"voltro data restore\" .((For each one, decide which it is. If the connection or the working directory(is wrong, fix that — the refusal names the reason on its own line. If the run(genuinely means to restore without the guard, say so:(( voltro data restore <dir> --no-marker((It warns every time and names why the marker was unavailable. Do not add it(everywhere as a migration step: it turns the refusal back off, which is the(behaviour this change removed.".split("(")).join("\n")
5790
+ }), Ei = T({
5791
+ version: "0.51.0",
5792
+ id: "0.51.0/03_restore-assets-returns-counts",
5793
+ title: "`restoreAssetsFromCas` returns `{ count, objects }`, not a number",
5794
+ kind: "manual",
5795
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getFullText().includes("restoreAssetsFromCas")),
5796
+ note: [
5797
+ "`restoreAssetsFromCas` (from `@voltro/data-transfer`) no longer returns a",
5798
+ "number:",
5799
+ "",
5800
+ " Effect<number, …> → Effect<{ count: number; objects: number }, …>",
5801
+ "",
5802
+ "WHY",
5803
+ "",
5804
+ "The number was a count of KEYS and was reported as \"blobs restored\". Several",
5805
+ "references legitimately name one key, and the content-addressed store keeps",
5806
+ "one body per distinct hash — so a run said \"57 blob(s) restored\" while 16",
5807
+ "objects appeared. Checking a restore against what is on disk found a gap that",
5808
+ "was not one.",
5809
+ "",
5810
+ "WHAT TO DO",
5811
+ "",
5812
+ "`tsc` points at every call site. Pick the number that answers your question:",
5813
+ "",
5814
+ " • `count` — distinct KEYS written to the destination. This is what the",
5815
+ " old return value was, so it is the migration that changes nothing.",
5816
+ " • `objects` — distinct sha256 BODIES read out of `assets/`. Below `count`",
5817
+ " when two keys hold identical content. Use it when you are comparing",
5818
+ " against files on a disk or a bucket listing.",
5819
+ "",
5820
+ "The same split is in the backup stamp (`references` / `count` / `objects`,",
5821
+ "with `totalBytes` and `objectBytes` beside them), so a report can state all",
5822
+ "three rather than implying one."
5823
+ ].join("\n")
5824
+ }), Di = "@voltro/i18n", Oi = "I18nProvider", ki = (e) => {
5825
+ let t = [];
5826
+ for (let n of e.getImportDeclarations()) {
5827
+ let e = n.getModuleSpecifierValue();
5828
+ if (e === Di || e === `${Di}/server`) for (let e of n.getNamedImports()) e.getName() === Oi && t.push(e.getAliasNode()?.getText() ?? e.getName());
5829
+ }
5830
+ return t;
5831
+ }, Ai = (e, t) => [...e.getDescendantsOfKind(S.JsxOpeningElement), ...e.getDescendantsOfKind(S.JsxSelfClosingElement)].filter((e) => t.includes(e.getTagNameNode().getText())), ji = [
5630
5832
  he,
5631
5833
  ge,
5632
5834
  ve,
@@ -5740,163 +5942,48 @@ var C = (e, t, n) => {
5740
5942
  pi,
5741
5943
  mi,
5742
5944
  hi,
5945
+ bi,
5946
+ xi,
5947
+ Si,
5948
+ Ci,
5949
+ wi,
5950
+ Ti,
5951
+ Ei,
5743
5952
  T({
5744
- version: "0.49.0",
5745
- id: "0.49.0/02_import-run-symbols",
5746
- title: "`ImportRun` / `describeImportRun` / `IMPORT_RUNS_TABLE` their `…TransferRun` names",
5953
+ version: "0.51.0",
5954
+ id: "0.51.0/04_i18n-timezone-prop",
5955
+ title: "`<I18nProvider>`: `timeZone` is its own prop, not an `intlConfig` member",
5747
5956
  kind: "transform",
5748
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
5749
- let t = e.getFullText();
5750
- return t.includes(J) ? !0 : gi.some((e) => t.includes(e)) ? _i.some(([e]) => t.includes(e)) : !1;
5751
- }),
5752
- transform: ({ project: e, helpers: t, log: n }) => {
5753
- let r = 0;
5754
- for (let n of gi) for (let [i, a] of _i) r += t.renameImport(e, {
5755
- module: n,
5756
- from: i,
5757
- to: a
5758
- });
5759
- let i = yi(e);
5760
- if (r + i === 0) return;
5761
- let a = [];
5762
- r > 0 && a.push(`${r} import(s) renamed to their \`…TransferRun\` names`), i > 0 && a.push(`${i} hand-spelled \`${J}\` → \`${vi}\``), n(`data-transfer run history: ${a.join("; ")}. An export writes to this record now, so the old names described half of what the table holds. The table itself carries a rename marker — \`voltro db apply\` (or a \`voltro dev\` boot) moves it with its rows on every dialect, so no data is lost and nothing is required of you there.`);
5957
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => ki(e).length > 0),
5958
+ transform: ({ project: e, log: t }) => {
5959
+ let n = 0, r = 0;
5960
+ for (let i of e.getSourceFiles()) {
5961
+ let e = ki(i);
5962
+ if (e.length !== 0) for (let a of Ai(i, e)) {
5963
+ let e = a.getAttribute("intlConfig");
5964
+ if (!e || !x.isJsxAttribute(e)) continue;
5965
+ let o = e.getInitializer();
5966
+ if (!o || !x.isJsxExpression(o)) continue;
5967
+ let s = o.getExpression();
5968
+ if (!s || !x.isObjectLiteralExpression(s)) {
5969
+ r++, t(` ? ${i.getBaseName()}:${a.getStartLineNumber()} intlConfig is not an object literal; check it by hand for a \`timeZone\``);
5970
+ continue;
5971
+ }
5972
+ let c = s.getProperty("timeZone");
5973
+ if (!c || !x.isPropertyAssignment(c)) continue;
5974
+ if (a.getAttribute("timeZone")) {
5975
+ c.remove(), n++;
5976
+ continue;
5977
+ }
5978
+ let l = c.getInitializer()?.getText() ?? "'UTC'";
5979
+ c.remove(), a.addAttribute({
5980
+ name: "timeZone",
5981
+ initializer: `{${l}}`
5982
+ }), s.getProperties().length === 0 && e.remove(), n++;
5983
+ }
5984
+ }
5985
+ n > 0 && (t(`I18nProvider: moved ${n} \`timeZone\` out of \`intlConfig\` onto its own prop`), t(" → it now reaches useFormatDate/useRelativeTime too, not only react-intl")), r > 0 && t(`I18nProvider: ${r} \`intlConfig\` value(s) could not be read — listed above`), n === 0 && r === 0 && (t("I18nProvider: no `intlConfig.timeZone` found — nothing to move"), t(" → if this app renders dates under SSR, set `timeZone` in the web app.config.ts"));
5763
5986
  }
5764
- }),
5765
- T({
5766
- version: "0.50.0",
5767
- id: "0.50.0/01_transfer-runs-endpoint",
5768
- title: "`GET /_voltro/admin/imports` → `GET /_voltro/admin/transfers`",
5769
- kind: "manual",
5770
- reach: "beyond-source",
5771
- appliesTo: ({ text: e }) => e.find("/_voltro/admin/imports").length > 0,
5772
- note: [
5773
- "The read-only endpoint that reports data-transfer runs has moved:",
5774
- "",
5775
- " GET /_voltro/admin/imports → GET /_voltro/admin/transfers",
5776
- "",
5777
- "Same secret, same query parameters, same response shape. Only the path changed.",
5778
- "",
5779
- "It answers for four directions now — import, export, native backup, native",
5780
- "restore — and it named one of them. The subcommand and the table were renamed",
5781
- "for that reason in 0.49.0; this path was left behind, which is worse than not",
5782
- "renaming any of them: a reader who followed the rename tries the matching path,",
5783
- "gets a 404, and concludes the instance is too old.",
5784
- "",
5785
- "`voltro data transfers --target api` uses the new path for you. What needs your",
5786
- "hand is anything that calls it directly — a monitoring check, an uptime probe,",
5787
- "a curl in a runbook, a dashboard datasource."
5788
- ].join("\n")
5789
- }),
5790
- T({
5791
- version: "0.50.0",
5792
- id: "0.50.0/02_reissue-data-imports-rename",
5793
- title: "RE-CHECK: `voltro data imports` → `voltro data transfers` (0.49.0's note could not see your scripts)",
5794
- kind: "manual",
5795
- reach: "beyond-source",
5796
- note: [
5797
- "This is a re-check of a rename that shipped in 0.49.0, not a new change.",
5798
- "",
5799
- " voltro data imports → voltro data transfers",
5800
- "",
5801
- "The 0.49.0 note that announced it only searched `.ts` / `.tsx` files — while",
5802
- "the command it is about lives in shell scripts, CI job definitions, Makefiles",
5803
- "and runbooks. So a project whose only occurrence was in, say, `.gitlab-ci.yml`",
5804
- "was told there was nothing to apply.",
5805
- "",
5806
- "A codemod runs once, on the jump that crosses its version, so that note cannot",
5807
- "be corrected for anyone already past 0.49.0. Hence this one. Search your",
5808
- "pipelines, deploy jobs and runbooks for `voltro data imports` now:",
5809
- "",
5810
- " grep -rn \"voltro data imports\" .",
5811
- "",
5812
- "The search `voltro update` runs is wider from 0.50.0 on (every text file git",
5813
- "tracks, not just source), but it still only reaches THIS repository — a step",
5814
- "defined in a CI runner's own UI, a CronJob spec in another repo, or a wiki",
5815
- "page is out of its reach by construction.",
5816
- "",
5817
- "Nothing else is required: the four renamed `@voltro/database` exports were",
5818
- "rewritten for you in 0.49.0, and the table carries a rename marker, so",
5819
- "`voltro db apply` moved `_voltro_data_imports` → `_voltro_data_transfers`",
5820
- "with its rows."
5821
- ].join("\n")
5822
- }),
5823
- T({
5824
- version: "0.50.0",
5825
- id: "0.50.0/03_interrupted-replace-tables-nullable",
5826
- title: "`InterruptedReplace.tables` is `number | null`",
5827
- kind: "manual",
5828
- appliesTo: ({ text: e }) => e.find("InterruptedReplace").length > 0,
5829
- note: [
5830
- "`InterruptedReplace.tables` is now `number | null`:",
5831
- "",
5832
- " readonly tables: number → readonly tables: number | null",
5833
- "",
5834
- "If you read it, `tsc` will point at the site. `null` means a native `voltro",
5835
- "data restore` wrote the marker: it replaces the whole database from an",
5836
- "artefact, so a table count is neither known up front nor meaningful after.",
5837
- "Read `kind` (a new field, `'replace'` | `'restore'`) when you need to tell",
5838
- "the two apart.",
5839
- "",
5840
- "The framework's own refusal text handles it by saying \"this database\" instead",
5841
- "of a count. If you render your own, that is the shape to copy — the one thing",
5842
- "not to do is coerce it, because `Number(null)` is `0` and the sentence then",
5843
- "reports a measurement that was never taken."
5844
- ].join("\n")
5845
- }),
5846
- T({
5847
- version: "0.51.0",
5848
- id: "0.51.0/01_computed-source-keeps-its-names",
5849
- title: "A COMPUTED `source:` can keep its typed names — do not widen the whole registry",
5850
- kind: "manual",
5851
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
5852
- let t = e.getFullText();
5853
- return /\bsource\s*:/.test(t) || t.includes("ReactivitySourceValue");
5854
- }),
5855
- note: [
5856
- "A follow-up to the 0.45.0 note about `source:` being typed against your",
5857
- "tables. Nothing changed in this release — this covers a case that note did",
5858
- "not, and it can only be sent once, so it is sent to everyone.",
5859
- "",
5860
- "THE CASE",
5861
- "",
5862
- "A GENERIC reader — the table is chosen by the caller out of a registry, or",
5863
- "arrives in `input` — is computed, and yet every name it can produce is known:",
5864
- "",
5865
- " source: Object.keys(JUNCTION_REGISTRY), // string[] — not assignable",
5866
- "",
5867
- "The 0.45.0 note offers two remedies and neither fits. There is no misspelled",
5868
- "name to fix. And widening — `ReactivitySourceValue`, or `as TableName[]` —",
5869
- "compiles at once while removing that entire registry from the check. It is",
5870
- "the easiest thing to reach for and it opts out wholesale, silently.",
5871
- "",
5872
- "WHAT TO DO INSTEAD",
5873
- "",
5874
- "Keep the literal’s keys instead of recovering them afterwards:",
5875
- "",
5876
- " const REGISTRY = { … } as const satisfies Readonly<Record<string, JunctionMeta>>",
5877
- "",
5878
- " // consumers that index with a plain string still get the wide type",
5879
- " export const JUNCTION_REGISTRY: Readonly<Record<string, JunctionMeta>> = REGISTRY",
5880
- "",
5881
- " // … and the source list keeps its literal names",
5882
- " export const JUNCTION_TABLE_NAMES = Object.keys(REGISTRY) as ReadonlyArray<keyof typeof REGISTRY>",
5883
- "",
5884
- "THE PART THAT IS EASY TO GET WRONG",
5885
- "",
5886
- "An ANNOTATION widens the keys back, even when the literal carries",
5887
- "`as const`. A registry written as",
5888
- "",
5889
- " export const R: Readonly<Record<string, Meta>> = { … } as const",
5890
- "",
5891
- "has `keyof typeof R === string`, and nothing about it looks wrong: the",
5892
- "annotation is checked against the literal and then replaces its type.",
5893
- "`satisfies` checks WITHOUT replacing. That is the whole difference, and it",
5894
- "is why the two-line split above exists rather than one declaration.",
5895
- "",
5896
- "`ReactivitySourceValue` stays correct for a source that genuinely is not a",
5897
- "literal — a name read from a config file, or one assembled at runtime. Use it",
5898
- "when there is no key set to keep, not when recovering one takes two lines."
5899
- ].join("\n")
5900
5987
  })
5901
5988
  ], Y = /* @__PURE__ */ new Set([
5902
5989
  "node_modules",
@@ -5910,12 +5997,12 @@ var C = (e, t, n) => {
5910
5997
  ".turbo",
5911
5998
  ".cache",
5912
5999
  ".output"
5913
- ]), xi = () => [...[...Y].map((e) => `**/${e}/**`), "**/.voltro-*/**"], X = /(?:from|import)\s*\(?\s*['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\)/g, Si = /\.(?:tsx?|jsx?|mjs|cjs)$/, Ci = (e) => g(e).replace(Si, ""), wi = (e) => {
6000
+ ]), Mi = () => [...[...Y].map((e) => `**/${e}/**`), "**/.voltro-*/**"], X = /(?:from|import)\s*\(?\s*['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\)/g, Ni = /\.(?:tsx?|jsx?|mjs|cjs)$/, Pi = (e) => g(e).replace(Ni, ""), Fi = (e) => {
5914
6001
  let t = e.split("/").filter((e) => e.length > 0).pop();
5915
6002
  if (t === void 0 || t === "." || t === "..") return;
5916
- let n = t.replace(Si, "");
6003
+ let n = t.replace(Ni, "");
5917
6004
  return n.length > 0 ? n : void 0;
5918
- }, Ti = (e) => {
6005
+ }, Ii = (e) => {
5919
6006
  let t = /* @__PURE__ */ new Map();
5920
6007
  for (let n of e) {
5921
6008
  let e;
@@ -5926,24 +6013,24 @@ var C = (e, t, n) => {
5926
6013
  }
5927
6014
  X.lastIndex = 0;
5928
6015
  for (let r = X.exec(e); r !== null; r = X.exec(e)) {
5929
- let e = wi(r[1] ?? r[2] ?? "");
6016
+ let e = Fi(r[1] ?? r[2] ?? "");
5930
6017
  if (e === void 0) continue;
5931
6018
  let i = t.get(e);
5932
6019
  i === void 0 ? t.set(e, /* @__PURE__ */ new Set([n])) : i.add(n);
5933
6020
  }
5934
6021
  }
5935
6022
  return t;
5936
- }, Ei = (e, t) => {
6023
+ }, Li = (e, t) => {
5937
6024
  let n = new Set(e);
5938
- for (let r of e) for (let e of [Ci(r), g(_(r))]) {
6025
+ for (let r of e) for (let e of [Pi(r), g(_(r))]) {
5939
6026
  let r = t.get(e);
5940
6027
  if (r !== void 0) for (let e of r) n.add(e);
5941
6028
  }
5942
6029
  return n;
5943
- }, Di = /* @__PURE__ */ ".ts,.tsx,.mts,.cts,.js,.jsx,.mjs,.cjs,.yml,.yaml,.toml,.json,.jsonc,.ini,.cfg,.conf,.sh,.bash,.zsh,.fish,.ps1,.bat,.cmd,.md,.mdx,.rst,.txt,.adoc,Makefile,makefile,GNUmakefile,Justfile,justfile,Dockerfile,Containerfile,Procfile,Jenkinsfile,Rakefile,Taskfile".split(","), Oi = 2097152, ki = 4e4, Ai = (e) => {
6030
+ }, Ri = /* @__PURE__ */ ".ts,.tsx,.mts,.cts,.js,.jsx,.mjs,.cjs,.yml,.yaml,.toml,.json,.jsonc,.ini,.cfg,.conf,.sh,.bash,.zsh,.fish,.ps1,.bat,.cmd,.md,.mdx,.rst,.txt,.adoc,Makefile,makefile,GNUmakefile,Justfile,justfile,Dockerfile,Containerfile,Procfile,Jenkinsfile,Rakefile,Taskfile".split(","), zi = 2097152, Bi = 4e4, Vi = (e) => {
5944
6031
  let t = e.slice(e.lastIndexOf("/") + 1);
5945
- return Di.some((e) => e.startsWith(".") ? t.endsWith(e) : t === e);
5946
- }, ji = (e) => {
6032
+ return Ri.some((e) => e.startsWith(".") ? t.endsWith(e) : t === e);
6033
+ }, Hi = (e) => {
5947
6034
  try {
5948
6035
  let t = ne("git", [
5949
6036
  "ls-files",
@@ -5964,19 +6051,19 @@ var C = (e, t, n) => {
5964
6051
  } catch {
5965
6052
  return;
5966
6053
  }
5967
- }, Z = (e, t = ji) => {
6054
+ }, Z = (e, t = Hi) => {
5968
6055
  let n = t(e);
5969
- if (n === void 0) return Mi;
6056
+ if (n === void 0) return Ui;
5970
6057
  let r = /* @__PURE__ */ new Map(), i = [];
5971
6058
  for (let t of n) {
5972
- if (t.split("/").some((e) => Y.has(e)) || !Ai(t)) continue;
5973
- if (r.size >= ki) {
6059
+ if (t.split("/").some((e) => Y.has(e)) || !Vi(t)) continue;
6060
+ if (r.size >= Bi) {
5974
6061
  i.push(t);
5975
6062
  continue;
5976
6063
  }
5977
6064
  let n = v(e, t);
5978
6065
  try {
5979
- if (te(n).size > Oi) {
6066
+ if (te(n).size > zi) {
5980
6067
  i.push(t);
5981
6068
  continue;
5982
6069
  }
@@ -6000,45 +6087,45 @@ var C = (e, t, n) => {
6000
6087
  scanned: r.size,
6001
6088
  skipped: i
6002
6089
  };
6003
- }, Mi = {
6090
+ }, Ui = {
6004
6091
  find: () => [],
6005
6092
  scanned: 0,
6006
6093
  skipped: []
6007
- }, Ni = [
6094
+ }, Wi = [
6008
6095
  "**/*.ts",
6009
6096
  "**/*.tsx",
6010
6097
  "!**/node_modules/**",
6011
6098
  "!**/dist/**",
6012
6099
  "!**/.framework/**",
6013
6100
  "!**/*.d.ts"
6014
- ], Q = (e, t, n = Ii) => {
6101
+ ], Q = (e, t, n = qi) => {
6015
6102
  let r = /* @__PURE__ */ new Set();
6016
- for (let e of t) for (let t of e.scope ?? Ni) r.add(t);
6103
+ for (let e of t) for (let t of e.scope ?? Wi) r.add(t);
6017
6104
  let i = n(e);
6018
6105
  if (i !== void 0) {
6019
- let t = [...r].filter((e) => !e.startsWith("!")).map((e) => Fi(e)), n = [...r].filter((e) => e.startsWith("!")).map((e) => Fi(e.slice(1))), a = [...Y], o = [];
6106
+ let t = [...r].filter((e) => !e.startsWith("!")).map((e) => Ki(e)), n = [...r].filter((e) => e.startsWith("!")).map((e) => Ki(e.slice(1))), a = [...Y], o = [];
6020
6107
  for (let r of i) {
6021
6108
  if (a.some((e) => r === e || r.startsWith(`${e}/`) || r.includes(`/${e}/`)) || !t.some((e) => e(r)) || n.some((e) => e(r))) continue;
6022
6109
  let i = v(e, r);
6023
6110
  y(i) && o.push(i);
6024
6111
  }
6025
- return Pi(o.length, e), o;
6112
+ return Gi(o.length, e), o;
6026
6113
  }
6027
6114
  let a = ae([...r], {
6028
6115
  cwd: e,
6029
6116
  absolute: !0,
6030
6117
  expandDirectories: !1,
6031
6118
  followSymbolicLinks: !1,
6032
- ignore: [...xi()]
6119
+ ignore: [...Mi()]
6033
6120
  });
6034
- return Pi(a.length, e), a;
6035
- }, Pi = (e, t) => {
6036
- let n = Li();
6121
+ return Gi(a.length, e), a;
6122
+ }, Gi = (e, t) => {
6123
+ let n = Ji();
6037
6124
  if (!(e <= n)) throw Error(`voltro update: the codemod scan matched ${e} files under ${t}, past the ${n} ceiling.\n Every matched file is parsed into one ts-morph project, so the heap needed grows with\n this number — past the ceiling it runs out of memory, and V8 aborts without naming a\n limit, which is why this stops here instead. Three ways out, in order:\n • put non-source directories in .gitignore (inside a git repo the scan then never\n sees them at all — a build cache, a data dump, a virtualenv),\n • run \`voltro update\` from the app directory rather than the workspace root, or\n • take one codemod at a time: \`voltro update --dry-run\` lists the ids, then\n \`voltro update --codemods-only --only <id>\` runs a single one.\n If every one of those files really is source this pass must rewrite, raise the ceiling\n with VOLTRO_CODEMOD_MAX_FILES and give node room: NODE_OPTIONS=--max-old-space-size=8192.`);
6038
- }, Fi = (e) => {
6125
+ }, Ki = (e) => {
6039
6126
  let t = e.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "\0").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\u0000/g, "(?:.*/)?").replace(/\\\{([^}]*)\\\}/g, (e, t) => `(?:${t.split(",").join("|")})`);
6040
6127
  return (e) => RegExp(`^${t}$`).test(e);
6041
- }, Ii = (e) => {
6128
+ }, qi = (e) => {
6042
6129
  try {
6043
6130
  let t = ne("git", [
6044
6131
  "ls-files",
@@ -6059,7 +6146,7 @@ var C = (e, t, n) => {
6059
6146
  } catch {
6060
6147
  return;
6061
6148
  }
6062
- }, Li = () => {
6149
+ }, Ji = () => {
6063
6150
  let e = Number(process.env.VOLTRO_CODEMOD_MAX_FILES);
6064
6151
  return Number.isInteger(e) && e > 0 ? e : 2e4;
6065
6152
  }, $ = (e, t) => {
@@ -6069,7 +6156,7 @@ var C = (e, t, n) => {
6069
6156
  if (t !== 0) return t;
6070
6157
  }
6071
6158
  return 0;
6072
- }, Ri = (e, t, n = bi) => n.filter((n) => $(e, n.version) < 0 && $(n.version, t) <= 0).slice().sort(me), zi = (e, t, n = () => {}, r = Mi) => {
6159
+ }, Yi = (e, t, n = ji) => n.filter((n) => $(e, n.version) < 0 && $(n.version, t) <= 0).slice().sort(me), Xi = (e, t, n = () => {}, r = Ui) => {
6073
6160
  let i = [], a = [], o = [];
6074
6161
  for (let s of t) {
6075
6162
  let t = !s.appliesTo || s.appliesTo({
@@ -6120,8 +6207,8 @@ var C = (e, t, n) => {
6120
6207
  manual: a,
6121
6208
  skipped: o
6122
6209
  };
6123
- }, Bi = async (e) => {
6124
- let t = e.log ?? (() => {}), n = Ri(e.from, e.to, e.codemods ?? bi), r = e.only === void 0 || e.only.length === 0 ? n : (() => {
6210
+ }, Zi = async (e) => {
6211
+ let t = e.log ?? (() => {}), n = Yi(e.from, e.to, e.codemods ?? ji), r = e.only === void 0 || e.only.length === 0 ? n : (() => {
6125
6212
  let t = n.filter((t) => e.only.includes(t.id)), r = e.only.filter((e) => !n.some((t) => t.id === e));
6126
6213
  if (r.length > 0) throw Error(`voltro update --only: no codemod in ${e.from} → ${e.to} has id ${r.map((e) => `"${e}"`).join(", ")}.\n Available for this jump:\n${n.map((e) => ` ${e.id}`).join("\n")}`);
6127
6214
  return t;
@@ -6148,15 +6235,15 @@ var C = (e, t, n) => {
6148
6235
  get skipped() {
6149
6236
  return (p ??= Z(e.root)).skipped;
6150
6237
  }
6151
- }, h = c.length > 0 ? Ti(Q(e.root, [{
6238
+ }, h = c.length > 0 ? Ii(Q(e.root, [{
6152
6239
  ...c[0],
6153
- scope: Ni
6240
+ scope: Wi
6154
6241
  }])) : void 0;
6155
6242
  for (let n of r) {
6156
6243
  if (c.includes(n)) {
6157
6244
  let r = a(), i = Q(e.root, [n]);
6158
- for (let e of Ei(i, h)) y(e) && r.addSourceFileAtPath(e);
6159
- let s = zi(r, [n], t, m);
6245
+ for (let e of Li(i, h)) y(e) && r.addSourceFileAtPath(e);
6246
+ let s = Xi(r, [n], t, m);
6160
6247
  e.dryRun || await r.save(), l.push(...s.applied), u.push(...s.manual), d.push(...s.skipped), o = void 0;
6161
6248
  continue;
6162
6249
  }
@@ -6165,7 +6252,7 @@ var C = (e, t, n) => {
6165
6252
  for (let t of s) y(t) && e.addSourceFileAtPath(t);
6166
6253
  return e;
6167
6254
  })();
6168
- let r = zi(o, [n], t, m);
6255
+ let r = Xi(o, [n], t, m);
6169
6256
  e.dryRun || await o.save(), l.push(...r.applied), u.push(...r.manual), d.push(...r.skipped);
6170
6257
  }
6171
6258
  return {
@@ -6175,4 +6262,4 @@ var C = (e, t, n) => {
6175
6262
  };
6176
6263
  };
6177
6264
  //#endregion
6178
- export { T as i, Bi as n, Y as r, $ as t };
6265
+ export { T as i, Zi as n, Y as r, $ as t };