@fulmenhq/tsfulmen 0.2.10 → 0.3.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.
package/CHANGELOG.md CHANGED
@@ -14,6 +14,32 @@ _No unreleased changes._
14
14
 
15
15
  ---
16
16
 
17
+ ## [0.3.0] - 2026-06-06
18
+
19
+ > **Breaking (majors wave).** Migrates four major dependencies (archiver 8, pino 10, TypeScript 6, commander 15) and **raises the Node engine floor to `>=22.12.0`** (was `>=20.0.0`). No external consumers are affected (the galaxy runs Node 22+). Shipped as four reviewed PRs (#6, #9, #10, #11). Full details in `docs/releases/v0.3.0.md`.
20
+
21
+ ### Breaking
22
+
23
+ - **Node engine floor `>=20.0.0` → `>=22.12.0`** (`package.json` `engines.node`). Driven by commander 15 (requires `node>=22.12.0` for `require(esm)`); also clears pino 10's Node-18 drop. Consumers must be on Node 22.12+.
24
+
25
+ ### Security
26
+
27
+ - **archiver 7.0.1 → 8.0.0** — escapes the **unpatchable lodash 4.x advisories** that archiver 7 pulled transitively via `archiver-utils` (GHSA-r5fr-rjxr-66jc `_.template` code injection [high] + two `_.unset`/`_.omit` prototype-pollution advisories [moderate] — none have a fixed lodash 4.x, so dropping lodash was the only remedy). archiver 8 removes `archiver-utils`/`lodash` entirely; its `readdir-glob → minimatch` chain resolves to a patched `minimatch@10.2.x`. Net `bun audit`: **23 → 17** findings (15 → 11 high).
28
+
29
+ ### Changed
30
+
31
+ - **archiver 8 API migration** (`src/fulpack/core.ts`) — archiver 8 is a ground-up ESM rewrite that replaced the callable `archiver(format, options)` factory with format-specific classes. Updated to `new TarArchive(...)` / `new ZipArchive(...)` and the imported `Archiver` type. No public-API or behavior change for fulpack (`create`/`extract`/`scan` unchanged); all 122 fulpack/test files green.
32
+ - **Local archiver v8 type shim** (`src/fulpack/archiver.d.ts`) — archiver 8 ships no types and `@types/archiver` is frozen on the removed v7 factory API, so `@types/archiver` was dropped in favor of a focused ambient `declare module "archiver"` covering the v8 surface fulpack uses. The shim leaks no `archiver` types into the published `dist` surface. **Remove the shim and restore `@types/archiver` once `@types/archiver@8` lands on DefinitelyTyped.**
33
+ - **pino 9.14.0 → 10.3.1** — clean major bump (no source changes). pino 10's only breaking change is dropping Node 18, satisfied by the wave's engine floor; pino remains CJS with a default export, so the `src/logging/logger.ts` integration is unchanged. tsfulmen uses custom redaction (`logging/middleware.ts`), not pino's `redact`/`censor`, so the v10.1.0 censor type change does not apply. Sinks are custom (not pino transports), so the thread-stream@4 / pino-abstract-transport@3 upgrades have no surface here. Added DEBUG/WARN severity-label coverage to complete the logger output assertions across all four methods.
34
+ - **commander 14.0.3 → 15.0.0** — commander 15 is ESM-only and requires `node>=22.12.0` (drives the engine-floor bump above). No source changes: tsfulmen is already ESM and all three CLIs (`foundry/signals`, `schema`, `telemetry/prometheus`) use the stable `import { Command }` API. The `schema export` command defines **standalone** negated flags (`--no-provenance`, `--no-validate`); commander 15's parsing change affects only **paired** positive+negative option definitions (e.g. both `--foo` and `--no-foo`), which tsfulmen does not use, so standalone `--no-*` behavior is unchanged (verified: `schema export --no-provenance --no-validate` works under v15). Validated by smoke-testing the CLIs under v15 at the parse level (help rendering, subcommands, `--json`, the `--no-*` flags above) plus the existing CLI-logic tests. The CLIs `program.parse()` on import, so commander-level arg-parse unit tests would need an `import.meta.main` guard + extracted `buildProgram()` (tracked as a testability follow-up, not part of this bump). _Note: `telemetry/prometheus` `export`/`validate` actions exercise downstream exporter logic that requires a populated `TelemetryRegistry`; those fail in a bare standalone CLI invocation (pre-existing, unrelated to commander — parsing succeeds, the `RefreshError` originates in `exporter.ts`)._
35
+ - **TypeScript (dev) 5.9.3 → 6.0.3** — compiler bump, no `src/` changes. The `tsconfig.json` already sets all options explicitly (`target`/`module`/`lib` ES2022, `moduleResolution: bundler`, `esModuleInterop: true`, `strict`, `skipLibCheck`) and uses none of the options TS 6 removed (`outFile`, `classic`/`node10` resolution, `importsNotUsedAsValues`, etc.), so the default changes don't apply. **Added `ignoreDeprecations: "6.0"`** to `tsconfig.json`: the `.d.ts` build (tsup 8.5.1) injects `baseUrl: "."` into the declaration compiler options (`rollup-plugin-dts` path), and TS 6 errors on the now-deprecated `baseUrl` (`TS5101`) — `ignoreDeprecations` is the TS-sanctioned 6.x-transition opt-out (remove once tsup stops injecting `baseUrl` or before TS 7). Typecheck, full `tsup` `.d.ts` emit, and the `validate:exports`/`validate:types`/`validate:imports` consumer-surface suite all pass under 6.0.3.
36
+
37
+ ### Fixed
38
+
39
+ - **Node 25 test teardown** — `src/config/__tests__/loader.test.ts` used the deprecated `fs.rmdir(path, { recursive: true })`, which is a deprecation warning on Node ≤24 but a hard failure on Node 25.x. Switched to `fs.rm(path, { recursive: true, force: true })` (surfaced during the pino 10 review on Node 25.8.0).
40
+
41
+ ---
42
+
17
43
  ## [0.2.10] - 2026-06-05
18
44
 
19
45
  ### Security
package/README.md CHANGED
@@ -8,7 +8,7 @@ Every team writes their own HTTP status helpers, exit code enums, and country co
8
8
  - **Cross-language parity**: Same exit codes, signals, and schemas as gofulmen, rsfulmen, pyfulmen
9
9
  - **Type-safe**: Full TypeScript types with strict mode throughout
10
10
 
11
- **Lifecycle Phase**: `beta` | **Version**: 0.2.10 | **Test Coverage**: 71%
11
+ **Lifecycle Phase**: `beta` | **Version**: 0.3.0 | **Test Coverage**: 71%
12
12
 
13
13
  **Install**: `bun add @fulmenhq/tsfulmen` (or `npm install @fulmenhq/tsfulmen`)
14
14
 
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, statSync, createReadStream, createWriteStream, r
2
2
  import { dirname, basename, join } from 'path';
3
3
  import { pipeline } from 'stream';
4
4
  import { createGunzip, createGzip } from 'zlib';
5
- import archiver from 'archiver';
5
+ import { TarArchive, ZipArchive } from 'archiver';
6
6
 
7
7
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
8
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
@@ -522,7 +522,7 @@ async function scanGzipFile(archive, options) {
522
522
  }
523
523
  async function createTarGzArchive(sources, output, options) {
524
524
  const writeStream = createWriteStream(output);
525
- const archive = archiver("tar", {
525
+ const archive = new TarArchive({
526
526
  gzip: true,
527
527
  gzipOptions: {
528
528
  level: options.compression_level
@@ -533,14 +533,17 @@ async function createTarGzArchive(sources, output, options) {
533
533
  const writePromise = new Promise((resolve, reject) => {
534
534
  writeStream.on("close", () => resolve());
535
535
  writeStream.on("error", reject);
536
- });
537
- archive.on("error", (error) => {
538
- throw new FulpackOperationError(
539
- createFulpackError(
540
- ERROR_CODES.EXTRACTION_FAILED,
541
- `TAR.GZ creation failed: ${error.message}`,
542
- "create" /* CREATE */,
543
- { details: { original_error: error } }
536
+ archive.on(
537
+ "error",
538
+ (error) => reject(
539
+ new FulpackOperationError(
540
+ createFulpackError(
541
+ ERROR_CODES.EXTRACTION_FAILED,
542
+ `TAR.GZ creation failed: ${error.message}`,
543
+ "create" /* CREATE */,
544
+ { details: { original_error: error } }
545
+ )
546
+ )
544
547
  )
545
548
  );
546
549
  });
@@ -569,7 +572,8 @@ async function createTarGzArchive(sources, output, options) {
569
572
  totalSize += stats.size;
570
573
  }
571
574
  }
572
- await archive.finalize();
575
+ archive.finalize().catch(() => {
576
+ });
573
577
  await writePromise;
574
578
  const outputStats = statSync(output);
575
579
  return {
@@ -586,7 +590,7 @@ async function createTarGzArchive(sources, output, options) {
586
590
  }
587
591
  async function createZipArchive(sources, output, options) {
588
592
  const writeStream = createWriteStream(output);
589
- const archive = archiver("zip", {
593
+ const archive = new ZipArchive({
590
594
  zlib: { level: options.compression_level }
591
595
  });
592
596
  let entryCount = 0;
@@ -594,14 +598,17 @@ async function createZipArchive(sources, output, options) {
594
598
  const writePromise = new Promise((resolve, reject) => {
595
599
  writeStream.on("close", () => resolve());
596
600
  writeStream.on("error", reject);
597
- });
598
- archive.on("error", (error) => {
599
- throw new FulpackOperationError(
600
- createFulpackError(
601
- ERROR_CODES.EXTRACTION_FAILED,
602
- `ZIP creation failed: ${error.message}`,
603
- "create" /* CREATE */,
604
- { details: { original_error: error } }
601
+ archive.on(
602
+ "error",
603
+ (error) => reject(
604
+ new FulpackOperationError(
605
+ createFulpackError(
606
+ ERROR_CODES.EXTRACTION_FAILED,
607
+ `ZIP creation failed: ${error.message}`,
608
+ "create" /* CREATE */,
609
+ { details: { original_error: error } }
610
+ )
611
+ )
605
612
  )
606
613
  );
607
614
  });
@@ -630,7 +637,8 @@ async function createZipArchive(sources, output, options) {
630
637
  totalSize += stats.size;
631
638
  }
632
639
  }
633
- await archive.finalize();
640
+ archive.finalize().catch(() => {
641
+ });
634
642
  await writePromise;
635
643
  const outputStats = statSync(output);
636
644
  return {
@@ -675,7 +683,7 @@ async function addDirectoryToTarGzArchive(archive, dirPath, archivePrefix, optio
675
683
  }
676
684
  async function createTarArchive(sources, output, options) {
677
685
  const writeStream = createWriteStream(output);
678
- const archive = archiver("tar", {
686
+ const archive = new TarArchive({
679
687
  gzip: false
680
688
  // Uncompressed
681
689
  });
@@ -684,14 +692,17 @@ async function createTarArchive(sources, output, options) {
684
692
  const writePromise = new Promise((resolve, reject) => {
685
693
  writeStream.on("close", () => resolve());
686
694
  writeStream.on("error", reject);
687
- });
688
- archive.on("error", (error) => {
689
- throw new FulpackOperationError(
690
- createFulpackError(
691
- ERROR_CODES.EXTRACTION_FAILED,
692
- `TAR creation failed: ${error.message}`,
693
- "create" /* CREATE */,
694
- { details: { original_error: error } }
695
+ archive.on(
696
+ "error",
697
+ (error) => reject(
698
+ new FulpackOperationError(
699
+ createFulpackError(
700
+ ERROR_CODES.EXTRACTION_FAILED,
701
+ `TAR creation failed: ${error.message}`,
702
+ "create" /* CREATE */,
703
+ { details: { original_error: error } }
704
+ )
705
+ )
695
706
  )
696
707
  );
697
708
  });
@@ -720,7 +731,8 @@ async function createTarArchive(sources, output, options) {
720
731
  totalSize += stats.size;
721
732
  }
722
733
  }
723
- await archive.finalize();
734
+ archive.finalize().catch(() => {
735
+ });
724
736
  await writePromise;
725
737
  const outputStats = statSync(output);
726
738
  return {