alex-c-line 1.28.1 → 1.29.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.
@@ -99,13 +99,16 @@ const PullRequestTemplateCategory = {
99
99
 
100
100
  //#endregion
101
101
  //#region src/configs/helpers/defineAlexCLinePrivateConfig.ts
102
- const alexCLinePrivateConfigSchema = zod.default.object({ useLocalPackage: zod.default.strictObject({ localPackages: zod.default.record(zod.default.string(), zod.default.strictObject({
103
- packageManager: zod.default.enum(_alextheman_utility_internal.PackageManager),
104
- path: zod.default.string(),
105
- prepareScript: zod.default.string().optional(),
106
- dependencyGroup: zod.default.enum(DependencyGroup).optional(),
107
- keepOldTarballs: zod.default.boolean().optional()
108
- })) }) });
102
+ const alexCLinePrivateConfigSchema = zod.default.object({ useLocalPackage: zod.default.strictObject({
103
+ enableCache: zod.default.boolean().optional(),
104
+ localPackages: zod.default.record(zod.default.string(), zod.default.strictObject({
105
+ packageManager: zod.default.enum(_alextheman_utility_internal.PackageManager),
106
+ path: zod.default.string(),
107
+ prepareScript: zod.default.string().optional(),
108
+ dependencyGroup: zod.default.enum(DependencyGroup).optional(),
109
+ keepOldTarballs: zod.default.boolean().optional()
110
+ }))
111
+ }) });
109
112
  function defineAlexCLinePrivateConfig(config) {
110
113
  return config;
111
114
  }
@@ -81,6 +81,8 @@ interface LocalPackage<ScriptName extends string = string> {
81
81
  keepOldTarballs?: boolean;
82
82
  }
83
83
  interface UseLocalPackageConfig<ScriptName extends string = string> {
84
+ /** Enable caching of the previous version number. */
85
+ enableCache?: boolean;
84
86
  /** A record of all packages that we may want to consider using locally. */
85
87
  localPackages: Record<string, LocalPackage<ScriptName>>;
86
88
  }
@@ -82,6 +82,8 @@ interface LocalPackage<ScriptName extends string = string> {
82
82
  keepOldTarballs?: boolean;
83
83
  }
84
84
  interface UseLocalPackageConfig<ScriptName extends string = string> {
85
+ /** Enable caching of the previous version number. */
86
+ enableCache?: boolean;
85
87
  /** A record of all packages that we may want to consider using locally. */
86
88
  localPackages: Record<string, LocalPackage<ScriptName>>;
87
89
  }
@@ -70,13 +70,16 @@ const PullRequestTemplateCategory = {
70
70
 
71
71
  //#endregion
72
72
  //#region src/configs/helpers/defineAlexCLinePrivateConfig.ts
73
- const alexCLinePrivateConfigSchema = z.object({ useLocalPackage: z.strictObject({ localPackages: z.record(z.string(), z.strictObject({
74
- packageManager: z.enum(PackageManager),
75
- path: z.string(),
76
- prepareScript: z.string().optional(),
77
- dependencyGroup: z.enum(DependencyGroup).optional(),
78
- keepOldTarballs: z.boolean().optional()
79
- })) }) });
73
+ const alexCLinePrivateConfigSchema = z.object({ useLocalPackage: z.strictObject({
74
+ enableCache: z.boolean().optional(),
75
+ localPackages: z.record(z.string(), z.strictObject({
76
+ packageManager: z.enum(PackageManager),
77
+ path: z.string(),
78
+ prepareScript: z.string().optional(),
79
+ dependencyGroup: z.enum(DependencyGroup).optional(),
80
+ keepOldTarballs: z.boolean().optional()
81
+ }))
82
+ }) });
80
83
  function defineAlexCLinePrivateConfig(config) {
81
84
  return config;
82
85
  }
package/dist/index.cjs CHANGED
@@ -28,10 +28,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  //#endregion
29
29
  let _alextheman_utility = require("@alextheman/utility");
30
30
  let commander = require("commander");
31
- let supports_color = require("supports-color");
32
- supports_color = __toESM(supports_color);
33
- let update_notifier = require("update-notifier");
34
- update_notifier = __toESM(update_notifier);
35
31
  let canvas = require("canvas");
36
32
  let node_fs_promises = require("node:fs/promises");
37
33
  let node_path = require("node:path");
@@ -42,6 +38,8 @@ let chalk = require("chalk");
42
38
  chalk = __toESM(chalk);
43
39
  let figlet = require("figlet");
44
40
  figlet = __toESM(figlet);
41
+ let env_paths = require("env-paths");
42
+ env_paths = __toESM(env_paths);
45
43
  let execa = require("execa");
46
44
  let dotenv = require("dotenv");
47
45
  dotenv = __toESM(dotenv);
@@ -58,6 +56,8 @@ let node_url = require("node:url");
58
56
  let gray_matter = require("gray-matter");
59
57
  gray_matter = __toESM(gray_matter);
60
58
  let _alextheman_utility_node = require("@alextheman/utility/node");
59
+ let supports_color = require("supports-color");
60
+ supports_color = __toESM(supports_color);
61
61
 
62
62
  //#region src/utility/miscellaneous/asciiToPng.ts
63
63
  async function asciiToPng(ascii, options) {
@@ -129,6 +129,20 @@ function artwork(program) {
129
129
  });
130
130
  }
131
131
 
132
+ //#endregion
133
+ //#region src/cache/global/envPaths.ts
134
+ const alexCLineEnvPaths = (0, env_paths.default)("alex-c-line");
135
+ const { cache: ALEX_C_LINE_GLOBAL_CACHE_DIRECTORY } = alexCLineEnvPaths;
136
+ const ALEX_C_LINE_GLOBAL_CACHE_PATH = node_path.default.join(ALEX_C_LINE_GLOBAL_CACHE_DIRECTORY, "cache.json");
137
+
138
+ //#endregion
139
+ //#region src/commands/cache-path.ts
140
+ function cachePath(program) {
141
+ program.command("cache-path").description("Log the path to the alex-c-line cache files.").action(() => {
142
+ console.info(ALEX_C_LINE_GLOBAL_CACHE_PATH);
143
+ });
144
+ }
145
+
132
146
  //#endregion
133
147
  //#region src/commands/check-for-file-dependencies.ts
134
148
  function findFileDependencies(dependencies) {
@@ -904,13 +918,16 @@ const PullRequestTemplateCategory = {
904
918
 
905
919
  //#endregion
906
920
  //#region src/configs/helpers/defineAlexCLinePrivateConfig.ts
907
- const alexCLinePrivateConfigSchema = zod.default.object({ useLocalPackage: zod.default.strictObject({ localPackages: zod.default.record(zod.default.string(), zod.default.strictObject({
908
- packageManager: zod.default.enum(_alextheman_utility_internal.PackageManager),
909
- path: zod.default.string(),
910
- prepareScript: zod.default.string().optional(),
911
- dependencyGroup: zod.default.enum(DependencyGroup).optional(),
912
- keepOldTarballs: zod.default.boolean().optional()
913
- })) }) });
921
+ const alexCLinePrivateConfigSchema = zod.default.object({ useLocalPackage: zod.default.strictObject({
922
+ enableCache: zod.default.boolean().optional(),
923
+ localPackages: zod.default.record(zod.default.string(), zod.default.strictObject({
924
+ packageManager: zod.default.enum(_alextheman_utility_internal.PackageManager),
925
+ path: zod.default.string(),
926
+ prepareScript: zod.default.string().optional(),
927
+ dependencyGroup: zod.default.enum(DependencyGroup).optional(),
928
+ keepOldTarballs: zod.default.boolean().optional()
929
+ }))
930
+ }) });
914
931
  function parseAlexCLinePrivateConfig(data) {
915
932
  return (0, _alextheman_utility.parseZodSchema)(alexCLinePrivateConfigSchema, data);
916
933
  }
@@ -1255,121 +1272,35 @@ function setReleaseStatus2(program) {
1255
1272
  });
1256
1273
  }
1257
1274
 
1275
+ //#endregion
1276
+ //#region src/utility/errors/convertDataErrorToProgramError.ts
1277
+ function convertDataErrorToProgramError(dataError, programError, options) {
1278
+ programError(dataError.message, {
1279
+ exitCode: options?.exitCode ?? 1,
1280
+ code: dataError.code
1281
+ });
1282
+ }
1283
+
1284
+ //#endregion
1285
+ //#region src/utility/miscellaneous/parseZodSchemaForProgram.ts
1286
+ function parseZodSchemaForProgram(programError, schema, data) {
1287
+ try {
1288
+ return (0, _alextheman_utility.parseZodSchema)(schema, data);
1289
+ } catch (error) {
1290
+ if (_alextheman_utility.DataError.check(error)) convertDataErrorToProgramError(error, programError);
1291
+ throw error;
1292
+ }
1293
+ }
1294
+
1258
1295
  //#endregion
1259
1296
  //#region package.json
1260
1297
  var name = "alex-c-line";
1261
- var version = "1.28.1";
1298
+ var version = "1.29.0";
1262
1299
  var description = "Command-line tool with commands to streamline the developer workflow.";
1263
- var package_default = {
1264
- name,
1265
- version,
1266
- description,
1267
- repository: {
1268
- "type": "git",
1269
- "url": "git+https://github.com/alextheman231/alex-c-line.git"
1270
- },
1271
- sideEffects: ["./dist/index.js"],
1272
- license: "MIT",
1273
- author: "alextheman",
1274
- type: "module",
1275
- exports: {
1276
- "./configs": {
1277
- "types": "./dist/configs/index.d.ts",
1278
- "require": "./dist/configs/index.cjs",
1279
- "import": "./dist/configs/index.js",
1280
- "default": "./dist/configs/index.js"
1281
- },
1282
- "./configs/internal": {
1283
- "types": "./dist/configs/internal/index.d.ts",
1284
- "require": "./dist/configs/internal/index.cjs",
1285
- "import": "./dist/configs/internal/index.js",
1286
- "default": "./dist/configs/internal/index.js"
1287
- }
1288
- },
1289
- bin: { "alex-c-line": "./dist/index.js" },
1290
- files: ["dist", "templates"],
1291
- scripts: {
1292
- "build": "tsdown",
1293
- "command": "bash -c 'pnpm run build && echo && echo \"Command output:\" && node dist/index.js $@' --",
1294
- "create-local-package": "pnpm run build && rm -f alex-c-line-*.tgz && pnpm pack",
1295
- "create-release-note": "bash -c 'git pull origin main && pnpm run command create-release-note-2 $@' --",
1296
- "format": "pnpm run format-prettier && pnpm run format-eslint",
1297
- "format-eslint": "eslint --fix --suppress-all \"package.json\" \"src/**/*.ts\" \"tests/**/*.ts\" && rm -f eslint-suppressions.json",
1298
- "format-prettier": "pnpm run format-prettier-typescript && pnpm run format-prettier-javascript && pnpm run format-prettier-yml",
1299
- "format-prettier-javascript": "prettier --write \"./**/*.js\"",
1300
- "format-prettier-typescript": "prettier --write --parser typescript \"./**/*.ts\"",
1301
- "format-prettier-yml": "prettier --write \"./**/*.{yml,yaml}\"",
1302
- "lint": "pnpm run lint-tsc && pnpm run lint-eslint && pnpm run lint-prettier",
1303
- "lint-eslint": "eslint \"package.json\" \"src/**/*.ts\" \"tests/**/*.ts\"",
1304
- "lint-prettier": "pnpm run lint-prettier-typescript && pnpm run lint-prettier-javascript && pnpm run lint-prettier-yml",
1305
- "lint-prettier-javascript": "prettier --check \"./**.js\"",
1306
- "lint-prettier-typescript": "prettier --check --parser typescript \"./**/*.ts\"",
1307
- "lint-prettier-yml": "prettier --check \"./**/*.{yml,yaml}\"",
1308
- "lint-tsc": "tsc --noEmit",
1309
- "pre-commit": "pnpm run command pre-commit-2",
1310
- "prepare": "husky",
1311
- "prepare-live-eslint-plugin": "pnpm uninstall @alextheman/eslint-plugin && pnpm install --save-dev @alextheman/eslint-plugin",
1312
- "prepare-live-utility": "pnpm uninstall @alextheman/utility && pnpm install @alextheman/utility",
1313
- "prepare-local-eslint-plugin": "dotenv -e .env -- sh -c 'ESLINT_PLUGIN_PATH=${LOCAL_ESLINT_PLUGIN_PATH:-../eslint-plugin}; pnpm --prefix \"$ESLINT_PLUGIN_PATH\" run build && pnpm uninstall @alextheman/eslint-plugin && pnpm install --save-dev file:\"$ESLINT_PLUGIN_PATH\"'",
1314
- "prepare-local-utility": "dotenv -e .env -- sh -c 'UTILITY_PATH=${LOCAL_UTILITY_PATH:-../utility}; pnpm --prefix \"$UTILITY_PATH\" run build && pnpm uninstall @alextheman/utility && pnpm install file:\"$UTILITY_PATH\"'",
1315
- "test": "vitest run",
1316
- "test-end-to-end": "RUN_END_TO_END=true vitest run tests/end-to-end --reporter verbose",
1317
- "test-watch": "vitest",
1318
- "update-dependencies": "pnpm update --latest && pnpm update",
1319
- "use-live-eslint-plugin": "pnpm run prepare-live-eslint-plugin && pnpm run lint",
1320
- "use-live-utility": "pnpm run prepare-live-utility",
1321
- "use-local-eslint-plugin": "pnpm run prepare-local-eslint-plugin && pnpm run lint",
1322
- "use-local-utility": "pnpm run prepare-local-utility"
1323
- },
1324
- dependencies: {
1325
- "@alextheman/utility": "^5.1.4",
1326
- "@inquirer/prompts": "^8.2.1",
1327
- "boxen": "^8.0.1",
1328
- "canvas": "^3.2.1",
1329
- "chalk": "^5.6.2",
1330
- "commander": "^14.0.3",
1331
- "dotenv": "^17.3.1",
1332
- "dotenv-stringify": "^3.0.1",
1333
- "execa": "^9.6.1",
1334
- "figlet": "^1.10.0",
1335
- "gray-matter": "^4.0.3",
1336
- "libsodium-wrappers": "^0.8.2",
1337
- "supports-color": "^10.2.2",
1338
- "update-notifier": "^7.3.1",
1339
- "zod": "^4.3.6"
1340
- },
1341
- devDependencies: {
1342
- "@alextheman/eslint-plugin": "^5.8.1",
1343
- "@commander-js/extra-typings": "^14.0.0",
1344
- "@types/eslint": "^9.6.1",
1345
- "@types/node": "^25.3.0",
1346
- "@types/update-notifier": "^6.0.8",
1347
- "dotenv-cli": "^11.0.0",
1348
- "eslint": "^10.0.1",
1349
- "husky": "^9.1.7",
1350
- "prettier": "^3.8.1",
1351
- "tempy": "^3.2.0",
1352
- "ts-node": "^10.9.2",
1353
- "tsdown": "^0.20.3",
1354
- "typescript": "^5.9.3",
1355
- "typescript-eslint": "^8.56.0",
1356
- "vite-tsconfig-paths": "^6.1.1",
1357
- "vitest": "^4.0.18"
1358
- },
1359
- packageManager: "pnpm@10.30.1+sha512.3590e550d5384caa39bd5c7c739f72270234b2f6059e13018f975c313b1eb9fefcc09714048765d4d9efe961382c312e624572c0420762bdc5d5940cdf9be73a",
1360
- engines: { "node": ">=22.0.0" },
1361
- pnpm: { "onlyBuiltDependencies": [
1362
- "canvas",
1363
- "core-js",
1364
- "esbuild",
1365
- "fsevents",
1366
- "unrs-resolver"
1367
- ] }
1368
- };
1369
1300
 
1370
1301
  //#endregion
1371
- //#region src/commands/update/checkUpdate.ts
1372
- async function checkUpdate(program) {
1302
+ //#region src/utility/updates/checkUpdate.ts
1303
+ async function checkUpdate(options) {
1373
1304
  const currentVersion = new _alextheman_utility.VersionNumber(version);
1374
1305
  const { stdout: npmViewResult } = await execa.execa`npm view alex-c-line version`;
1375
1306
  const latestVersion = new _alextheman_utility.VersionNumber(npmViewResult.trim());
@@ -1389,40 +1320,25 @@ async function checkUpdate(program) {
1389
1320
  return centerLine(line, width);
1390
1321
  }).join("\n")
1391
1322
  });
1392
- if (program) program.error(messageWithArtwork, {
1393
- exitCode: 2,
1394
- code: "OUTDATED_VERSION"
1395
- });
1396
- else console.info(messageWithArtwork);
1397
- } else console.info(`alex-c-line is up to date (${currentVersion}).`);
1398
- }
1399
-
1400
- //#endregion
1401
- //#region src/utility/errors/convertDataErrorToProgramError.ts
1402
- function convertDataErrorToProgramError(dataError, programError, options) {
1403
- programError(dataError.message, {
1404
- exitCode: options?.exitCode ?? 1,
1405
- code: dataError.code
1406
- });
1407
- }
1408
-
1409
- //#endregion
1410
- //#region src/utility/miscellaneous/parseZodSchemaForProgram.ts
1411
- function parseZodSchemaForProgram(programError, schema, data) {
1412
- try {
1413
- return (0, _alextheman_utility.parseZodSchema)(schema, data);
1414
- } catch (error) {
1415
- if (_alextheman_utility.DataError.check(error)) convertDataErrorToProgramError(error, programError);
1416
- throw error;
1417
- }
1323
+ if (options?.program) {
1324
+ const { program } = options;
1325
+ program.error(messageWithArtwork, {
1326
+ exitCode: 2,
1327
+ code: "OUTDATED_VERSION"
1328
+ });
1329
+ } else console.info(messageWithArtwork);
1330
+ } else if (options?.logNoUpdates) console.info(`alex-c-line is up to date (${currentVersion}).`);
1418
1331
  }
1419
1332
 
1420
1333
  //#endregion
1421
- //#region src/commands/update/index.ts
1334
+ //#region src/commands/update.ts
1422
1335
  const optionsSchema = zod.default.object({ check: zod.default.boolean().optional() });
1423
1336
  function update(program) {
1424
- program.command("update").description("Handle updates of the currently installed alex-c-line").option("--check", "Check for available updates").action(async (rawOptions) => {
1425
- if (parseZodSchemaForProgram(program.error, optionsSchema, rawOptions).check) await checkUpdate(program);
1337
+ program.command("update").description("Handle updates of the currently installed alex-c-line").option("--check", "Check for available updates").option("--apply", "Apply the latest update").action(async (rawOptions) => {
1338
+ if (parseZodSchemaForProgram(program.error, optionsSchema, rawOptions).check) await checkUpdate({
1339
+ program,
1340
+ logNoUpdates: true
1341
+ });
1426
1342
  else console.info("Unsupported option. Expected `--check`.");
1427
1343
  });
1428
1344
  }
@@ -1446,7 +1362,7 @@ const alexCLineProjectCacheSchema = zod.default.object({ useLocalPackage: zod.de
1446
1362
  })) }).partial() }).partial();
1447
1363
 
1448
1364
  //#endregion
1449
- //#region src/cache/project/parseAlexCLineCache.ts
1365
+ //#region src/cache/project/parseAlexCLineProjectCache.ts
1450
1366
  function parseAlexCLineProjectCache(data) {
1451
1367
  return (0, _alextheman_utility.parseZodSchema)(alexCLineProjectCacheSchema, data);
1452
1368
  }
@@ -1529,7 +1445,7 @@ function useLocalPackage(program) {
1529
1445
  exitCode: 1,
1530
1446
  code: "ALEX_C_LINE_PRIVATE_CONFIG_NOT_FOUND"
1531
1447
  });
1532
- const { useLocalPackage: { localPackages } } = await loadAlexCLinePrivateConfig(configPath);
1448
+ const { useLocalPackage: { enableCache, localPackages } } = await loadAlexCLinePrivateConfig(configPath);
1533
1449
  const localPackage = localPackages[packageName];
1534
1450
  if (!localPackage) throw new _alextheman_utility.DataError({
1535
1451
  packageName,
@@ -1566,7 +1482,7 @@ function useLocalPackage(program) {
1566
1482
  code: "LOCAL_ALEX_C_LINE_ERROR"
1567
1483
  });
1568
1484
  } else {
1569
- const cacheContents = await loadAlexCLineProjectCache();
1485
+ const cacheContents = enableCache ? await loadAlexCLineProjectCache() : {};
1570
1486
  if (!reverse) {
1571
1487
  if (prepareScript) await (0, execa.execa)({ cwd: localPackagePath })`${packageManager} run ${prepareScript}`;
1572
1488
  if (!keepOldTarballs) await removeAllTarballs(localPackagePath, packageName);
@@ -1588,7 +1504,7 @@ function useLocalPackage(program) {
1588
1504
  cwd: process.cwd(),
1589
1505
  stdio: "inherit"
1590
1506
  });
1591
- if (!reverse) {
1507
+ if (!reverse && enableCache) {
1592
1508
  const packageCacheData = {
1593
1509
  ...cacheContents?.useLocalPackage?.dependencies?.[packageName] ?? {},
1594
1510
  previousVersion: dependencies[packageName],
@@ -1605,7 +1521,7 @@ function useLocalPackage(program) {
1605
1521
  }
1606
1522
  }
1607
1523
  });
1608
- } else await createAlexCLineProjectCache({
1524
+ } else if (enableCache) await createAlexCLineProjectCache({
1609
1525
  ...cacheContents ?? {},
1610
1526
  useLocalPackage: {
1611
1527
  ...cacheContents?.useLocalPackage,
@@ -1673,6 +1589,7 @@ function loadCommands(program, commandMap) {
1673
1589
  function createCommands(program) {
1674
1590
  loadCommands(program, {
1675
1591
  artwork,
1592
+ cachePath,
1676
1593
  checkForFileDependencies,
1677
1594
  checkLockfileVersionDiscrepancy,
1678
1595
  checkReleaseNote,
@@ -1723,18 +1640,61 @@ function formatError(error) {
1723
1640
  throw error;
1724
1641
  }
1725
1642
 
1643
+ //#endregion
1644
+ //#region src/cache/global/createAlexCLineGlobalCache.ts
1645
+ async function createAlexCLineGlobalCache(cacheData) {
1646
+ await (0, node_fs_promises.mkdir)(ALEX_C_LINE_GLOBAL_CACHE_DIRECTORY, { recursive: true });
1647
+ await (0, node_fs_promises.writeFile)(ALEX_C_LINE_GLOBAL_CACHE_PATH, JSON.stringify(cacheData, null, 2));
1648
+ }
1649
+
1650
+ //#endregion
1651
+ //#region src/cache/global/types/AlexCLineGlobalCache.ts
1652
+ const alexCLineGlobalCacheSchema = zod.default.looseObject({ updateChecks: zod.default.record(zod.default.string(), zod.default.string()).optional() });
1653
+
1654
+ //#endregion
1655
+ //#region src/cache/global/parseAlexCLineGlobalCache.ts
1656
+ function parseAlexCLineGlobalCache(input) {
1657
+ return (0, _alextheman_utility.parseZodSchema)(alexCLineGlobalCacheSchema, input);
1658
+ }
1659
+
1660
+ //#endregion
1661
+ //#region src/cache/global/loadAlexCLineGlobalCache.ts
1662
+ async function loadAlexCLineGlobalCache() {
1663
+ try {
1664
+ return parseAlexCLineGlobalCache(JSON.parse(await (0, node_fs_promises.readFile)(ALEX_C_LINE_GLOBAL_CACHE_PATH, "utf-8")));
1665
+ } catch (error) {
1666
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return null;
1667
+ throw error;
1668
+ }
1669
+ }
1670
+
1671
+ //#endregion
1672
+ //#region src/utility/updates/runAutomatedUpdateCheck.ts
1673
+ async function runAutomatedUpdateCheck() {
1674
+ try {
1675
+ const cacheData = await loadAlexCLineGlobalCache();
1676
+ const lastChecked = cacheData?.updateChecks?.[version] ? new Date(cacheData?.updateChecks?.[version]) : void 0;
1677
+ const currentDate = /* @__PURE__ */ new Date();
1678
+ if (lastChecked === void 0 || currentDate.getTime() - lastChecked.getTime() >= _alextheman_utility.ONE_DAY_IN_MILLISECONDS) {
1679
+ await checkUpdate({ logNoUpdates: false });
1680
+ await createAlexCLineGlobalCache({
1681
+ ...cacheData ?? {},
1682
+ updateChecks: {
1683
+ ...cacheData?.updateChecks ?? {},
1684
+ [version]: currentDate.toISOString()
1685
+ }
1686
+ });
1687
+ }
1688
+ } catch {}
1689
+ }
1690
+
1726
1691
  //#endregion
1727
1692
  //#region src/index.ts
1728
1693
  (async () => {
1729
1694
  try {
1730
1695
  const program = new commander.Command();
1731
1696
  program.name(name).description(description).version(version);
1732
- if (!(process.env.NODE_ENV === "test" || (0, _alextheman_utility.parseBoolean)(process.env.RUN_END_TO_END ?? "false") || (0, _alextheman_utility.parseBoolean)(process.env.CI ?? "false"))) (0, update_notifier.default)({ pkg: package_default }).notify({ message: `
1733
- ${await createAlexCLineArtwork({ includeColors: Boolean(supports_color.default.stdout) })}
1734
- A new update of \`alex-c-line\` is available!
1735
- {currentVersion} → {latestVersion}
1736
- Run \`{updateCommand}\` to update.
1737
- ` });
1697
+ if (!(process.env.NODE_ENV === "test" || (0, _alextheman_utility.parseBoolean)(process.env.RUN_END_TO_END ?? "false") || (0, _alextheman_utility.parseBoolean)(process.env.CI ?? "false"))) await runAutomatedUpdateCheck();
1738
1698
  createCommands(program);
1739
1699
  await program.parseAsync(process.argv);
1740
1700
  } catch (error) {
package/dist/index.js CHANGED
@@ -1,15 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
- import { DataError, VersionNumber, VersionType, encryptWithKey, fillArray, getStringsAndInterpolations, interpolate, isTemplateStringsArray, kebabToCamel, normaliseIndents, omitProperties, parseBoolean, parseVersionType, parseZodSchema, parseZodSchemaAsync, removeDuplicates, removeUndefinedFromObject, stringifyDotenv } from "@alextheman/utility";
3
+ import { DataError, ONE_DAY_IN_MILLISECONDS, VersionNumber, VersionType, encryptWithKey, fillArray, getStringsAndInterpolations, interpolate, isTemplateStringsArray, kebabToCamel, normaliseIndents, omitProperties, parseBoolean, parseVersionType, parseZodSchema, parseZodSchemaAsync, removeDuplicates, removeUndefinedFromObject, stringifyDotenv } from "@alextheman/utility";
4
4
  import { Command } from "commander";
5
- import supportsColor from "supports-color";
6
- import updateNotifier from "update-notifier";
7
5
  import { createCanvas } from "canvas";
8
6
  import { access, mkdir, readFile, readdir, rename, rm, rmdir, writeFile } from "node:fs/promises";
9
7
  import path from "node:path";
10
8
  import boxen from "boxen";
11
9
  import chalk from "chalk";
12
10
  import figlet from "figlet";
11
+ import envPaths from "env-paths";
13
12
  import { ExecaError, execa } from "execa";
14
13
  import dotenv, { parse } from "dotenv";
15
14
  import dotenvStringify from "dotenv-stringify";
@@ -20,6 +19,7 @@ import { PackageManager, getDependenciesFromGroup, getExpectedTgzName, getPackag
20
19
  import { fileURLToPath, pathToFileURL } from "node:url";
21
20
  import matter from "gray-matter";
22
21
  import { parseFilePath } from "@alextheman/utility/node";
22
+ import supportsColor from "supports-color";
23
23
 
24
24
  //#region src/utility/miscellaneous/asciiToPng.ts
25
25
  async function asciiToPng(ascii, options) {
@@ -91,6 +91,20 @@ function artwork(program) {
91
91
  });
92
92
  }
93
93
 
94
+ //#endregion
95
+ //#region src/cache/global/envPaths.ts
96
+ const alexCLineEnvPaths = envPaths("alex-c-line");
97
+ const { cache: ALEX_C_LINE_GLOBAL_CACHE_DIRECTORY } = alexCLineEnvPaths;
98
+ const ALEX_C_LINE_GLOBAL_CACHE_PATH = path.join(ALEX_C_LINE_GLOBAL_CACHE_DIRECTORY, "cache.json");
99
+
100
+ //#endregion
101
+ //#region src/commands/cache-path.ts
102
+ function cachePath(program) {
103
+ program.command("cache-path").description("Log the path to the alex-c-line cache files.").action(() => {
104
+ console.info(ALEX_C_LINE_GLOBAL_CACHE_PATH);
105
+ });
106
+ }
107
+
94
108
  //#endregion
95
109
  //#region src/commands/check-for-file-dependencies.ts
96
110
  function findFileDependencies(dependencies) {
@@ -866,13 +880,16 @@ const PullRequestTemplateCategory = {
866
880
 
867
881
  //#endregion
868
882
  //#region src/configs/helpers/defineAlexCLinePrivateConfig.ts
869
- const alexCLinePrivateConfigSchema = z.object({ useLocalPackage: z.strictObject({ localPackages: z.record(z.string(), z.strictObject({
870
- packageManager: z.enum(PackageManager),
871
- path: z.string(),
872
- prepareScript: z.string().optional(),
873
- dependencyGroup: z.enum(DependencyGroup).optional(),
874
- keepOldTarballs: z.boolean().optional()
875
- })) }) });
883
+ const alexCLinePrivateConfigSchema = z.object({ useLocalPackage: z.strictObject({
884
+ enableCache: z.boolean().optional(),
885
+ localPackages: z.record(z.string(), z.strictObject({
886
+ packageManager: z.enum(PackageManager),
887
+ path: z.string(),
888
+ prepareScript: z.string().optional(),
889
+ dependencyGroup: z.enum(DependencyGroup).optional(),
890
+ keepOldTarballs: z.boolean().optional()
891
+ }))
892
+ }) });
876
893
  function parseAlexCLinePrivateConfig(data) {
877
894
  return parseZodSchema(alexCLinePrivateConfigSchema, data);
878
895
  }
@@ -1217,121 +1234,35 @@ function setReleaseStatus2(program) {
1217
1234
  });
1218
1235
  }
1219
1236
 
1237
+ //#endregion
1238
+ //#region src/utility/errors/convertDataErrorToProgramError.ts
1239
+ function convertDataErrorToProgramError(dataError, programError, options) {
1240
+ programError(dataError.message, {
1241
+ exitCode: options?.exitCode ?? 1,
1242
+ code: dataError.code
1243
+ });
1244
+ }
1245
+
1246
+ //#endregion
1247
+ //#region src/utility/miscellaneous/parseZodSchemaForProgram.ts
1248
+ function parseZodSchemaForProgram(programError, schema, data) {
1249
+ try {
1250
+ return parseZodSchema(schema, data);
1251
+ } catch (error) {
1252
+ if (DataError.check(error)) convertDataErrorToProgramError(error, programError);
1253
+ throw error;
1254
+ }
1255
+ }
1256
+
1220
1257
  //#endregion
1221
1258
  //#region package.json
1222
1259
  var name = "alex-c-line";
1223
- var version = "1.28.1";
1260
+ var version = "1.29.0";
1224
1261
  var description = "Command-line tool with commands to streamline the developer workflow.";
1225
- var package_default = {
1226
- name,
1227
- version,
1228
- description,
1229
- repository: {
1230
- "type": "git",
1231
- "url": "git+https://github.com/alextheman231/alex-c-line.git"
1232
- },
1233
- sideEffects: ["./dist/index.js"],
1234
- license: "MIT",
1235
- author: "alextheman",
1236
- type: "module",
1237
- exports: {
1238
- "./configs": {
1239
- "types": "./dist/configs/index.d.ts",
1240
- "require": "./dist/configs/index.cjs",
1241
- "import": "./dist/configs/index.js",
1242
- "default": "./dist/configs/index.js"
1243
- },
1244
- "./configs/internal": {
1245
- "types": "./dist/configs/internal/index.d.ts",
1246
- "require": "./dist/configs/internal/index.cjs",
1247
- "import": "./dist/configs/internal/index.js",
1248
- "default": "./dist/configs/internal/index.js"
1249
- }
1250
- },
1251
- bin: { "alex-c-line": "./dist/index.js" },
1252
- files: ["dist", "templates"],
1253
- scripts: {
1254
- "build": "tsdown",
1255
- "command": "bash -c 'pnpm run build && echo && echo \"Command output:\" && node dist/index.js $@' --",
1256
- "create-local-package": "pnpm run build && rm -f alex-c-line-*.tgz && pnpm pack",
1257
- "create-release-note": "bash -c 'git pull origin main && pnpm run command create-release-note-2 $@' --",
1258
- "format": "pnpm run format-prettier && pnpm run format-eslint",
1259
- "format-eslint": "eslint --fix --suppress-all \"package.json\" \"src/**/*.ts\" \"tests/**/*.ts\" && rm -f eslint-suppressions.json",
1260
- "format-prettier": "pnpm run format-prettier-typescript && pnpm run format-prettier-javascript && pnpm run format-prettier-yml",
1261
- "format-prettier-javascript": "prettier --write \"./**/*.js\"",
1262
- "format-prettier-typescript": "prettier --write --parser typescript \"./**/*.ts\"",
1263
- "format-prettier-yml": "prettier --write \"./**/*.{yml,yaml}\"",
1264
- "lint": "pnpm run lint-tsc && pnpm run lint-eslint && pnpm run lint-prettier",
1265
- "lint-eslint": "eslint \"package.json\" \"src/**/*.ts\" \"tests/**/*.ts\"",
1266
- "lint-prettier": "pnpm run lint-prettier-typescript && pnpm run lint-prettier-javascript && pnpm run lint-prettier-yml",
1267
- "lint-prettier-javascript": "prettier --check \"./**.js\"",
1268
- "lint-prettier-typescript": "prettier --check --parser typescript \"./**/*.ts\"",
1269
- "lint-prettier-yml": "prettier --check \"./**/*.{yml,yaml}\"",
1270
- "lint-tsc": "tsc --noEmit",
1271
- "pre-commit": "pnpm run command pre-commit-2",
1272
- "prepare": "husky",
1273
- "prepare-live-eslint-plugin": "pnpm uninstall @alextheman/eslint-plugin && pnpm install --save-dev @alextheman/eslint-plugin",
1274
- "prepare-live-utility": "pnpm uninstall @alextheman/utility && pnpm install @alextheman/utility",
1275
- "prepare-local-eslint-plugin": "dotenv -e .env -- sh -c 'ESLINT_PLUGIN_PATH=${LOCAL_ESLINT_PLUGIN_PATH:-../eslint-plugin}; pnpm --prefix \"$ESLINT_PLUGIN_PATH\" run build && pnpm uninstall @alextheman/eslint-plugin && pnpm install --save-dev file:\"$ESLINT_PLUGIN_PATH\"'",
1276
- "prepare-local-utility": "dotenv -e .env -- sh -c 'UTILITY_PATH=${LOCAL_UTILITY_PATH:-../utility}; pnpm --prefix \"$UTILITY_PATH\" run build && pnpm uninstall @alextheman/utility && pnpm install file:\"$UTILITY_PATH\"'",
1277
- "test": "vitest run",
1278
- "test-end-to-end": "RUN_END_TO_END=true vitest run tests/end-to-end --reporter verbose",
1279
- "test-watch": "vitest",
1280
- "update-dependencies": "pnpm update --latest && pnpm update",
1281
- "use-live-eslint-plugin": "pnpm run prepare-live-eslint-plugin && pnpm run lint",
1282
- "use-live-utility": "pnpm run prepare-live-utility",
1283
- "use-local-eslint-plugin": "pnpm run prepare-local-eslint-plugin && pnpm run lint",
1284
- "use-local-utility": "pnpm run prepare-local-utility"
1285
- },
1286
- dependencies: {
1287
- "@alextheman/utility": "^5.1.4",
1288
- "@inquirer/prompts": "^8.2.1",
1289
- "boxen": "^8.0.1",
1290
- "canvas": "^3.2.1",
1291
- "chalk": "^5.6.2",
1292
- "commander": "^14.0.3",
1293
- "dotenv": "^17.3.1",
1294
- "dotenv-stringify": "^3.0.1",
1295
- "execa": "^9.6.1",
1296
- "figlet": "^1.10.0",
1297
- "gray-matter": "^4.0.3",
1298
- "libsodium-wrappers": "^0.8.2",
1299
- "supports-color": "^10.2.2",
1300
- "update-notifier": "^7.3.1",
1301
- "zod": "^4.3.6"
1302
- },
1303
- devDependencies: {
1304
- "@alextheman/eslint-plugin": "^5.8.1",
1305
- "@commander-js/extra-typings": "^14.0.0",
1306
- "@types/eslint": "^9.6.1",
1307
- "@types/node": "^25.3.0",
1308
- "@types/update-notifier": "^6.0.8",
1309
- "dotenv-cli": "^11.0.0",
1310
- "eslint": "^10.0.1",
1311
- "husky": "^9.1.7",
1312
- "prettier": "^3.8.1",
1313
- "tempy": "^3.2.0",
1314
- "ts-node": "^10.9.2",
1315
- "tsdown": "^0.20.3",
1316
- "typescript": "^5.9.3",
1317
- "typescript-eslint": "^8.56.0",
1318
- "vite-tsconfig-paths": "^6.1.1",
1319
- "vitest": "^4.0.18"
1320
- },
1321
- packageManager: "pnpm@10.30.1+sha512.3590e550d5384caa39bd5c7c739f72270234b2f6059e13018f975c313b1eb9fefcc09714048765d4d9efe961382c312e624572c0420762bdc5d5940cdf9be73a",
1322
- engines: { "node": ">=22.0.0" },
1323
- pnpm: { "onlyBuiltDependencies": [
1324
- "canvas",
1325
- "core-js",
1326
- "esbuild",
1327
- "fsevents",
1328
- "unrs-resolver"
1329
- ] }
1330
- };
1331
1262
 
1332
1263
  //#endregion
1333
- //#region src/commands/update/checkUpdate.ts
1334
- async function checkUpdate(program) {
1264
+ //#region src/utility/updates/checkUpdate.ts
1265
+ async function checkUpdate(options) {
1335
1266
  const currentVersion = new VersionNumber(version);
1336
1267
  const { stdout: npmViewResult } = await execa`npm view alex-c-line version`;
1337
1268
  const latestVersion = new VersionNumber(npmViewResult.trim());
@@ -1351,40 +1282,25 @@ async function checkUpdate(program) {
1351
1282
  return centerLine(line, width);
1352
1283
  }).join("\n")
1353
1284
  });
1354
- if (program) program.error(messageWithArtwork, {
1355
- exitCode: 2,
1356
- code: "OUTDATED_VERSION"
1357
- });
1358
- else console.info(messageWithArtwork);
1359
- } else console.info(`alex-c-line is up to date (${currentVersion}).`);
1360
- }
1361
-
1362
- //#endregion
1363
- //#region src/utility/errors/convertDataErrorToProgramError.ts
1364
- function convertDataErrorToProgramError(dataError, programError, options) {
1365
- programError(dataError.message, {
1366
- exitCode: options?.exitCode ?? 1,
1367
- code: dataError.code
1368
- });
1369
- }
1370
-
1371
- //#endregion
1372
- //#region src/utility/miscellaneous/parseZodSchemaForProgram.ts
1373
- function parseZodSchemaForProgram(programError, schema, data) {
1374
- try {
1375
- return parseZodSchema(schema, data);
1376
- } catch (error) {
1377
- if (DataError.check(error)) convertDataErrorToProgramError(error, programError);
1378
- throw error;
1379
- }
1285
+ if (options?.program) {
1286
+ const { program } = options;
1287
+ program.error(messageWithArtwork, {
1288
+ exitCode: 2,
1289
+ code: "OUTDATED_VERSION"
1290
+ });
1291
+ } else console.info(messageWithArtwork);
1292
+ } else if (options?.logNoUpdates) console.info(`alex-c-line is up to date (${currentVersion}).`);
1380
1293
  }
1381
1294
 
1382
1295
  //#endregion
1383
- //#region src/commands/update/index.ts
1296
+ //#region src/commands/update.ts
1384
1297
  const optionsSchema = z.object({ check: z.boolean().optional() });
1385
1298
  function update(program) {
1386
- program.command("update").description("Handle updates of the currently installed alex-c-line").option("--check", "Check for available updates").action(async (rawOptions) => {
1387
- if (parseZodSchemaForProgram(program.error, optionsSchema, rawOptions).check) await checkUpdate(program);
1299
+ program.command("update").description("Handle updates of the currently installed alex-c-line").option("--check", "Check for available updates").option("--apply", "Apply the latest update").action(async (rawOptions) => {
1300
+ if (parseZodSchemaForProgram(program.error, optionsSchema, rawOptions).check) await checkUpdate({
1301
+ program,
1302
+ logNoUpdates: true
1303
+ });
1388
1304
  else console.info("Unsupported option. Expected `--check`.");
1389
1305
  });
1390
1306
  }
@@ -1408,7 +1324,7 @@ const alexCLineProjectCacheSchema = z.object({ useLocalPackage: z.object({ depen
1408
1324
  })) }).partial() }).partial();
1409
1325
 
1410
1326
  //#endregion
1411
- //#region src/cache/project/parseAlexCLineCache.ts
1327
+ //#region src/cache/project/parseAlexCLineProjectCache.ts
1412
1328
  function parseAlexCLineProjectCache(data) {
1413
1329
  return parseZodSchema(alexCLineProjectCacheSchema, data);
1414
1330
  }
@@ -1491,7 +1407,7 @@ function useLocalPackage(program) {
1491
1407
  exitCode: 1,
1492
1408
  code: "ALEX_C_LINE_PRIVATE_CONFIG_NOT_FOUND"
1493
1409
  });
1494
- const { useLocalPackage: { localPackages } } = await loadAlexCLinePrivateConfig(configPath);
1410
+ const { useLocalPackage: { enableCache, localPackages } } = await loadAlexCLinePrivateConfig(configPath);
1495
1411
  const localPackage = localPackages[packageName];
1496
1412
  if (!localPackage) throw new DataError({
1497
1413
  packageName,
@@ -1528,7 +1444,7 @@ function useLocalPackage(program) {
1528
1444
  code: "LOCAL_ALEX_C_LINE_ERROR"
1529
1445
  });
1530
1446
  } else {
1531
- const cacheContents = await loadAlexCLineProjectCache();
1447
+ const cacheContents = enableCache ? await loadAlexCLineProjectCache() : {};
1532
1448
  if (!reverse) {
1533
1449
  if (prepareScript) await execa({ cwd: localPackagePath })`${packageManager} run ${prepareScript}`;
1534
1450
  if (!keepOldTarballs) await removeAllTarballs(localPackagePath, packageName);
@@ -1550,7 +1466,7 @@ function useLocalPackage(program) {
1550
1466
  cwd: process.cwd(),
1551
1467
  stdio: "inherit"
1552
1468
  });
1553
- if (!reverse) {
1469
+ if (!reverse && enableCache) {
1554
1470
  const packageCacheData = {
1555
1471
  ...cacheContents?.useLocalPackage?.dependencies?.[packageName] ?? {},
1556
1472
  previousVersion: dependencies[packageName],
@@ -1567,7 +1483,7 @@ function useLocalPackage(program) {
1567
1483
  }
1568
1484
  }
1569
1485
  });
1570
- } else await createAlexCLineProjectCache({
1486
+ } else if (enableCache) await createAlexCLineProjectCache({
1571
1487
  ...cacheContents ?? {},
1572
1488
  useLocalPackage: {
1573
1489
  ...cacheContents?.useLocalPackage,
@@ -1635,6 +1551,7 @@ function loadCommands(program, commandMap) {
1635
1551
  function createCommands(program) {
1636
1552
  loadCommands(program, {
1637
1553
  artwork,
1554
+ cachePath,
1638
1555
  checkForFileDependencies,
1639
1556
  checkLockfileVersionDiscrepancy,
1640
1557
  checkReleaseNote,
@@ -1685,18 +1602,61 @@ function formatError(error) {
1685
1602
  throw error;
1686
1603
  }
1687
1604
 
1605
+ //#endregion
1606
+ //#region src/cache/global/createAlexCLineGlobalCache.ts
1607
+ async function createAlexCLineGlobalCache(cacheData) {
1608
+ await mkdir(ALEX_C_LINE_GLOBAL_CACHE_DIRECTORY, { recursive: true });
1609
+ await writeFile(ALEX_C_LINE_GLOBAL_CACHE_PATH, JSON.stringify(cacheData, null, 2));
1610
+ }
1611
+
1612
+ //#endregion
1613
+ //#region src/cache/global/types/AlexCLineGlobalCache.ts
1614
+ const alexCLineGlobalCacheSchema = z.looseObject({ updateChecks: z.record(z.string(), z.string()).optional() });
1615
+
1616
+ //#endregion
1617
+ //#region src/cache/global/parseAlexCLineGlobalCache.ts
1618
+ function parseAlexCLineGlobalCache(input) {
1619
+ return parseZodSchema(alexCLineGlobalCacheSchema, input);
1620
+ }
1621
+
1622
+ //#endregion
1623
+ //#region src/cache/global/loadAlexCLineGlobalCache.ts
1624
+ async function loadAlexCLineGlobalCache() {
1625
+ try {
1626
+ return parseAlexCLineGlobalCache(JSON.parse(await readFile(ALEX_C_LINE_GLOBAL_CACHE_PATH, "utf-8")));
1627
+ } catch (error) {
1628
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return null;
1629
+ throw error;
1630
+ }
1631
+ }
1632
+
1633
+ //#endregion
1634
+ //#region src/utility/updates/runAutomatedUpdateCheck.ts
1635
+ async function runAutomatedUpdateCheck() {
1636
+ try {
1637
+ const cacheData = await loadAlexCLineGlobalCache();
1638
+ const lastChecked = cacheData?.updateChecks?.[version] ? new Date(cacheData?.updateChecks?.[version]) : void 0;
1639
+ const currentDate = /* @__PURE__ */ new Date();
1640
+ if (lastChecked === void 0 || currentDate.getTime() - lastChecked.getTime() >= ONE_DAY_IN_MILLISECONDS) {
1641
+ await checkUpdate({ logNoUpdates: false });
1642
+ await createAlexCLineGlobalCache({
1643
+ ...cacheData ?? {},
1644
+ updateChecks: {
1645
+ ...cacheData?.updateChecks ?? {},
1646
+ [version]: currentDate.toISOString()
1647
+ }
1648
+ });
1649
+ }
1650
+ } catch {}
1651
+ }
1652
+
1688
1653
  //#endregion
1689
1654
  //#region src/index.ts
1690
1655
  (async () => {
1691
1656
  try {
1692
1657
  const program = new Command();
1693
1658
  program.name(name).description(description).version(version);
1694
- if (!(process.env.NODE_ENV === "test" || parseBoolean(process.env.RUN_END_TO_END ?? "false") || parseBoolean(process.env.CI ?? "false"))) updateNotifier({ pkg: package_default }).notify({ message: `
1695
- ${await createAlexCLineArtwork({ includeColors: Boolean(supportsColor.stdout) })}
1696
- A new update of \`alex-c-line\` is available!
1697
- {currentVersion} → {latestVersion}
1698
- Run \`{updateCommand}\` to update.
1699
- ` });
1659
+ if (!(process.env.NODE_ENV === "test" || parseBoolean(process.env.RUN_END_TO_END ?? "false") || parseBoolean(process.env.CI ?? "false"))) await runAutomatedUpdateCheck();
1700
1660
  createCommands(program);
1701
1661
  await program.parseAsync(process.argv);
1702
1662
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alex-c-line",
3
- "version": "1.28.1",
3
+ "version": "1.29.0",
4
4
  "description": "Command-line tool with commands to streamline the developer workflow.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,7 +34,7 @@
34
34
  "templates"
35
35
  ],
36
36
  "dependencies": {
37
- "@alextheman/utility": "^5.1.4",
37
+ "@alextheman/utility": "^5.3.0",
38
38
  "@inquirer/prompts": "^8.2.1",
39
39
  "boxen": "^8.0.1",
40
40
  "canvas": "^3.2.1",
@@ -42,12 +42,12 @@
42
42
  "commander": "^14.0.3",
43
43
  "dotenv": "^17.3.1",
44
44
  "dotenv-stringify": "^3.0.1",
45
+ "env-paths": "^4.0.0",
45
46
  "execa": "^9.6.1",
46
47
  "figlet": "^1.10.0",
47
48
  "gray-matter": "^4.0.3",
48
49
  "libsodium-wrappers": "^0.8.2",
49
50
  "supports-color": "^10.2.2",
50
- "update-notifier": "^7.3.1",
51
51
  "zod": "^4.3.6"
52
52
  },
53
53
  "devDependencies": {