@pagopa/dx-cli 0.11.2 → 0.14.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 (3) hide show
  1. package/README.md +30 -6
  2. package/bin/index.js +229 -51
  3. package/package.json +5 -3
package/README.md CHANGED
@@ -112,19 +112,43 @@ This command helps you:
112
112
 
113
113
  #### `init`
114
114
 
115
- Initialize resources such as new projects with standardized structure.
115
+ Bootstrap a new monorepo project with standardized structure and remote repository provisioning.
116
116
 
117
117
  ```bash
118
- # Initialize a new monorepo project
119
118
  dx init project
120
119
  ```
121
120
 
122
121
  This command will:
123
122
 
124
- - Guide you through creating a new monorepo project
125
- - Set up the project structure according to PagoPA's DevEx guidelines
126
- - Configure necessary tools and dependencies
127
- - Bootstrap the project with best practices
123
+ - Check that required tools (e.g., Terraform CLI) are installed
124
+ - Interactively prompt for project metadata (cloud provider, region, environments, cost center, etc.)
125
+ - Generate a monorepo structure following PagoPA DevEx guidelines
126
+ - Create a remote GitHub repository using Terraform
127
+ - Push the initial codebase to the newly created repository
128
+
129
+ **Example usage:**
130
+
131
+ ```bash
132
+ $ dx init project
133
+ ? What is the repository name? my-monorepo
134
+ ? What is the GitHub repository owner? pagopa
135
+ ? What is the repository description? My new PagoPA monorepo
136
+ ...
137
+
138
+ ✔ Terraform CLI is installed!
139
+ ✔ Workspace files created successfully!
140
+ ✔ GitHub repository created successfully!
141
+ ✔ Code pushed to GitHub successfully!
142
+
143
+ Workspace created successfully!
144
+ - Name: my-monorepo
145
+ - Cloud Service Provider: azure
146
+ - CSP location: italynorth
147
+ - GitHub Repository: https://github.com/pagopa/my-monorepo
148
+ ```
149
+
150
+ > [!NOTE]
151
+ > The command will fail early if required tools are missing.
128
152
 
129
153
  #### `info`
130
154
 
package/bin/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  // src/index.ts
4
4
  import "core-js/actual/set/index.js";
5
5
  import { configure, getConsoleSink } from "@logtape/logtape";
6
+ import { Octokit as Octokit2 } from "octokit";
6
7
 
7
8
  // src/adapters/codemods/registry.ts
8
9
  import { okAsync } from "neverthrow";
@@ -31,8 +32,8 @@ import * as YAML2 from "yaml";
31
32
  import { getLogger } from "@logtape/logtape";
32
33
  import { Octokit } from "octokit";
33
34
  var getLatestCommitSha = async (owner, repo, ref = "main") => {
34
- const octokit = new Octokit();
35
- const response = await octokit.rest.repos.getCommit({
35
+ const octokit2 = new Octokit();
36
+ const response = await octokit2.rest.repos.getCommit({
36
37
  owner,
37
38
  ref,
38
39
  repo
@@ -52,8 +53,8 @@ var getLatestCommitShaOrRef = async (owner, repo, ref = "main") => {
52
53
 
53
54
  // src/adapters/codemods/yaml.ts
54
55
  import * as YAML from "yaml";
55
- var isChildOf = (path2, key) => {
56
- const ancestor = path2.at(-1);
56
+ var isChildOf = (path3, key) => {
57
+ const ancestor = path3.at(-1);
57
58
  return YAML.isPair(ancestor) && YAML.isScalar(ancestor.key) && typeof ancestor.key.value === "string" && ancestor.key.value === key;
58
59
  };
59
60
 
@@ -63,8 +64,8 @@ var updateJSCodeReviewJob = (sha) => (workflow, filename) => {
63
64
  const document = YAML2.parseDocument(workflow);
64
65
  let updated = false;
65
66
  YAML2.visit(document, {
66
- Map(_, map, path2) {
67
- if (map.has("jobs") || isChildOf(path2, "jobs")) {
67
+ Map(_, map, path3) {
68
+ if (map.has("jobs") || isChildOf(path3, "jobs")) {
68
69
  return void 0;
69
70
  }
70
71
  if (map.has("uses")) {
@@ -127,8 +128,8 @@ var migrateWorkflow = (sha) => (workflow, filename) => {
127
128
  const document = YAML3.parseDocument(workflow);
128
129
  let updated = false;
129
130
  YAML3.visit(document, {
130
- Map(_, map, path2) {
131
- if (isChildOf(path2, "jobs") || isChildOf(path2, "with")) {
131
+ Map(_, map, path3) {
132
+ if (isChildOf(path3, "jobs") || isChildOf(path3, "with")) {
132
133
  return void 0;
133
134
  }
134
135
  if (map.has("jobs")) {
@@ -740,8 +741,29 @@ var makeInfoCommand = (dependencies) => new Command3().name("info").description(
740
741
  });
741
742
 
742
743
  // src/adapters/commander/commands/init.ts
743
- import scaffoldMonorepo from "@pagopa/monorepo-generator";
744
+ import loadMonorepoScaffolder, {
745
+ answersSchema,
746
+ PLOP_MONOREPO_GENERATOR_NAME
747
+ } from "@pagopa/monorepo-generator";
748
+ import chalk from "chalk";
744
749
  import { Command as Command4 } from "commander";
750
+ import { $ as $3 } from "execa";
751
+ import { okAsync as okAsync2, ResultAsync as ResultAsync6 } from "neverthrow";
752
+ import * as path from "path";
753
+ import { oraPromise } from "ora";
754
+
755
+ // src/adapters/execa/terraform.ts
756
+ import { $ as $2 } from "execa";
757
+ var tf$ = $2({
758
+ environment: {
759
+ NO_COLOR: "1",
760
+ TF_IN_AUTOMATION: "1",
761
+ TF_INPUT: "0"
762
+ },
763
+ shell: true
764
+ });
765
+
766
+ // src/adapters/plop/index.ts
745
767
  import { Result, ResultAsync as ResultAsync4 } from "neverthrow";
746
768
  import nodePlop from "node-plop";
747
769
  var initPlop = () => ResultAsync4.fromPromise(
@@ -752,24 +774,180 @@ var getGenerator = (plopAPI) => Result.fromThrowable(
752
774
  plopAPI.getGenerator,
753
775
  () => new Error("Generator not found")
754
776
  );
755
- var runGenerator = (generator) => ResultAsync4.fromPromise(
777
+ var getPrompts = (generator) => ResultAsync4.fromPromise(
756
778
  generator.runPrompts(),
757
- () => new Error("Failed to run the generator prompts")
758
- ).andThen(
759
- (answers) => ResultAsync4.fromPromise(
760
- generator.runActions(answers),
761
- () => new Error("Failed to run the generator actions")
762
- )
779
+ (cause) => new Error("Failed to run the generator prompts", { cause })
780
+ );
781
+
782
+ // src/adapters/zod/index.ts
783
+ import { ResultAsync as ResultAsync5 } from "neverthrow";
784
+ var decode = (schema) => (data) => ResultAsync5.fromPromise(
785
+ schema.parseAsync(data),
786
+ (cause) => new Error("Input is not valid for the given schema", { cause })
787
+ );
788
+
789
+ // src/adapters/commander/commands/init.ts
790
+ var Repository = class {
791
+ constructor(name, owner) {
792
+ this.name = name;
793
+ this.owner = owner;
794
+ }
795
+ get ssh() {
796
+ return `git@github.com:${this.owner}/${this.name}.git`;
797
+ }
798
+ get url() {
799
+ return `https://github.com/${this.owner}/${this.name}`;
800
+ }
801
+ };
802
+ var withSpinner = (text, successText, failText, promise) => ResultAsync6.fromPromise(
803
+ oraPromise(promise, {
804
+ failText,
805
+ successText,
806
+ text
807
+ }),
808
+ (cause) => new Error(failText, { cause })
763
809
  );
764
- var makeInitCommand = () => new Command4().name("init").description(
810
+ var validateAnswers = (answers) => okAsync2(answers);
811
+ var runGeneratorActions = (generator) => (answers) => withSpinner(
812
+ "Creating workspace files...",
813
+ "Workspace files created successfully!",
814
+ "Failed to create workspace files.",
815
+ generator.runActions(answers)
816
+ ).map(() => answers);
817
+ var displaySummary = (initResult) => {
818
+ const { csp, pr, repository } = initResult;
819
+ console.log(chalk.green.bold("\nWorkspace created successfully!"));
820
+ console.log(`- Cloud Service Provider: ${chalk.cyan(csp.name)}`);
821
+ console.log(`- CSP location: ${chalk.cyan(csp.location)}`);
822
+ if (repository) {
823
+ console.log(`- Name: ${chalk.cyan(repository.name)}`);
824
+ console.log(`- GitHub Repository: ${chalk.cyan(repository.url)}
825
+ `);
826
+ } else {
827
+ console.log(
828
+ chalk.yellow(
829
+ `
830
+ \u26A0\uFE0F GitHub repository may not have been created automatically.`
831
+ )
832
+ );
833
+ }
834
+ if (pr) {
835
+ console.log(chalk.green.bold("\nNext Steps:"));
836
+ console.log(
837
+ `1. Review the Pull Request in the GitHub repository: ${chalk.underline(pr.url)}`
838
+ );
839
+ console.log(
840
+ `2. Visit ${chalk.underline("https://dx.pagopa.it/getting-started")} to deploy your first project
841
+ `
842
+ );
843
+ } else {
844
+ console.log(
845
+ chalk.yellow(`
846
+ \u26A0\uFE0F There was an error during Pull Request creation.`)
847
+ );
848
+ console.log(
849
+ `Please, manually create a Pull Request in the GitHub repository to review the scaffolded code.
850
+ `
851
+ );
852
+ }
853
+ };
854
+ var checkTerraformCliIsInstalled = (text, successText, failText) => withSpinner(text, successText, failText, tf$`terraform -version`);
855
+ var checkPreconditions = () => checkTerraformCliIsInstalled(
856
+ "Checking Terraform CLI is installed...",
857
+ "Terraform CLI is installed!",
858
+ "Terraform CLI is not installed."
859
+ );
860
+ var createRemoteRepository = ({
861
+ repoName,
862
+ repoOwner
863
+ }) => {
864
+ const cwd2 = path.resolve(repoName, "infra", "repository");
865
+ const applyTerraform = async () => {
866
+ await tf$({ cwd: cwd2 })`terraform init`;
867
+ await tf$({ cwd: cwd2 })`terraform apply -auto-approve`;
868
+ };
869
+ return withSpinner(
870
+ "Creating GitHub repository...",
871
+ "GitHub repository created successfully!",
872
+ "Failed to create GitHub repository.",
873
+ applyTerraform()
874
+ ).map(() => new Repository(repoName, repoOwner));
875
+ };
876
+ var initializeGitRepository = (repository) => {
877
+ const cwd2 = path.resolve(repository.name);
878
+ const branchName = "features/scaffold-workspace";
879
+ const git$ = $3({
880
+ cwd: cwd2,
881
+ shell: true
882
+ });
883
+ const pushToOrigin = async () => {
884
+ await git$`git init`;
885
+ await git$`git add README.md`;
886
+ await git$`git commit --no-gpg-sign -m "Create README.md"`;
887
+ await git$`git branch -M main`;
888
+ await git$`git remote add origin ${repository.ssh}`;
889
+ await git$`git push -u origin main`;
890
+ await git$`git switch -c ${branchName}`;
891
+ await git$`git add .`;
892
+ await git$`git commit --no-gpg-sign -m "Scaffold workspace"`;
893
+ await git$`git push -u origin ${branchName}`;
894
+ };
895
+ return withSpinner(
896
+ "Pushing code to GitHub...",
897
+ "Code pushed to GitHub successfully!",
898
+ "Failed to push code to GitHub.",
899
+ pushToOrigin()
900
+ ).map(() => ({ branchName, repository }));
901
+ };
902
+ var handleNewGitHubRepository = (octokit2) => (answers) => createRemoteRepository(answers).andThen(initializeGitRepository).andThen(
903
+ (localWorkspace) => createPullRequest(octokit2)(localWorkspace).map((pr) => ({
904
+ pr,
905
+ repository: localWorkspace.repository
906
+ }))
907
+ );
908
+ var makeInitResult = (answers, { pr, repository }) => {
909
+ const csp = {
910
+ location: answers.csp === "azure" ? answers.azureLocation : answers.awsRegion,
911
+ name: answers.csp
912
+ };
913
+ return {
914
+ csp,
915
+ pr,
916
+ repository
917
+ };
918
+ };
919
+ var createPullRequest = (octokit2) => ({
920
+ branchName,
921
+ repository
922
+ }) => withSpinner(
923
+ "Creating Pull Request...",
924
+ "Pull Request created successfully!",
925
+ "Failed to create Pull Request.",
926
+ octokit2.rest.pulls.create({
927
+ base: "main",
928
+ body: "This PR contains the scaffolded monorepo structure.",
929
+ head: branchName,
930
+ owner: repository.owner,
931
+ repo: repository.name,
932
+ title: "Scaffold repository"
933
+ })
934
+ ).map(({ data }) => ({ url: data.html_url })).orElse(() => okAsync2(void 0));
935
+ var makeInitCommand = ({
936
+ octokit: octokit2
937
+ }) => new Command4().name("init").description(
765
938
  "Command to initialize resources (like projects, subscriptions, ...)"
766
939
  ).addCommand(
767
940
  new Command4("project").description("Initialize a new monorepo project").action(async function() {
768
- await initPlop().andTee(scaffoldMonorepo).andThen((plop) => getGenerator(plop)("monorepo")).andThen(runGenerator).andTee(() => {
769
- console.log("Monorepo initialized successfully \u2705");
770
- }).orTee((err2) => {
771
- this.error(err2.message);
772
- });
941
+ await checkPreconditions().andThen(initPlop).andTee(loadMonorepoScaffolder).andThen((plop) => getGenerator(plop)(PLOP_MONOREPO_GENERATOR_NAME)).andThen(
942
+ (generator) => (
943
+ // Ask the user the questions defined in the plop generator
944
+ getPrompts(generator).andThen(decode(answersSchema)).andThen(validateAnswers).andThen(runGeneratorActions(generator))
945
+ )
946
+ ).andThen(
947
+ (answers) => handleNewGitHubRepository(octokit2)(answers).map(
948
+ (repoPr) => makeInitResult(answers, repoPr)
949
+ )
950
+ ).match(displaySummary, exitWithError(this));
773
951
  })
774
952
  );
775
953
 
@@ -806,14 +984,17 @@ var makeSavemoneyCommand = () => new Command5("savemoney").description(
806
984
  // src/adapters/commander/index.ts
807
985
  var makeCli = (deps2, config2, cliDeps) => {
808
986
  const program2 = new Command6();
809
- program2.name("dx").description("The CLI for DX-Platform").version("0.11.2");
987
+ program2.name("dx").description("The CLI for DX-Platform").version("0.14.0");
810
988
  program2.addCommand(makeDoctorCommand(deps2, config2));
811
989
  program2.addCommand(makeCodemodCommand(cliDeps));
812
- program2.addCommand(makeInitCommand());
990
+ program2.addCommand(makeInitCommand(deps2));
813
991
  program2.addCommand(makeSavemoneyCommand());
814
992
  program2.addCommand(makeInfoCommand(deps2));
815
993
  return program2;
816
994
  };
995
+ var exitWithError = (command) => (error) => {
996
+ command.error(error.message);
997
+ };
817
998
 
818
999
  // src/adapters/logtape/validation-reporter.ts
819
1000
  import { getLogger as getLogger7 } from "@logtape/logtape";
@@ -835,16 +1016,9 @@ import { join as join2 } from "path";
835
1016
  import * as process3 from "process";
836
1017
 
837
1018
  // src/adapters/node/fs/file-reader.ts
838
- import { ResultAsync as ResultAsync6 } from "neverthrow";
1019
+ import { ResultAsync as ResultAsync7 } from "neverthrow";
839
1020
  import fs3 from "fs/promises";
840
1021
 
841
- // src/adapters/zod/index.ts
842
- import { ResultAsync as ResultAsync5 } from "neverthrow";
843
- var decode = (schema) => ResultAsync5.fromThrowable(
844
- schema.parseAsync,
845
- (cause) => new Error("File content is not valid for the given schema", { cause })
846
- );
847
-
848
1022
  // src/adapters/node/json/index.ts
849
1023
  import { Result as Result2 } from "neverthrow";
850
1024
  var parseJson = Result2.fromThrowable(
@@ -853,14 +1027,14 @@ var parseJson = Result2.fromThrowable(
853
1027
  );
854
1028
 
855
1029
  // src/adapters/node/fs/file-reader.ts
856
- var readFile2 = (filePath) => ResultAsync6.fromPromise(
1030
+ var readFile2 = (filePath) => ResultAsync7.fromPromise(
857
1031
  fs3.readFile(filePath, "utf-8"),
858
1032
  (cause) => new Error(`Failed to read file: ${filePath}`, { cause })
859
1033
  );
860
1034
  var readFileAndDecode = (filePath, schema) => readFile2(filePath).andThen(parseJson).andThen(decode(schema));
861
- var fileExists = (path2) => ResultAsync6.fromPromise(
862
- fs3.stat(path2),
863
- () => new Error(`${path2} not found.`)
1035
+ var fileExists = (path3) => ResultAsync7.fromPromise(
1036
+ fs3.stat(path3),
1037
+ () => new Error(`${path3} not found.`)
864
1038
  ).map(() => true);
865
1039
 
866
1040
  // src/adapters/node/package-json.ts
@@ -889,8 +1063,8 @@ var makePackageJsonReader = () => ({
889
1063
 
890
1064
  // src/adapters/node/repository.ts
891
1065
  import * as glob from "glob";
892
- import { okAsync as okAsync2, ResultAsync as ResultAsync7 } from "neverthrow";
893
- import * as path from "path";
1066
+ import { okAsync as okAsync3, ResultAsync as ResultAsync8 } from "neverthrow";
1067
+ import * as path2 from "path";
894
1068
  import { z as z3 } from "zod/v4";
895
1069
 
896
1070
  // src/adapters/yaml/index.ts
@@ -903,14 +1077,14 @@ var parseYaml = Result3.fromThrowable(
903
1077
 
904
1078
  // src/adapters/node/repository.ts
905
1079
  var findRepositoryRoot = (dir = process.cwd()) => {
906
- const gitPath = path.join(dir, ".git");
1080
+ const gitPath = path2.join(dir, ".git");
907
1081
  return fileExists(gitPath).mapErr(
908
1082
  () => new Error(
909
1083
  "Could not find repository root. Make sure to have the repo initialized."
910
1084
  )
911
1085
  ).map(() => dir);
912
1086
  };
913
- var resolveWorkspacePattern = (repoRoot, pattern) => ResultAsync7.fromPromise(
1087
+ var resolveWorkspacePattern = (repoRoot, pattern) => ResultAsync8.fromPromise(
914
1088
  // For now it is not possible to use the fs.glob function (from node:fs/promises)
915
1089
  // because it is not possible to run it on Node 20.x
916
1090
  glob.glob(pattern, { cwd: repoRoot }),
@@ -920,25 +1094,25 @@ var resolveWorkspacePattern = (repoRoot, pattern) => ResultAsync7.fromPromise(
920
1094
  ).map(
921
1095
  (subDirectories) => (
922
1096
  // Create the absolute path to the subdirectory
923
- subDirectories.map((directory) => path.join(repoRoot, directory))
1097
+ subDirectories.map((directory) => path2.join(repoRoot, directory))
924
1098
  )
925
1099
  );
926
- var getWorkspaces = (repoRoot) => readFile2(path.join(repoRoot, "pnpm-workspace.yaml")).andThen(parseYaml).andThen(
1100
+ var getWorkspaces = (repoRoot) => readFile2(path2.join(repoRoot, "pnpm-workspace.yaml")).andThen(parseYaml).andThen(
927
1101
  (obj) => (
928
1102
  // If no packages are defined, go on with an empty array
929
1103
  decode(z3.object({ packages: z3.array(z3.string()) }))(obj).orElse(
930
- () => okAsync2({ packages: [] })
1104
+ () => okAsync3({ packages: [] })
931
1105
  )
932
1106
  )
933
1107
  ).andThen(
934
1108
  ({ packages }) => (
935
1109
  // For every package pattern in the pnpm-workspace.yaml file, get the list of subdirectories
936
- ResultAsync7.combine(
1110
+ ResultAsync8.combine(
937
1111
  packages.map((pattern) => resolveWorkspacePattern(repoRoot, pattern))
938
1112
  ).map((workspacesList) => workspacesList.flat()).andThen((workspaceFolders) => {
939
1113
  const workspaceResults = workspaceFolders.map(
940
1114
  (nodeWorkspaceDirectory) => readFileAndDecode(
941
- path.join(nodeWorkspaceDirectory, "package.json"),
1115
+ path2.join(nodeWorkspaceDirectory, "package.json"),
942
1116
  packageJsonSchema
943
1117
  ).map(
944
1118
  ({ name }) => (
@@ -947,7 +1121,7 @@ var getWorkspaces = (repoRoot) => readFile2(path.join(repoRoot, "pnpm-workspace.
947
1121
  )
948
1122
  )
949
1123
  );
950
- return ResultAsync7.combine(workspaceResults);
1124
+ return ResultAsync8.combine(workspaceResults);
951
1125
  })
952
1126
  )
953
1127
  );
@@ -966,19 +1140,19 @@ var getConfig = () => ({
966
1140
  });
967
1141
 
968
1142
  // src/use-cases/apply-codemod.ts
969
- import { errAsync, okAsync as okAsync3, ResultAsync as ResultAsync8 } from "neverthrow";
1143
+ import { errAsync, okAsync as okAsync4, ResultAsync as ResultAsync9 } from "neverthrow";
970
1144
  var getCodemodById = (registry2, id) => registry2.getById(id).andThen(
971
- (codemod) => codemod ? okAsync3(codemod) : errAsync(new Error(`Codemod with id ${id} not found`))
1145
+ (codemod) => codemod ? okAsync4(codemod) : errAsync(new Error(`Codemod with id ${id} not found`))
972
1146
  );
973
- var safeGetInfo = (getInfo2) => ResultAsync8.fromPromise(
1147
+ var safeGetInfo = (getInfo2) => ResultAsync9.fromPromise(
974
1148
  getInfo2(),
975
1149
  (error) => new Error("Failed to get info", { cause: error })
976
1150
  );
977
- var applyCodemodById = (registry2, getInfo2) => (id) => ResultAsync8.combine([
1151
+ var applyCodemodById = (registry2, getInfo2) => (id) => ResultAsync9.combine([
978
1152
  safeGetInfo(getInfo2),
979
1153
  getCodemodById(registry2, id)
980
1154
  ]).andThen(
981
- ([info, codemod]) => ResultAsync8.fromPromise(codemod.apply(info), (error) => {
1155
+ ([info, codemod]) => ResultAsync9.fromPromise(codemod.apply(info), (error) => {
982
1156
  const message = error instanceof Error ? `: ${error.message}` : "";
983
1157
  return new Error("Failed to apply codemod" + message, { cause: error });
984
1158
  })
@@ -1009,7 +1183,11 @@ await configure({
1009
1183
  var repositoryReader = makeRepositoryReader();
1010
1184
  var packageJsonReader = makePackageJsonReader();
1011
1185
  var validationReporter = makeValidationReporter();
1186
+ var octokit = new Octokit2({
1187
+ auth: process.env.GITHUB_TOKEN
1188
+ });
1012
1189
  var deps = {
1190
+ octokit,
1013
1191
  packageJsonReader,
1014
1192
  repositoryReader,
1015
1193
  validationReporter
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagopa/dx-cli",
3
- "version": "0.11.2",
3
+ "version": "0.14.0",
4
4
  "type": "module",
5
5
  "description": "A CLI useful to manage DX tools.",
6
6
  "repository": {
@@ -20,6 +20,7 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@logtape/logtape": "^1.2.2",
23
+ "chalk": "^5.6.2",
23
24
  "commander": "^14.0.2",
24
25
  "core-js": "^3.47.0",
25
26
  "execa": "^9.6.1",
@@ -27,12 +28,13 @@
27
28
  "neverthrow": "^8.2.0",
28
29
  "node-plop": "^0.32.3",
29
30
  "octokit": "^5.0.5",
31
+ "ora": "^9.0.0",
30
32
  "replace-in-file": "^8.3.0",
31
33
  "semver": "^7.7.2",
32
34
  "yaml": "^2.8.2",
33
- "zod": "^3.25.76",
35
+ "zod": "^4.1.13",
34
36
  "@pagopa/dx-savemoney": "^0.1.4",
35
- "@pagopa/monorepo-generator": "^0.11.2"
37
+ "@pagopa/monorepo-generator": "^0.13.0"
36
38
  },
37
39
  "devDependencies": {
38
40
  "@tsconfig/node22": "22.0.2",