@voltro/cli 0.50.0 → 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 (111) hide show
  1. package/CHANGELOG.md +160 -0
  2. package/dist/{apiBuild-CFYXo06U.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-CnSH-zru.js → build-DETLZAFt.js} +271 -240
  8. package/dist/{checkCommand-OL41BMIb.js → checkCommand-CWcnDArJ.js} +1 -1
  9. package/dist/{checkCommand-Do3SQbLx.js → checkCommand-DNuPiWMc.js} +56 -46
  10. package/dist/{codegenCommand-BJzaRz6q.js → codegenCommand-BNBHcNNj.js} +2 -2
  11. package/dist/{codemodRunner-De3k0pRd.js → codemodRunner-BDVixlSw.js} +278 -136
  12. package/dist/{commands-By23Ljny.js → commands-B1OiS9bX.js} +24 -23
  13. package/dist/{dashboardCommand-DN1rAABE.js → dashboardCommand-C-vvPY1B.js} +5 -5
  14. package/dist/{dataCommand-B93TUBIx.js → dataCommand-C1GxXW5q.js} +458 -384
  15. package/dist/dbCommand-C27HIsGE.js +2 -0
  16. package/dist/{dbCommand-Bf7Cy0mn.js → dbCommand-If4Y1xQ-.js} +1 -1
  17. package/dist/{dev-BWwE0QjK.js → dev-CK522MV5.js} +1 -1
  18. package/dist/{dev-1yu42ubu.js → dev-kdAg9Q7l.js} +1246 -1246
  19. package/dist/doctorCommand-BK4l18eG.js +2 -0
  20. package/dist/{doctorCommand-y-EbP8st.js → doctorCommand-nKmeW78u.js} +42 -26
  21. package/dist/{doctorSourceRelations-Dd48LPvO.js → doctorSourceRelations-c5mlN5HI.js} +66 -34
  22. package/dist/{dormancyCommand-Dc7KRNCG.js → dormancyCommand-CY3wa_SW.js} +1 -1
  23. package/dist/{embeddingsCommand-kPs132bB.js → embeddingsCommand-BDLIgje_.js} +1 -1
  24. package/dist/{envCommand-E12zSt51.js → envCommand-C6V_xVlT.js} +13 -13
  25. package/dist/{evolveCommand-B3nagPVc.js → evolveCommand-D3c4DSfN.js} +2 -2
  26. package/dist/{generateCommand-CcyvH2ve.js → generateCommand-DTNPGjxo.js} +1 -1
  27. package/dist/index.js +1 -1
  28. package/dist/{infoCommand-Cq0Yz98u.js → infoCommand-BnRFEF1o.js} +1 -1
  29. package/dist/{inspect-B7U7Cl_Z.js → inspect-CtL_xTbu.js} +1 -0
  30. package/dist/inspect-CuGDYES0.js +2 -0
  31. package/dist/manifestBuild-CPjhvM62.js +2 -0
  32. package/dist/{manifestBuild-BK42hu0k.js → manifestBuild-CuU1VrSm.js} +2 -2
  33. package/dist/{migrate-D3MK9BpK.js → migrate-DehuBakM.js} +1 -1
  34. package/dist/{runtimeTrace-BpLUcrfy.js → runtimeTrace-ZsBU7Tkx.js} +1 -1
  35. package/dist/{sdkgen-dl4-WKF6.js → sdkgen-O4XqWOjM.js} +1 -1
  36. package/dist/serveCommand-BRnPCxVd.js +2 -0
  37. package/dist/{serveCommand-8CTzQCY0.js → serveCommand-DdiYNBBu.js} +4 -4
  38. package/dist/serveEntry.js +1 -1
  39. package/dist/{start-7HcSIKEp.js → start-BLNmWkLa.js} +331 -327
  40. package/dist/{start-DvlYDyAr.js → start-Dzicuyw8.js} +1 -1
  41. package/dist/startEntry.js +1 -1
  42. package/dist/{updateCommand-i8JbrLAA.js → updateCommand-Bs322Q78.js} +1 -1
  43. package/dist/updateCommand-eXB35SEv.js +2 -0
  44. package/dist/{webDev-B4IEMB0n.js → webDev-B-ubQEMX.js} +283 -224
  45. package/dist/webDev-DposiF3j.js +2 -0
  46. package/dist/{webhooksCommand-B0bdyuQ4.js → webhooksCommand-FLYY9IXh.js} +1 -1
  47. package/package.json +41 -17
  48. package/templates/AGENTS.core.md +15 -5
  49. package/templates/AGENTS.md +16 -6
  50. package/templates/agent-docs/_index.md +1 -1
  51. package/templates/agent-docs/cli.md +92 -3
  52. package/templates/agent-docs/data.md +36 -0
  53. package/templates/agent-docs/internationalization.md +131 -13
  54. package/templates/agent-docs/whats-new.md +73 -27
  55. package/templates/apps/api-ai/package.json +7 -7
  56. package/templates/apps/api-auth/package.json +8 -8
  57. package/templates/apps/api-backend/package.json +7 -7
  58. package/templates/apps/api-backend-deactivation/package.json +7 -7
  59. package/templates/apps/api-backend-mail/package.json +8 -8
  60. package/templates/apps/api-backend-mariadb/package.json +9 -9
  61. package/templates/apps/api-backend-sqlite/package.json +8 -8
  62. package/templates/apps/api-backend-storage/package.json +8 -8
  63. package/templates/apps/api-cms/package.json +10 -10
  64. package/templates/apps/api-collab/package.json +8 -8
  65. package/templates/apps/api-data-advanced/package.json +8 -8
  66. package/templates/apps/api-durable/package.json +8 -8
  67. package/templates/apps/api-feature-flags/package.json +9 -9
  68. package/templates/apps/api-governance/package.json +8 -8
  69. package/templates/apps/api-kv/package.json +8 -8
  70. package/templates/apps/api-moderation/package.json +8 -8
  71. package/templates/apps/api-observability/package.json +8 -8
  72. package/templates/apps/api-ratelimit/package.json +8 -8
  73. package/templates/apps/api-rbac/package.json +8 -8
  74. package/templates/apps/api-rest/package.json +7 -7
  75. package/templates/apps/api-saas/package.json +11 -11
  76. package/templates/apps/api-saas-starter/package.json +10 -10
  77. package/templates/apps/api-search/package.json +8 -8
  78. package/templates/apps/api-status/package.json +8 -8
  79. package/templates/apps/api-versioning/package.json +8 -8
  80. package/templates/apps/api-webhooks/package.json +9 -9
  81. package/templates/apps/changelog/package.json +6 -6
  82. package/templates/apps/edge-functions/package.json +2 -2
  83. package/templates/apps/frontend-admin/package.json +8 -8
  84. package/templates/apps/frontend-app/package.json +9 -9
  85. package/templates/apps/frontend-auth/package.json +8 -8
  86. package/templates/apps/frontend-blank/package.json +7 -7
  87. package/templates/apps/frontend-cms/package.json +9 -9
  88. package/templates/apps/frontend-collab/package.json +10 -10
  89. package/templates/apps/frontend-contact/package.json +7 -7
  90. package/templates/apps/frontend-dashboard/package.json +7 -7
  91. package/templates/apps/frontend-docs/package.json +7 -7
  92. package/templates/apps/frontend-i18n/package.json +6 -6
  93. package/templates/apps/frontend-landing/package.json +7 -7
  94. package/templates/apps/frontend-portal/package.json +8 -8
  95. package/templates/apps/frontend-saas/package.json +8 -8
  96. package/templates/apps/frontend-spa/package.json +7 -7
  97. package/templates/apps/frontend-ssr/package.json +7 -7
  98. package/templates/apps/frontend-ssr-api/package.json +8 -8
  99. package/templates/apps/frontend-static-blog/package.json +6 -6
  100. package/templates/apps/frontend-status/package.json +8 -8
  101. package/templates/apps/mobile-app/package.json +4 -4
  102. package/dist/apiBuild-BWo8KI6n.js +0 -2
  103. package/dist/appGraph-KGDPTuTy.js +0 -110
  104. package/dist/appGraph-zuMGKVYX.js +0 -2
  105. package/dist/dbCommand-Bnl7MSGC.js +0 -2
  106. package/dist/doctorCommand-BmovAwb7.js +0 -2
  107. package/dist/inspect-DUze25t0.js +0 -2
  108. package/dist/manifestBuild-j0n109tt.js +0 -2
  109. package/dist/serveCommand-C_WI4TTG.js +0 -2
  110. package/dist/updateCommand-D91-TZ7E.js +0 -2
  111. package/dist/webDev-B79ji8M5.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,108 +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
5987
  })
5846
5988
  ], Y = /* @__PURE__ */ new Set([
5847
5989
  "node_modules",
@@ -5855,12 +5997,12 @@ var C = (e, t, n) => {
5855
5997
  ".turbo",
5856
5998
  ".cache",
5857
5999
  ".output"
5858
- ]), 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) => {
5859
6001
  let t = e.split("/").filter((e) => e.length > 0).pop();
5860
6002
  if (t === void 0 || t === "." || t === "..") return;
5861
- let n = t.replace(Si, "");
6003
+ let n = t.replace(Ni, "");
5862
6004
  return n.length > 0 ? n : void 0;
5863
- }, Ti = (e) => {
6005
+ }, Ii = (e) => {
5864
6006
  let t = /* @__PURE__ */ new Map();
5865
6007
  for (let n of e) {
5866
6008
  let e;
@@ -5871,24 +6013,24 @@ var C = (e, t, n) => {
5871
6013
  }
5872
6014
  X.lastIndex = 0;
5873
6015
  for (let r = X.exec(e); r !== null; r = X.exec(e)) {
5874
- let e = wi(r[1] ?? r[2] ?? "");
6016
+ let e = Fi(r[1] ?? r[2] ?? "");
5875
6017
  if (e === void 0) continue;
5876
6018
  let i = t.get(e);
5877
6019
  i === void 0 ? t.set(e, /* @__PURE__ */ new Set([n])) : i.add(n);
5878
6020
  }
5879
6021
  }
5880
6022
  return t;
5881
- }, Ei = (e, t) => {
6023
+ }, Li = (e, t) => {
5882
6024
  let n = new Set(e);
5883
- 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))]) {
5884
6026
  let r = t.get(e);
5885
6027
  if (r !== void 0) for (let e of r) n.add(e);
5886
6028
  }
5887
6029
  return n;
5888
- }, 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) => {
5889
6031
  let t = e.slice(e.lastIndexOf("/") + 1);
5890
- return Di.some((e) => e.startsWith(".") ? t.endsWith(e) : t === e);
5891
- }, ji = (e) => {
6032
+ return Ri.some((e) => e.startsWith(".") ? t.endsWith(e) : t === e);
6033
+ }, Hi = (e) => {
5892
6034
  try {
5893
6035
  let t = ne("git", [
5894
6036
  "ls-files",
@@ -5909,19 +6051,19 @@ var C = (e, t, n) => {
5909
6051
  } catch {
5910
6052
  return;
5911
6053
  }
5912
- }, Z = (e, t = ji) => {
6054
+ }, Z = (e, t = Hi) => {
5913
6055
  let n = t(e);
5914
- if (n === void 0) return Mi;
6056
+ if (n === void 0) return Ui;
5915
6057
  let r = /* @__PURE__ */ new Map(), i = [];
5916
6058
  for (let t of n) {
5917
- if (t.split("/").some((e) => Y.has(e)) || !Ai(t)) continue;
5918
- if (r.size >= ki) {
6059
+ if (t.split("/").some((e) => Y.has(e)) || !Vi(t)) continue;
6060
+ if (r.size >= Bi) {
5919
6061
  i.push(t);
5920
6062
  continue;
5921
6063
  }
5922
6064
  let n = v(e, t);
5923
6065
  try {
5924
- if (te(n).size > Oi) {
6066
+ if (te(n).size > zi) {
5925
6067
  i.push(t);
5926
6068
  continue;
5927
6069
  }
@@ -5945,45 +6087,45 @@ var C = (e, t, n) => {
5945
6087
  scanned: r.size,
5946
6088
  skipped: i
5947
6089
  };
5948
- }, Mi = {
6090
+ }, Ui = {
5949
6091
  find: () => [],
5950
6092
  scanned: 0,
5951
6093
  skipped: []
5952
- }, Ni = [
6094
+ }, Wi = [
5953
6095
  "**/*.ts",
5954
6096
  "**/*.tsx",
5955
6097
  "!**/node_modules/**",
5956
6098
  "!**/dist/**",
5957
6099
  "!**/.framework/**",
5958
6100
  "!**/*.d.ts"
5959
- ], Q = (e, t, n = Ii) => {
6101
+ ], Q = (e, t, n = qi) => {
5960
6102
  let r = /* @__PURE__ */ new Set();
5961
- 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);
5962
6104
  let i = n(e);
5963
6105
  if (i !== void 0) {
5964
- 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 = [];
5965
6107
  for (let r of i) {
5966
6108
  if (a.some((e) => r === e || r.startsWith(`${e}/`) || r.includes(`/${e}/`)) || !t.some((e) => e(r)) || n.some((e) => e(r))) continue;
5967
6109
  let i = v(e, r);
5968
6110
  y(i) && o.push(i);
5969
6111
  }
5970
- return Pi(o.length, e), o;
6112
+ return Gi(o.length, e), o;
5971
6113
  }
5972
6114
  let a = ae([...r], {
5973
6115
  cwd: e,
5974
6116
  absolute: !0,
5975
6117
  expandDirectories: !1,
5976
6118
  followSymbolicLinks: !1,
5977
- ignore: [...xi()]
6119
+ ignore: [...Mi()]
5978
6120
  });
5979
- return Pi(a.length, e), a;
5980
- }, Pi = (e, t) => {
5981
- let n = Li();
6121
+ return Gi(a.length, e), a;
6122
+ }, Gi = (e, t) => {
6123
+ let n = Ji();
5982
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.`);
5983
- }, Fi = (e) => {
6125
+ }, Ki = (e) => {
5984
6126
  let t = e.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "\0").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\u0000/g, "(?:.*/)?").replace(/\\\{([^}]*)\\\}/g, (e, t) => `(?:${t.split(",").join("|")})`);
5985
6127
  return (e) => RegExp(`^${t}$`).test(e);
5986
- }, Ii = (e) => {
6128
+ }, qi = (e) => {
5987
6129
  try {
5988
6130
  let t = ne("git", [
5989
6131
  "ls-files",
@@ -6004,7 +6146,7 @@ var C = (e, t, n) => {
6004
6146
  } catch {
6005
6147
  return;
6006
6148
  }
6007
- }, Li = () => {
6149
+ }, Ji = () => {
6008
6150
  let e = Number(process.env.VOLTRO_CODEMOD_MAX_FILES);
6009
6151
  return Number.isInteger(e) && e > 0 ? e : 2e4;
6010
6152
  }, $ = (e, t) => {
@@ -6014,7 +6156,7 @@ var C = (e, t, n) => {
6014
6156
  if (t !== 0) return t;
6015
6157
  }
6016
6158
  return 0;
6017
- }, 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) => {
6018
6160
  let i = [], a = [], o = [];
6019
6161
  for (let s of t) {
6020
6162
  let t = !s.appliesTo || s.appliesTo({
@@ -6065,8 +6207,8 @@ var C = (e, t, n) => {
6065
6207
  manual: a,
6066
6208
  skipped: o
6067
6209
  };
6068
- }, Bi = async (e) => {
6069
- 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 : (() => {
6070
6212
  let t = n.filter((t) => e.only.includes(t.id)), r = e.only.filter((e) => !n.some((t) => t.id === e));
6071
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")}`);
6072
6214
  return t;
@@ -6093,15 +6235,15 @@ var C = (e, t, n) => {
6093
6235
  get skipped() {
6094
6236
  return (p ??= Z(e.root)).skipped;
6095
6237
  }
6096
- }, h = c.length > 0 ? Ti(Q(e.root, [{
6238
+ }, h = c.length > 0 ? Ii(Q(e.root, [{
6097
6239
  ...c[0],
6098
- scope: Ni
6240
+ scope: Wi
6099
6241
  }])) : void 0;
6100
6242
  for (let n of r) {
6101
6243
  if (c.includes(n)) {
6102
6244
  let r = a(), i = Q(e.root, [n]);
6103
- for (let e of Ei(i, h)) y(e) && r.addSourceFileAtPath(e);
6104
- 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);
6105
6247
  e.dryRun || await r.save(), l.push(...s.applied), u.push(...s.manual), d.push(...s.skipped), o = void 0;
6106
6248
  continue;
6107
6249
  }
@@ -6110,7 +6252,7 @@ var C = (e, t, n) => {
6110
6252
  for (let t of s) y(t) && e.addSourceFileAtPath(t);
6111
6253
  return e;
6112
6254
  })();
6113
- let r = zi(o, [n], t, m);
6255
+ let r = Xi(o, [n], t, m);
6114
6256
  e.dryRun || await o.save(), l.push(...r.applied), u.push(...r.manual), d.push(...r.skipped);
6115
6257
  }
6116
6258
  return {
@@ -6120,4 +6262,4 @@ var C = (e, t, n) => {
6120
6262
  };
6121
6263
  };
6122
6264
  //#endregion
6123
- 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 };