@akii09/pdfx-cli 0.1.3 → 0.1.4

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 (2) hide show
  1. package/dist/index.js +163 -73
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,6 +6,9 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/index.ts
9
+ import { readFileSync } from "fs";
10
+ import { join } from "path";
11
+ import { fileURLToPath } from "url";
9
12
  import { Command } from "commander";
10
13
 
11
14
  // src/commands/add.ts
@@ -989,11 +992,11 @@ function datetimeRegex(args) {
989
992
  regex = `${regex}(${opts.join("|")})`;
990
993
  return new RegExp(`^${regex}$`);
991
994
  }
992
- function isValidIP(ip, version) {
993
- if ((version === "v4" || !version) && ipv4Regex.test(ip)) {
995
+ function isValidIP(ip, version2) {
996
+ if ((version2 === "v4" || !version2) && ipv4Regex.test(ip)) {
994
997
  return true;
995
998
  }
996
- if ((version === "v6" || !version) && ipv6Regex.test(ip)) {
999
+ if ((version2 === "v6" || !version2) && ipv6Regex.test(ip)) {
997
1000
  return true;
998
1001
  }
999
1002
  return false;
@@ -1020,11 +1023,11 @@ function isValidJWT(jwt, alg) {
1020
1023
  return false;
1021
1024
  }
1022
1025
  }
1023
- function isValidCidr(ip, version) {
1024
- if ((version === "v4" || !version) && ipv4CidrRegex.test(ip)) {
1026
+ function isValidCidr(ip, version2) {
1027
+ if ((version2 === "v4" || !version2) && ipv4CidrRegex.test(ip)) {
1025
1028
  return true;
1026
1029
  }
1027
- if ((version === "v6" || !version) && ipv6CidrRegex.test(ip)) {
1030
+ if ((version2 === "v6" || !version2) && ipv6CidrRegex.test(ip)) {
1028
1031
  return true;
1029
1032
  }
1030
1033
  return false;
@@ -4498,14 +4501,14 @@ function getInstalledVersion(packageName, cwd = process.cwd()) {
4498
4501
  ...pkg.dependencies,
4499
4502
  ...pkg.devDependencies
4500
4503
  };
4501
- const version = deps[packageName];
4502
- if (!version) return null;
4503
- return semver.clean(version) || semver.coerce(version)?.version || null;
4504
+ const version2 = deps[packageName];
4505
+ if (!version2) return null;
4506
+ return semver.clean(version2) || semver.coerce(version2)?.version || null;
4504
4507
  }
4505
4508
  function validateReactPdfRenderer(cwd = process.cwd()) {
4506
- const version = getInstalledVersion("@react-pdf/renderer", cwd);
4509
+ const version2 = getInstalledVersion("@react-pdf/renderer", cwd);
4507
4510
  const required = REQUIRED_VERSIONS["@react-pdf/renderer"];
4508
- if (!version) {
4511
+ if (!version2) {
4509
4512
  return {
4510
4513
  valid: false,
4511
4514
  installed: false,
@@ -4513,19 +4516,19 @@ function validateReactPdfRenderer(cwd = process.cwd()) {
4513
4516
  message: "@react-pdf/renderer is not installed"
4514
4517
  };
4515
4518
  }
4516
- const isCompatible = semver.satisfies(version, required);
4519
+ const isCompatible = semver.satisfies(version2, required);
4517
4520
  return {
4518
4521
  valid: isCompatible,
4519
4522
  installed: true,
4520
- currentVersion: version,
4523
+ currentVersion: version2,
4521
4524
  requiredVersion: required,
4522
- message: isCompatible ? "@react-pdf/renderer version is compatible" : `@react-pdf/renderer version ${version} does not meet requirement ${required}`
4525
+ message: isCompatible ? "@react-pdf/renderer version is compatible" : `@react-pdf/renderer version ${version2} does not meet requirement ${required}`
4523
4526
  };
4524
4527
  }
4525
4528
  function validateReact(cwd = process.cwd()) {
4526
- const version = getInstalledVersion("react", cwd);
4529
+ const version2 = getInstalledVersion("react", cwd);
4527
4530
  const required = REQUIRED_VERSIONS.react;
4528
- if (!version) {
4531
+ if (!version2) {
4529
4532
  return {
4530
4533
  valid: false,
4531
4534
  installed: false,
@@ -4533,26 +4536,26 @@ function validateReact(cwd = process.cwd()) {
4533
4536
  message: "React is not installed"
4534
4537
  };
4535
4538
  }
4536
- const isCompatible = semver.satisfies(version, required);
4539
+ const isCompatible = semver.satisfies(version2, required);
4537
4540
  return {
4538
4541
  valid: isCompatible,
4539
4542
  installed: true,
4540
- currentVersion: version,
4543
+ currentVersion: version2,
4541
4544
  requiredVersion: required,
4542
- message: isCompatible ? "React version is compatible" : `React version ${version} does not meet requirement ${required}`
4545
+ message: isCompatible ? "React version is compatible" : `React version ${version2} does not meet requirement ${required}`
4543
4546
  };
4544
4547
  }
4545
4548
  function validateNodeVersion() {
4546
- const version = process.version;
4549
+ const version2 = process.version;
4547
4550
  const required = REQUIRED_VERSIONS.node;
4548
- const cleanVersion = semver.clean(version);
4551
+ const cleanVersion = semver.clean(version2);
4549
4552
  if (!cleanVersion) {
4550
4553
  return {
4551
4554
  valid: false,
4552
4555
  installed: true,
4553
- currentVersion: version,
4556
+ currentVersion: version2,
4554
4557
  requiredVersion: required,
4555
- message: `Unable to parse Node.js version: ${version}`
4558
+ message: `Unable to parse Node.js version: ${version2}`
4556
4559
  };
4557
4560
  }
4558
4561
  const isCompatible = semver.satisfies(cleanVersion, required);
@@ -4901,7 +4904,9 @@ async function fetchComponent(name, registryUrl) {
4901
4904
  } catch (err) {
4902
4905
  const isTimeout = err instanceof Error && err.name === "TimeoutError";
4903
4906
  throw new NetworkError(
4904
- isTimeout ? "Registry request timed out" : `Could not reach ${registryUrl}`
4907
+ isTimeout ? `Registry request timed out after 10 seconds.
4908
+ ${chalk.dim("Check your internet connection or try again later.")}` : `Could not reach registry at ${registryUrl}
4909
+ ${chalk.dim("Verify the URL is correct and you have internet access.")}`
4905
4910
  );
4906
4911
  }
4907
4912
  if (!response.ok) {
@@ -4991,6 +4996,12 @@ async function installComponent(name, config, force) {
4991
4996
  return;
4992
4997
  }
4993
4998
  async function add(components, options = {}) {
4999
+ if (!components || components.length === 0) {
5000
+ console.error(chalk.red("Error: Component name required"));
5001
+ console.log(chalk.dim("Usage: pdfx add <component...>"));
5002
+ console.log(chalk.dim("Example: pdfx add heading text table\n"));
5003
+ process.exit(1);
5004
+ }
4994
5005
  const reactPdfCheck = validateReactPdfRenderer();
4995
5006
  if (!reactPdfCheck.installed) {
4996
5007
  console.error(chalk.red("\nError: @react-pdf/renderer is not installed\n"));
@@ -5018,6 +5029,9 @@ async function add(components, options = {}) {
5018
5029
  let config;
5019
5030
  try {
5020
5031
  config = readConfig(configPath);
5032
+ if (options.registry) {
5033
+ config = { ...config, registry: options.registry };
5034
+ }
5021
5035
  } catch (error) {
5022
5036
  if (error instanceof ConfigError) {
5023
5037
  console.error(chalk.red(error.message));
@@ -5112,7 +5126,9 @@ async function diff(components) {
5112
5126
  } catch (err) {
5113
5127
  const isTimeout = err instanceof Error && err.name === "TimeoutError";
5114
5128
  throw new NetworkError(
5115
- isTimeout ? "Registry request timed out" : `Could not reach ${config.registry}`
5129
+ isTimeout ? `Registry request timed out after 10 seconds.
5130
+ ${chalk2.dim("Check your internet connection or try again later.")}` : `Could not reach registry at ${config.registry}
5131
+ ${chalk2.dim("Verify the URL is correct and you have internet access.")}`
5116
5132
  );
5117
5133
  }
5118
5134
  if (!response.ok) {
@@ -5147,8 +5163,13 @@ async function diff(components) {
5147
5163
  console.log(chalk2.yellow(` ${fileName}: differs from registry`));
5148
5164
  const localLines = localContent.split("\n");
5149
5165
  const registryLines = registryContent.split("\n");
5166
+ const lineDiff = localLines.length - registryLines.length;
5150
5167
  console.log(chalk2.dim(` Local: ${localLines.length} lines`));
5151
5168
  console.log(chalk2.dim(` Registry: ${registryLines.length} lines`));
5169
+ if (lineDiff !== 0) {
5170
+ const diffText = lineDiff > 0 ? `${Math.abs(lineDiff)} line${Math.abs(lineDiff) > 1 ? "s" : ""} added locally` : `${Math.abs(lineDiff)} line${Math.abs(lineDiff) > 1 ? "s" : ""} removed locally`;
5171
+ console.log(chalk2.dim(` \u2192 ${diffText}`));
5172
+ }
5152
5173
  }
5153
5174
  }
5154
5175
  console.log();
@@ -5368,12 +5389,23 @@ function runPreFlightChecks(cwd = process.cwd()) {
5368
5389
  `${environment.hasPackageJson.message}
5369
5390
  ${chalk4.dim("\u2192")} ${environment.hasPackageJson.fixCommand}`
5370
5391
  );
5371
- }
5372
- if (!environment.isReactProject.valid) {
5392
+ } else if (!environment.isReactProject.valid) {
5373
5393
  blockingErrors.push(
5374
5394
  `${environment.isReactProject.message}
5375
5395
  ${chalk4.dim("\u2192")} ${environment.isReactProject.fixCommand}`
5376
5396
  );
5397
+ } else {
5398
+ if (!dependencies.react.valid && dependencies.react.installed) {
5399
+ warnings.push(
5400
+ `${dependencies.react.message}
5401
+ ${chalk4.dim("\u2192")} Current: ${dependencies.react.currentVersion}, Required: ${dependencies.react.requiredVersion}`
5402
+ );
5403
+ } else if (!dependencies.react.installed) {
5404
+ blockingErrors.push(
5405
+ `${dependencies.react.message}
5406
+ ${chalk4.dim("\u2192")} Install React: npm install react react-dom`
5407
+ );
5408
+ }
5377
5409
  }
5378
5410
  if (!dependencies.nodeJs.valid) {
5379
5411
  blockingErrors.push(
@@ -5382,17 +5414,6 @@ function runPreFlightChecks(cwd = process.cwd()) {
5382
5414
  ${chalk4.dim("\u2192")} Visit https://nodejs.org to upgrade`
5383
5415
  );
5384
5416
  }
5385
- if (!dependencies.react.valid && dependencies.react.installed) {
5386
- warnings.push(
5387
- `${dependencies.react.message}
5388
- ${chalk4.dim("\u2192")} Current: ${dependencies.react.currentVersion}, Required: ${dependencies.react.requiredVersion}`
5389
- );
5390
- } else if (!dependencies.react.installed) {
5391
- blockingErrors.push(
5392
- `${dependencies.react.message}
5393
- ${chalk4.dim("\u2192")} This should have been caught by React project check`
5394
- );
5395
- }
5396
5417
  if (dependencies.reactPdfRenderer.installed && !dependencies.reactPdfRenderer.valid) {
5397
5418
  warnings.push(
5398
5419
  `${dependencies.reactPdfRenderer.message}
@@ -5471,13 +5492,31 @@ async function init() {
5471
5492
  type: "text",
5472
5493
  name: "componentDir",
5473
5494
  message: "Where should we install components?",
5474
- initial: DEFAULTS.COMPONENT_DIR
5495
+ initial: DEFAULTS.COMPONENT_DIR,
5496
+ validate: (value) => {
5497
+ if (!value || value.trim().length === 0) {
5498
+ return "Component directory is required";
5499
+ }
5500
+ if (path7.isAbsolute(value)) {
5501
+ return "Please use a relative path (e.g., ./src/components/pdfx)";
5502
+ }
5503
+ if (!value.startsWith(".")) {
5504
+ return "Path should start with ./ or ../ (e.g., ./src/components/pdfx)";
5505
+ }
5506
+ return true;
5507
+ }
5475
5508
  },
5476
5509
  {
5477
5510
  type: "text",
5478
5511
  name: "registry",
5479
5512
  message: "Registry URL:",
5480
- initial: DEFAULTS.REGISTRY_URL
5513
+ initial: DEFAULTS.REGISTRY_URL,
5514
+ validate: (value) => {
5515
+ if (!value || !value.startsWith("http")) {
5516
+ return "Please enter a valid HTTP(S) URL";
5517
+ }
5518
+ return true;
5519
+ }
5481
5520
  },
5482
5521
  {
5483
5522
  type: "select",
@@ -5506,7 +5545,19 @@ async function init() {
5506
5545
  type: "text",
5507
5546
  name: "themePath",
5508
5547
  message: "Where should we create the theme file?",
5509
- initial: DEFAULTS.THEME_FILE
5548
+ initial: DEFAULTS.THEME_FILE,
5549
+ validate: (value) => {
5550
+ if (!value || value.trim().length === 0) {
5551
+ return "Theme path is required";
5552
+ }
5553
+ if (path7.isAbsolute(value)) {
5554
+ return "Please use a relative path (e.g., ./src/lib/pdfx-theme.ts)";
5555
+ }
5556
+ if (!value.endsWith(".ts") && !value.endsWith(".tsx")) {
5557
+ return "Theme file must have .ts or .tsx extension (e.g., ./src/lib/pdfx-theme.ts)";
5558
+ }
5559
+ return true;
5560
+ }
5510
5561
  }
5511
5562
  ],
5512
5563
  {
@@ -5534,6 +5585,8 @@ async function init() {
5534
5585
  }
5535
5586
  const spinner = ora4("Creating config and theme files...").start();
5536
5587
  try {
5588
+ const componentDirPath = path7.resolve(process.cwd(), answers.componentDir);
5589
+ ensureDir(componentDirPath);
5537
5590
  fs7.writeFileSync(path7.join(process.cwd(), "pdfx.json"), JSON.stringify(config, null, 2));
5538
5591
  const presetName = answers.themePreset || "professional";
5539
5592
  const preset = themePresets[presetName];
@@ -5564,18 +5617,25 @@ import ora5 from "ora";
5564
5617
  var FETCH_TIMEOUT_MS2 = 1e4;
5565
5618
  async function list() {
5566
5619
  const configPath = path8.join(process.cwd(), "pdfx.json");
5567
- if (!checkFileExists(configPath)) {
5568
- console.error(chalk6.red("Error: pdfx.json not found"));
5569
- console.log(chalk6.yellow("Run: pdfx init"));
5570
- process.exit(1);
5571
- }
5572
- const raw = readJsonFile(configPath);
5573
- const configResult = configSchema.safeParse(raw);
5574
- if (!configResult.success) {
5575
- console.error(chalk6.red("Invalid pdfx.json"));
5576
- process.exit(1);
5620
+ let config;
5621
+ let hasLocalProject = false;
5622
+ if (checkFileExists(configPath)) {
5623
+ const raw = readJsonFile(configPath);
5624
+ const configResult = configSchema.safeParse(raw);
5625
+ if (!configResult.success) {
5626
+ console.error(chalk6.red("Invalid pdfx.json"));
5627
+ process.exit(1);
5628
+ }
5629
+ config = configResult.data;
5630
+ hasLocalProject = true;
5631
+ } else {
5632
+ config = {
5633
+ registry: "https://pdfx.akashpise.dev/r",
5634
+ componentDir: "./src/components/pdfx",
5635
+ theme: "./src/lib/pdfx-theme.ts"
5636
+ };
5637
+ console.log(chalk6.dim("No pdfx.json found. Listing components from default registry.\n"));
5577
5638
  }
5578
- const config = configResult.data;
5579
5639
  const spinner = ora5("Fetching component list...").start();
5580
5640
  try {
5581
5641
  let response;
@@ -5586,7 +5646,9 @@ async function list() {
5586
5646
  } catch (err) {
5587
5647
  const isTimeout = err instanceof Error && err.name === "TimeoutError";
5588
5648
  throw new NetworkError(
5589
- isTimeout ? "Registry request timed out" : `Could not reach ${config.registry}`
5649
+ isTimeout ? `Registry request timed out after 10 seconds.
5650
+ ${chalk6.dim("Check your internet connection or try again later.")}` : `Could not reach registry at ${config.registry}
5651
+ ${chalk6.dim("Verify the URL is correct and you have internet access.")}`
5590
5652
  );
5591
5653
  }
5592
5654
  if (!response.ok) {
@@ -5598,18 +5660,22 @@ async function list() {
5598
5660
  throw new RegistryError("Invalid registry format");
5599
5661
  }
5600
5662
  spinner.stop();
5601
- const targetDir = path8.resolve(process.cwd(), config.componentDir);
5602
5663
  const components = result.data.items.filter((item) => item.type === "registry:ui");
5603
5664
  console.log(chalk6.bold(`
5604
5665
  Available Components (${components.length})
5605
5666
  `));
5606
5667
  for (const item of components) {
5607
- const componentSubDir = path8.join(targetDir, item.name);
5608
- const localPath = safePath(componentSubDir, `pdfx-${item.name}.tsx`);
5609
- const installed = checkFileExists(localPath);
5610
- const status = installed ? chalk6.green("[installed]") : chalk6.dim("[not installed]");
5611
- console.log(` ${chalk6.cyan(item.name.padEnd(20))} ${item.description}`);
5612
- console.log(` ${"".padEnd(20)} ${status}`);
5668
+ if (hasLocalProject) {
5669
+ const targetDir = path8.resolve(process.cwd(), config.componentDir);
5670
+ const componentSubDir = path8.join(targetDir, item.name);
5671
+ const localPath = safePath(componentSubDir, `pdfx-${item.name}.tsx`);
5672
+ const installed = checkFileExists(localPath);
5673
+ const status = installed ? chalk6.green("[installed]") : chalk6.dim("[not installed]");
5674
+ console.log(` ${chalk6.cyan(item.name.padEnd(20))} ${item.description}`);
5675
+ console.log(` ${"".padEnd(20)} ${status}`);
5676
+ } else {
5677
+ console.log(` ${chalk6.cyan(item.name.padEnd(20))} ${item.description}`);
5678
+ }
5613
5679
  console.log();
5614
5680
  }
5615
5681
  } catch (error) {
@@ -6016,6 +6082,14 @@ import ora7 from "ora";
6016
6082
  import prompts5 from "prompts";
6017
6083
  import ts from "typescript";
6018
6084
  async function themeInit() {
6085
+ const configPath = path10.join(process.cwd(), "pdfx.json");
6086
+ if (!fs8.existsSync(configPath)) {
6087
+ console.error(chalk8.red("\nError: pdfx.json not found"));
6088
+ console.log(chalk8.yellow("\n PDFx is not initialized in this project.\n"));
6089
+ console.log(chalk8.cyan(" Run: pdfx init"));
6090
+ console.log(chalk8.dim(" This will set up your project configuration and theme.\n"));
6091
+ process.exit(1);
6092
+ }
6019
6093
  console.log(chalk8.bold.cyan("\n PDFx Theme Setup\n"));
6020
6094
  const answers = await prompts5(
6021
6095
  [
@@ -6071,14 +6145,14 @@ async function themeInit() {
6071
6145
  const contextPath = path10.join(path10.dirname(absThemePath), "pdfx-theme-context.tsx");
6072
6146
  fs8.writeFileSync(contextPath, generateThemeContextFile(), "utf-8");
6073
6147
  spinner.succeed(`Created ${themePath} with ${presetName} theme`);
6074
- const configPath = path10.join(process.cwd(), "pdfx.json");
6075
- if (fs8.existsSync(configPath)) {
6148
+ const configPath2 = path10.join(process.cwd(), "pdfx.json");
6149
+ if (fs8.existsSync(configPath2)) {
6076
6150
  try {
6077
- const rawConfig = readJsonFile(configPath);
6151
+ const rawConfig = readJsonFile(configPath2);
6078
6152
  const result = configSchema.safeParse(rawConfig);
6079
6153
  if (result.success) {
6080
6154
  const updatedConfig = { ...result.data, theme: themePath };
6081
- fs8.writeFileSync(configPath, JSON.stringify(updatedConfig, null, 2), "utf-8");
6155
+ fs8.writeFileSync(configPath2, JSON.stringify(updatedConfig, null, 2), "utf-8");
6082
6156
  console.log(chalk8.green(" Updated pdfx.json with theme path"));
6083
6157
  }
6084
6158
  } catch {
@@ -6096,14 +6170,16 @@ async function themeInit() {
6096
6170
  }
6097
6171
  }
6098
6172
  async function themeSwitch(presetName) {
6173
+ const resolvedPreset = presetName === "default" ? "professional" : presetName;
6099
6174
  const validPresets = Object.keys(themePresets);
6100
- if (!validPresets.includes(presetName)) {
6101
- console.error(
6102
- chalk8.red(`Invalid preset: "${presetName}". Valid presets: ${validPresets.join(", ")}`)
6103
- );
6175
+ if (!validPresets.includes(resolvedPreset)) {
6176
+ console.error(chalk8.red(`\u2716 Invalid theme preset: "${presetName}"`));
6177
+ console.log(chalk8.dim(` Available presets: ${validPresets.join(", ")}, default
6178
+ `));
6179
+ console.log(chalk8.dim(" Usage: pdfx theme switch <preset>"));
6104
6180
  process.exit(1);
6105
6181
  }
6106
- const validatedPreset = presetName;
6182
+ const validatedPreset = resolvedPreset;
6107
6183
  const configPath = path10.join(process.cwd(), "pdfx.json");
6108
6184
  if (!fs8.existsSync(configPath)) {
6109
6185
  console.error(chalk8.red('No pdfx.json found. Run "pdfx init" first.'));
@@ -6241,8 +6317,11 @@ async function themeValidate() {
6241
6317
  const parsedTheme = parseThemeObject(absThemePath);
6242
6318
  const result = themeSchema.safeParse(parsedTheme);
6243
6319
  if (!result.success) {
6244
- const message = result.error.issues.map((issue) => issue.message).join(", ");
6245
- spinner.fail(`Theme validation failed: ${message}`);
6320
+ const issues = result.error.issues.map((issue) => ` \u2192 ${chalk8.yellow(issue.path.join("."))}: ${issue.message}`).join("\n");
6321
+ spinner.fail("Theme validation failed");
6322
+ console.log(chalk8.red("\n Missing or invalid fields:\n"));
6323
+ console.log(issues);
6324
+ console.log(chalk8.dim("\n Fix these fields in your theme file and run validate again.\n"));
6246
6325
  process.exit(1);
6247
6326
  }
6248
6327
  spinner.succeed("Theme file is valid");
@@ -6259,9 +6338,20 @@ async function themeValidate() {
6259
6338
 
6260
6339
  // src/index.ts
6261
6340
  var program = new Command();
6262
- program.name("pdfx").description("CLI for PDFx components").version("0.1.0");
6341
+ var __filename = fileURLToPath(import.meta.url);
6342
+ var __dirname = join(__filename, "..");
6343
+ var packageJsonPath = join(__dirname, "../package.json");
6344
+ var version = "0.1.3";
6345
+ try {
6346
+ const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
6347
+ version = pkg.version;
6348
+ } catch {
6349
+ }
6350
+ program.name("pdfx").description("CLI for PDFx components").version(version);
6263
6351
  program.command("init").description("Initialize pdfx in your project").action(init);
6264
- program.command("add <components...>").description("Add components to your project").option("-f, --force", "Overwrite existing files without prompting").action((components, options) => add(components, options));
6352
+ program.command("add <components...>").description("Add components to your project").option("-f, --force", "Overwrite existing files without prompting").option("-r, --registry <url>", "Override registry URL").action(
6353
+ (components, options) => add(components, options)
6354
+ );
6265
6355
  program.command("list").description("List available components from registry").action(list);
6266
6356
  program.command("diff <components...>").description("Compare local components with registry versions").action(diff);
6267
6357
  var themeCmd = program.command("theme").description("Manage PDF themes");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akii09/pdfx-cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "CLI for PDFx components",
5
5
  "type": "module",
6
6
  "bin": {