pob 40.7.3 → 40.8.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.
@@ -1,6 +1,8 @@
1
1
  import Generator from "yeoman-generator";
2
+ import { hasExpoApp } from "../../../utils/expoApps.js";
2
3
  import inMonorepo from "../../../utils/inMonorepo.js";
3
4
  import { writeAndFormatJson } from "../../../utils/writeAndFormat.js";
5
+ import { getRenovateExtends } from "./renovateExtends.js";
4
6
 
5
7
  export default class CoreRenovateGenerator extends Generator {
6
8
  constructor(args, opts) {
@@ -57,7 +59,7 @@ export default class CoreRenovateGenerator extends Generator {
57
59
  this.enableRenovate = this.enableRenovateConfig.enable;
58
60
  }
59
61
 
60
- writing() {
62
+ async writing() {
61
63
  if (this.enableRenovate) {
62
64
  const pkg = this.fs.readJSON(this.destinationPath("package.json"), {});
63
65
  const renovateConfig = this.fs.readJSON(
@@ -68,18 +70,15 @@ export default class CoreRenovateGenerator extends Generator {
68
70
  if (this.options.app) {
69
71
  renovateConfig.$schema =
70
72
  "https://docs.renovatebot.com/renovate-schema.json";
71
- renovateConfig.extends = [
72
- "config:js-app",
73
- "github>christophehurpeau/renovate-presets",
74
- ];
75
- } else {
76
- renovateConfig.extends = [
77
- "config:js-lib",
78
- pkg.name === "pob-monorepo"
79
- ? undefined
80
- : "github>christophehurpeau/renovate-presets",
81
- ].filter(Boolean);
82
73
  }
74
+ renovateConfig.extends = getRenovateExtends({
75
+ app: this.options.app,
76
+ pkgName: pkg.name,
77
+ withExpo: await hasExpoApp({
78
+ rootPath: this.destinationPath(),
79
+ pobConfig: this.config.getAll(),
80
+ }),
81
+ });
83
82
 
84
83
  return writeAndFormatJson(
85
84
  this.fs,
@@ -0,0 +1,14 @@
1
+ const presets = "github>christophehurpeau/renovate-presets";
2
+
3
+ /**
4
+ * The expo preset comes last so that it overrides the rules of the default
5
+ * preset matching the same packages.
6
+ *
7
+ * @param {{ app: boolean, pkgName: string | undefined, withExpo: boolean }} options
8
+ */
9
+ export const getRenovateExtends = ({ app, pkgName, withExpo }) =>
10
+ [
11
+ app ? "config:js-app" : "config:js-lib",
12
+ !app && pkgName === "pob-monorepo" ? undefined : presets,
13
+ withExpo ? `${presets}:expo` : undefined,
14
+ ].filter(Boolean);
@@ -0,0 +1,37 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { getRenovateExtends } from "./renovateExtends.js";
3
+
4
+ const presets = "github>christophehurpeau/renovate-presets";
5
+
6
+ describe("getRenovateExtends", () => {
7
+ it("extends the lib config and the presets for a library", () => {
8
+ expect(
9
+ getRenovateExtends({ app: false, pkgName: "lib", withExpo: false }),
10
+ ).toEqual(["config:js-lib", presets]);
11
+ });
12
+
13
+ it("extends the app config and the presets for an app", () => {
14
+ expect(
15
+ getRenovateExtends({ app: true, pkgName: "app", withExpo: false }),
16
+ ).toEqual(["config:js-app", presets]);
17
+ });
18
+
19
+ it("does not extend the presets for pob-monorepo", () => {
20
+ expect(
21
+ getRenovateExtends({
22
+ app: false,
23
+ pkgName: "pob-monorepo",
24
+ withExpo: false,
25
+ }),
26
+ ).toEqual(["config:js-lib"]);
27
+ });
28
+
29
+ it("extends the expo preset last with an expo app", () => {
30
+ expect(
31
+ getRenovateExtends({ app: true, pkgName: "mobile", withExpo: true }),
32
+ ).toEqual(["config:js-app", presets, `${presets}:expo`]);
33
+ expect(
34
+ getRenovateExtends({ app: false, pkgName: "alouette", withExpo: true }),
35
+ ).toEqual(["config:js-lib", presets, `${presets}:expo`]);
36
+ });
37
+ });
@@ -152,7 +152,8 @@ export default class PobMonorepoGenerator extends Generator {
152
152
  this.packageNames = this.packages.map((pkg) => pkg.name);
153
153
  this.packageConfigs = this.packageLocations.map((location) => {
154
154
  try {
155
- return JSON.parse(fs.readFileSync(`${location}/.yo-rc.json`)).pob;
155
+ return JSON.parse(fs.readFileSync(`${location}/.yo-rc.json`, "utf8"))
156
+ .pob;
156
157
  } catch {
157
158
  console.warn(`warn: could not read pob config in ${location}`);
158
159
  return {};
@@ -1,6 +1,9 @@
1
- import fs from "node:fs";
2
1
  import path from "node:path";
3
- import { buildDependentsMaps, discoverWorkspaces } from "./workspaceUtils.js";
2
+ import {
3
+ buildDependentsMaps,
4
+ discoverWorkspaces,
5
+ readPobConfig,
6
+ } from "./workspaceUtils.js";
4
7
 
5
8
  export const appsWithBrowser = ["alp", "next.js", "vite", "vite-with-server"];
6
9
 
@@ -19,14 +22,6 @@ export const getAppTsconfigTarget = ({ appType, dom, withBun }) => {
19
22
  return undefined;
20
23
  };
21
24
 
22
- const readPobConfig = (location) => {
23
- try {
24
- return JSON.parse(fs.readFileSync(path.join(location, ".yo-rc.json"))).pob;
25
- } catch {
26
- return undefined;
27
- }
28
- };
29
-
30
25
  /**
31
26
  * Target of the apps directly depending on the package, for packages compiled
32
27
  * by the bundler of these apps (untranspiled libraries). Expo wins as the
@@ -0,0 +1,22 @@
1
+ import path from "node:path";
2
+ import { discoverWorkspaces, readPobConfig } from "./workspaceUtils.js";
3
+
4
+ /** @param {Record<string, any> | undefined} pobConfig */
5
+ export const isExpoApp = (pobConfig) =>
6
+ pobConfig?.project?.type === "app" && pobConfig.app?.type === "expo";
7
+
8
+ /**
9
+ * Based on the pob config of the project and of its workspaces, like
10
+ * getAppTsconfigTarget, rather than on the package.json dependencies.
11
+ *
12
+ * @param {{ rootPath: string, pobConfig: Record<string, any> | undefined }} options
13
+ */
14
+ export const hasExpoApp = async ({ rootPath, pobConfig }) => {
15
+ if (isExpoApp(pobConfig)) return true;
16
+ const workspaces = await discoverWorkspaces(rootPath);
17
+ return workspaces.some(
18
+ (workspace) =>
19
+ !workspace.isRoot &&
20
+ isExpoApp(readPobConfig(path.join(rootPath, workspace.location))),
21
+ );
22
+ };
@@ -0,0 +1,81 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import { describe, expect, it } from "vitest";
5
+ import { hasExpoApp, isExpoApp } from "./expoApps.js";
6
+
7
+ const app = (type) => ({ project: { type: "app" }, app: { type } });
8
+
9
+ async function writeWorkspace(rootPath, location, pkg, pobConfig) {
10
+ const dir = path.join(rootPath, location);
11
+ await fs.mkdir(dir, { recursive: true });
12
+ await fs.writeFile(path.join(dir, "package.json"), JSON.stringify(pkg));
13
+ if (pobConfig) {
14
+ await fs.writeFile(
15
+ path.join(dir, ".yo-rc.json"),
16
+ JSON.stringify({ pob: pobConfig }),
17
+ );
18
+ }
19
+ }
20
+
21
+ /** Monorepo with a workspace for each [name, pobConfig] pair */
22
+ async function prepareMonorepo(workspaces) {
23
+ const rootPath = await fs.mkdtemp(path.join(os.tmpdir(), "pob-"));
24
+ await writeWorkspace(rootPath, ".", {
25
+ name: "root",
26
+ workspaces: ["packages/*"],
27
+ });
28
+ for (const [name, pobConfig] of workspaces) {
29
+ await writeWorkspace(rootPath, `packages/${name}`, { name }, pobConfig);
30
+ }
31
+ return rootPath;
32
+ }
33
+
34
+ const rootPobConfig = { project: { type: "lib" } };
35
+
36
+ describe("isExpoApp", () => {
37
+ it("returns true for an expo app", () => {
38
+ expect(isExpoApp(app("expo"))).toBe(true);
39
+ });
40
+
41
+ it("returns false for other apps, libraries and missing configs", () => {
42
+ expect(isExpoApp(app("vite"))).toBe(false);
43
+ expect(isExpoApp({ project: { type: "lib" }, app: { type: "expo" } })).toBe(
44
+ false,
45
+ );
46
+ expect(isExpoApp(undefined)).toBe(false);
47
+ });
48
+ });
49
+
50
+ describe("hasExpoApp", () => {
51
+ it("returns true when the project is an expo app", async () => {
52
+ const rootPath = await fs.mkdtemp(path.join(os.tmpdir(), "pob-"));
53
+ await writeWorkspace(rootPath, ".", { name: "mobile" });
54
+ expect(await hasExpoApp({ rootPath, pobConfig: app("expo") })).toBe(true);
55
+ });
56
+
57
+ it("returns false for a project without workspaces", async () => {
58
+ const rootPath = await fs.mkdtemp(path.join(os.tmpdir(), "pob-"));
59
+ await writeWorkspace(rootPath, ".", { name: "web" });
60
+ expect(await hasExpoApp({ rootPath, pobConfig: app("vite") })).toBe(false);
61
+ });
62
+
63
+ it("returns true when a workspace is an expo app", async () => {
64
+ const rootPath = await prepareMonorepo([
65
+ ["lib", rootPobConfig],
66
+ ["expo-demo-app", app("expo")],
67
+ ]);
68
+ expect(await hasExpoApp({ rootPath, pobConfig: rootPobConfig })).toBe(true);
69
+ });
70
+
71
+ it("returns false when no workspace is an expo app", async () => {
72
+ const rootPath = await prepareMonorepo([
73
+ ["lib", rootPobConfig],
74
+ ["web", app("vite")],
75
+ ["no-pob-config", undefined],
76
+ ]);
77
+ expect(await hasExpoApp({ rootPath, pobConfig: rootPobConfig })).toBe(
78
+ false,
79
+ );
80
+ });
81
+ });
@@ -12,11 +12,13 @@ const rootMonorepo =
12
12
 
13
13
  const rootMonorepoPkg =
14
14
  rootMonorepo &&
15
- JSON.parse(fs.readFileSync(path.resolve(rootMonorepo, "package.json")));
15
+ JSON.parse(
16
+ fs.readFileSync(path.resolve(rootMonorepo, "package.json"), "utf8"),
17
+ );
16
18
 
17
19
  const getInMonorepoThings = () => {
18
20
  const rootYoConfig = JSON.parse(
19
- fs.readFileSync(path.resolve(rootMonorepo, ".yo-rc.json")),
21
+ fs.readFileSync(path.resolve(rootMonorepo, ".yo-rc.json"), "utf8"),
20
22
  );
21
23
  const cwd = process.cwd();
22
24
 
@@ -15,7 +15,9 @@ const readPublishedPackageNames = (directory) =>
15
15
  .filter((entry) => entry.isDirectory())
16
16
  .map((entry) => path.join(rootPath, directory, entry.name, "package.json"))
17
17
  .filter((packageJsonPath) => fs.existsSync(packageJsonPath))
18
- .map((packageJsonPath) => JSON.parse(fs.readFileSync(packageJsonPath)))
18
+ .map((packageJsonPath) =>
19
+ JSON.parse(fs.readFileSync(packageJsonPath, "utf8")),
20
+ )
19
21
  .filter((pkg) => !pkg.private)
20
22
  .map((pkg) => pkg.name);
21
23
 
@@ -3,12 +3,14 @@ import semver from "semver";
3
3
  import { latestLTS, maintenanceLTS } from "./nodeVersions.js";
4
4
  import * as packageUtils from "./package.js";
5
5
 
6
- // 22.18.0 is the version with strip typescript out of experimental status.
7
- // 22 skips node 23: features backported to 22 are experimental there.
6
+ // Min versions are the October 2026 security releases (CVE-2026-105482,
7
+ // CVE-2026-111497...), above 22.18.0 which has strip typescript stable.
8
+ // 26.13.0 is the first 26 LTS, with the same code as security release 26.12.1.
9
+ // Odd majors are skipped: features backported to 22 are experimental in 23.
8
10
  const enginesNodeForMajor = new Map([
9
- ["22", "^22.18.0 || >=24.14.1"],
10
- ["24", ">=24.14.1"],
11
- ["26", ">=26.0.0"],
11
+ ["22", "^22.23.4 || ^24.22.1 || >=26.13.0"],
12
+ ["24", "^24.22.1 || >=26.13.0"],
13
+ ["26", ">=26.13.0"],
12
14
  ]);
13
15
 
14
16
  const minVersionForMajor = (major) => {
@@ -6,12 +6,12 @@ import {
6
6
  updateTypesNodeFromEngines,
7
7
  } from "./nodeEngines.js";
8
8
 
9
- const NODE_22 = "^22.18.0 || >=24.14.1";
9
+ const NODE_22 = "^22.23.4 || ^24.22.1 || >=26.13.0";
10
10
 
11
11
  describe("updateTypesNodeFromEngines", () => {
12
12
  it("keeps pinned devDependencies pinned when lower than engines min major", () => {
13
13
  const pkg = {
14
- engines: { node: ">=24.14.1" },
14
+ engines: { node: "^24.22.1 || >=26.13.0" },
15
15
  devDependencies: { "@types/node": "22.5.0" },
16
16
  };
17
17
  updateTypesNodeFromEngines(pkg);
@@ -31,7 +31,7 @@ describe("updateTypesNodeFromEngines", () => {
31
31
 
32
32
  it("keeps devDependencies with the same major", () => {
33
33
  const pkg = {
34
- engines: { node: ">=24.14.1" },
34
+ engines: { node: "^24.22.1 || >=26.13.0" },
35
35
  devDependencies: { "@types/node": "24.13.3" },
36
36
  };
37
37
  updateTypesNodeFromEngines(pkg);
@@ -49,7 +49,7 @@ describe("updateTypesNodeFromEngines", () => {
49
49
 
50
50
  it("updates a devDependencies range", () => {
51
51
  const pkg = {
52
- engines: { node: ">=24.14.1" },
52
+ engines: { node: "^24.22.1 || >=26.13.0" },
53
53
  devDependencies: { "@types/node": ">=22.0.0" },
54
54
  };
55
55
  updateTypesNodeFromEngines(pkg);
@@ -66,7 +66,10 @@ describe("updateTypesNodeFromEngines", () => {
66
66
  });
67
67
 
68
68
  it("does not add @types/node", () => {
69
- const pkg = { engines: { node: ">=24.14.1" }, devDependencies: {} };
69
+ const pkg = {
70
+ engines: { node: "^24.22.1 || >=26.13.0" },
71
+ devDependencies: {},
72
+ };
70
73
  updateTypesNodeFromEngines(pkg);
71
74
  expect(pkg.devDependencies["@types/node"]).toBeUndefined();
72
75
  });
@@ -88,7 +91,7 @@ describe("updateTypesNodeFromEngines", () => {
88
91
  "keeps unparseable version %s",
89
92
  (version) => {
90
93
  const pkg = {
91
- engines: { node: ">=24.14.1" },
94
+ engines: { node: "^24.22.1 || >=26.13.0" },
92
95
  devDependencies: { "@types/node": version },
93
96
  };
94
97
  updateTypesNodeFromEngines(pkg);
@@ -101,8 +104,8 @@ describe("resolveEnginesNode", () => {
101
104
  it.each([
102
105
  ["18", NODE_22],
103
106
  ["22", NODE_22],
104
- ["24", ">=24.14.1"],
105
- ["26", ">=26.0.0"],
107
+ ["24", "^24.22.1 || >=26.13.0"],
108
+ ["26", ">=26.13.0"],
106
109
  ])("resolves min major %s from envs", (minMajor, expected) => {
107
110
  expect(resolveEnginesNode(">=22.18.0", minMajor, true)).toBe(expected);
108
111
  });
@@ -126,18 +129,22 @@ describe("resolveEnginesNode", () => {
126
129
  });
127
130
 
128
131
  it("lets envs lower engines.node", () => {
129
- expect(resolveEnginesNode(">=24.14.1", "22", true)).toBe(NODE_22);
132
+ expect(resolveEnginesNode("^24.22.1 || >=26.13.0", "22", true)).toBe(
133
+ NODE_22,
134
+ );
130
135
  });
131
136
 
132
137
  it.each([true, false])(
133
138
  "keeps a higher min version of the same major (hasEnvs: %s)",
134
139
  (hasEnvs) => {
135
- expect(resolveEnginesNode(">=24.15.3", "24", hasEnvs)).toBe(">=24.15.3");
140
+ expect(resolveEnginesNode(">=24.23.0", "24", hasEnvs)).toBe(">=24.23.0");
136
141
  },
137
142
  );
138
143
 
139
144
  it("raises a lower min version of the same major", () => {
140
- expect(resolveEnginesNode(">=24.0.0", "24", true)).toBe(">=24.14.1");
145
+ expect(resolveEnginesNode(">=24.0.0", "24", true)).toBe(
146
+ "^24.22.1 || >=26.13.0",
147
+ );
141
148
  });
142
149
  });
143
150
 
@@ -148,7 +155,7 @@ describe("updateNodeEngines", () => {
148
155
  devDependencies: { "@types/node": "22.5.0" },
149
156
  };
150
157
  updateNodeEngines(pkg, {});
151
- expect(pkg.engines.node).toBe(">=24.14.1");
158
+ expect(pkg.engines.node).toBe("^24.22.1 || >=26.13.0");
152
159
  expect(pkg.devDependencies["@types/node"]).toBe(
153
160
  pobDependencies["@types/node"],
154
161
  );
@@ -157,7 +164,7 @@ describe("updateNodeEngines", () => {
157
164
  it("supports babelEnvs", () => {
158
165
  const pkg = { pob: { babelEnvs: [{ target: "node", version: "24" }] } };
159
166
  updateNodeEngines(pkg, {});
160
- expect(pkg.engines.node).toBe(">=24.14.1");
167
+ expect(pkg.engines.node).toBe("^24.22.1 || >=26.13.0");
161
168
  });
162
169
 
163
170
  it("uses the maintenance LTS without envs", () => {
@@ -169,7 +176,7 @@ describe("updateNodeEngines", () => {
169
176
  it("uses the latest LTS without envs when onlyLatestLTS", () => {
170
177
  const pkg = {};
171
178
  updateNodeEngines(pkg, { onlyLatestLTS: true });
172
- expect(pkg.engines.node).toBe(">=24.14.1");
179
+ expect(pkg.engines.node).toBe("^24.22.1 || >=26.13.0");
173
180
  });
174
181
 
175
182
  it("removes @types/node when there is no node target", () => {
@@ -196,9 +203,9 @@ describe("updateNodeEngines", () => {
196
203
  it("keeps a higher engines.node when there is no node target", () => {
197
204
  const pkg = {
198
205
  pob: { envs: [{ target: "browser" }] },
199
- engines: { node: ">=24.14.1" },
206
+ engines: { node: "^24.22.1 || >=26.13.0" },
200
207
  };
201
208
  updateNodeEngines(pkg, {});
202
- expect(pkg.engines.node).toBe(">=24.14.1");
209
+ expect(pkg.engines.node).toBe("^24.22.1 || >=26.13.0");
203
210
  });
204
211
  });
@@ -86,6 +86,7 @@ const getVersionFromDependencyName = (dependency) => {
86
86
  // import.meta
87
87
  // .resolve(`${dependency}/package.json`, import.meta.url)
88
88
  ),
89
+ "utf8",
89
90
  ),
90
91
  );
91
92
  return pkgJson.version;
@@ -1,5 +1,5 @@
1
1
  import fs from "node:fs";
2
2
 
3
3
  export default JSON.parse(
4
- fs.readFileSync(new URL("../../package.json", import.meta.url)),
4
+ fs.readFileSync(new URL("../../package.json", import.meta.url), "utf8"),
5
5
  );
@@ -3,6 +3,16 @@ import { glob } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { PackageDependencyDescriptorUtils } from "./packageDependencyDescriptorUtils.js";
5
5
 
6
+ export const readPobConfig = (location) => {
7
+ try {
8
+ return JSON.parse(
9
+ fs.readFileSync(path.join(location, ".yo-rc.json"), "utf8"),
10
+ ).pob;
11
+ } catch {
12
+ return undefined;
13
+ }
14
+ };
15
+
6
16
  export const getWorkspaceName = (workspace) => {
7
17
  if (workspace?.manifest?.raw?.name) return workspace.manifest.raw.name;
8
18
  return path.basename(workspace.location) || "unnamed-workspace";
@@ -20,7 +30,7 @@ export const discoverWorkspaces = async (rootPath) => {
20
30
  if (!fs.existsSync(rootPackageJSONPath)) {
21
31
  return [];
22
32
  }
23
- const rootPkg = JSON.parse(fs.readFileSync(rootPackageJSONPath));
33
+ const rootPkg = JSON.parse(fs.readFileSync(rootPackageJSONPath, "utf8"));
24
34
 
25
35
  let workspaceGlobs = [];
26
36
  if (Array.isArray(rootPkg.workspaces)) {
@@ -48,7 +58,7 @@ export const discoverWorkspaces = async (rootPath) => {
48
58
  if (found.has(match)) continue;
49
59
  found.add(match);
50
60
  const filePath = path.join(rootPath, match);
51
- const content = JSON.parse(fs.readFileSync(filePath));
61
+ const content = JSON.parse(fs.readFileSync(filePath, "utf8"));
52
62
  const dir = path.dirname(match);
53
63
  workspaces.push({
54
64
  name: content.name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pob",
3
- "version": "40.7.3",
3
+ "version": "40.8.0",
4
4
  "description": "Pile of bones, library generator with git/babel/typescript/typedoc/readme/vitest",
5
5
  "keywords": [
6
6
  "skeleton"
@@ -31,12 +31,12 @@
31
31
  "./package.json": "./package.json"
32
32
  },
33
33
  "dependencies": {
34
- "@pob/codemods": "0.18.2",
35
- "@pob/eslint-config": "68.3.0",
36
- "@pob/eslint-config-typescript-react": "68.4.0",
37
- "@pob/sort-object": "11.3.0",
38
- "@pob/sort-pkg": "13.3.0",
39
- "@yeoman/adapter": "4.0.3",
34
+ "@pob/codemods": "0.18.3",
35
+ "@pob/eslint-config": "68.4.0",
36
+ "@pob/eslint-config-typescript-react": "68.5.0",
37
+ "@pob/sort-object": "11.3.1",
38
+ "@pob/sort-pkg": "13.3.1",
39
+ "@yeoman/adapter": "5.1.1",
40
40
  "eslint": "10.14.0",
41
41
  "findup-sync": "^5.0.0",
42
42
  "git-remote-url": "^1.0.1",
@@ -45,27 +45,27 @@
45
45
  "json5": "^2.2.3",
46
46
  "lodash.camelcase": "^4.3.0",
47
47
  "lodash.kebabcase": "^4.1.1",
48
- "mem-fs": "4.1.5",
48
+ "mem-fs": "6.0.0",
49
49
  "mem-fs-editor": "12.1.0",
50
50
  "minimist": "1.2.8",
51
51
  "oxfmt": "0.75.0",
52
52
  "parse-author": "2.0.0",
53
- "pob-dependencies": "28.6.0",
53
+ "pob-dependencies": "28.7.0",
54
54
  "semver": "7.8.5",
55
55
  "typescript": "npm:@typescript/typescript6@6.0.2",
56
56
  "validate-npm-package-name": "^8.0.0",
57
- "yeoman-environment": "6.3.0",
57
+ "yeoman-environment": "8.5.1",
58
58
  "yeoman-generator": "10.3.0"
59
59
  },
60
60
  "devDependencies": {
61
- "@pob/root": "28.6.1",
61
+ "@pob/root": "28.7.0",
62
62
  "@types/node": "24.19.1",
63
63
  "@typescript/native": "npm:typescript@7.0.2",
64
64
  "@yeoman/types": "1.13.1",
65
- "tsrs": "0.18.0-ts7.1.0-dev.20260929"
65
+ "tsrs": "0.20.1-ts7.1.0-beta"
66
66
  },
67
67
  "engines": {
68
- "node": ">=24.14.1"
68
+ "node": "^24.22.1 || >=26.13.0"
69
69
  },
70
70
  "pob": {
71
71
  "typescript": "check-only"