@fulmenhq/tsfulmen
v0.2.8
Published
TypeScript Fulmen helper library - ergonomic access to Crucible SSOT and core utilities
Maintainers
Readme
tsfulmen
Stop reinventing catalogs. Start shipping.
Every team writes their own HTTP status helpers, exit code enums, and country code lookups. tsfulmen provides production-grade TypeScript implementations derived from a single source of truth—so your TypeScript/Node.js services use the same codes as your Go, Rust, and Python services.
- Zero runtime network calls: All catalogs embedded at build time
- Cross-language parity: Same exit codes, signals, and schemas as gofulmen, rsfulmen, pyfulmen
- Type-safe: Full TypeScript types with strict mode throughout
Lifecycle Phase: beta | Version: 0.2.3 | Test Coverage: 71%
Install: bun add @fulmenhq/tsfulmen (or npm install @fulmenhq/tsfulmen)
📖 Read the complete tsfulmen Overview for comprehensive documentation including module catalog, dependency map, and roadmap.
Who Should Use This
Platform Engineers & SREs: Standardize exit codes across all services so alerting thresholds and runbooks work consistently—whether the service is written in TypeScript, Go, Rust, or Python.
Security & Compliance Teams: Embedded catalogs eliminate network calls for reference data. Audit dependencies with bun pm ls or npm ls.
Polyglot Teams: When your organization runs multiple languages, tsfulmen ensures your Node.js services speak the same language as the rest of your stack. Same HTTP status groupings. Same signal handling semantics. Same error codes.
Full-Stack Teams: Use tsfulmen in both backend Node.js services and frontend applications. Works with Bun, Node.js, Deno, and browser environments.
Features
- ✅ Error Handling - Schema-backed FulmenError with severity levels (43 tests)
- ✅ Fulencode - Canonical encoding/decoding facade for base64, hex, base32, and text formats (40+ tests)
- ✅ Telemetry & Metrics - Counter/gauge/histogram with OTLP export (85 tests)
- ✅ Telemetry Instrumentation - Metrics in config, schema, crucible modules (24 tests)
- ✅ HTTP Server Metrics - Type-safe HTTP instrumentation with Express/Fastify/Bun middleware (40+ tests)
- ✅ FulHash - Fast hashing with XXH3-128 and SHA-256, 22x faster small inputs (157 tests)
- ✅ Fulpack - Archive operations (TAR, TAR.GZ, ZIP, GZIP) with security-first design (20 tests)
- ✅ Progressive Logging - Policy enforcement with Pino profiles, middleware pipeline, secure redaction (121 tests)
- ✅ Crucible Shim - Typed access to synced schemas, docs, and config defaults (96 tests)
- ✅ DocScribe - Document processing with frontmatter parsing (50+ tests)
- ✅ Config Path API - XDG-compliant configuration directory resolution (26 tests)
- ✅ Schema Validation - JSON Schema 2020-12 validation with AJV and CLI (115 tests)
- ✅ Foundry Module - Pattern catalogs, HTTP statuses, MIME detection, similarity (278 tests)
- ✅ Exit Codes - Standardized process exit codes with simplified modes and platform detection (34 tests)
- ✅ Signal Handling - Cross-platform signal handling with graceful shutdown and Windows fallback (180 tests)
- ✅ Application Identity - .fulmen/app.yaml discovery with caching and validation (93 tests)
- ✅ Pathfinder - Filesystem traversal, repository root discovery with security boundaries, checksums, and observability (70 tests)
- ✅ Three-Layer Config Loading - Defaults → User → Env pattern with schema validation (6 tests)
- ✅ Workhorse Control-Plane - Config reload endpoint, control discovery, runtime info for service orchestration
Installation
bun add @fulmenhq/tsfulmen
# or
npm install @fulmenhq/tsfulmenDevelopment Setup
Prerequisites
- Bun >= 1.0.0
- Git
- Access to sibling
cruciblerepository (for SSOT sync)
Quick Start
# Clone repository
git clone https://github.com/fulmenhq/tsfulmen.git
cd tsfulmen
# Bootstrap (install deps + tools)
make bootstrap
# Sync assets from Crucible
make sync-ssot
# Run tests
make test
# Build library
make buildMakefile Targets
TSFulmen follows the FulmenHQ Makefile Standard. All CI/CD operations use make targets, not package.json scripts.
Required Targets
make help- Show all available targetsmake bootstrap- Install dependencies and external toolsmake sync-ssot- Sync assets from Crucible SSOTmake tools- Verify external tools are installedmake test- Run test suitemake build- Build distributable artifactsmake lint- Run linting checksmake fmt- Apply code formattingmake typecheck- Run TypeScript type checkingmake check-all- Run all quality checks (lint + typecheck + test)make clean- Remove build artifacts
Version Management
make version- Print current versionmake version-bump-patch- Bump patch versionmake version-bump-minor- Bump minor versionmake version-bump-major- Bump major versionmake version-bump-calver- Bump to CalVer
Architecture
TSFulmen implements the Fulmen Helper Library Standard and mirrors capabilities from gofulmen.
See TSFulmen Overview for complete architecture documentation, module catalog, and dependency map.
Module Structure
src/
├── config/ # ✅ Config path API (XDG-compliant directories)
├── crucible/ # 🚧 Crucible SSOT shim
├── errors/ # ✅ Error handling & propagation
├── foundry/ # ✅ Pattern catalogs, HTTP statuses, MIME detection
├── fulencode/ # ✅ Canonical encoding/decoding (base64, hex, base32, text)
├── fulhash/ # ✅ Fast hashing with XXH3-128 and SHA-256
├── fulpack/ # ✅ Archive operations (TAR, TAR.GZ, ZIP, GZIP)
├── logging/ # ✅ Progressive logging with policy enforcement
├── pathfinder/ # ✅ Filesystem traversal with checksums and observability
├── schema/ # ✅ Schema validation (AJV + CLI, multi-dialect)
└── telemetry/ # ✅ Metrics collection & exportSSOT Sync Model
Assets from Crucible are synced via goneat and committed to version control:
docs/crucible-ts/ # Synced documentation
schemas/crucible-ts/ # Synced JSON schemas
config/crucible-ts/ # Synced config defaults
.crucible/metadata/ # Sync metadataSync configuration: .goneat/ssot-consumer.yaml
See Sync Model Architecture for details.
Usage
Crucible Assets (Docs / Schemas / Config)
TSFulmen’s Crucible shim reads SSOT assets from your repository checkout. Your repo must contain synced Crucible directories:
docs/crucible-ts/schemas/crucible-ts/config/crucible-ts/
Sync them via goneat ssot sync (or make sync-ssot in this repo).
import {
getCrucibleVersion,
getDocumentationWithMetadata,
getConfigDefaults,
listSchemas,
loadSchemaById,
} from "@fulmenhq/tsfulmen/crucible";
console.log("Crucible version:", getCrucibleVersion());
// Docs IDs include .md
const { content, metadata } = await getDocumentationWithMetadata(
"standards/library/modules/app-identity.md",
);
// Schema IDs do NOT include extensions
const schema = await loadSchemaById(
"observability/logging/v1.0.0/logging-policy",
);
const defaults = await getConfigDefaults("library", "v1.0.0");
const schemaSummaries = await listSchemas("observability");
console.log(schemaSummaries.length);If your app does not run with the repo root as process.cwd(), resolve it first:
import { findRepositoryRoot, GitMarkers } from "@fulmenhq/tsfulmen/pathfinder";
const repoRoot = await findRepositoryRoot(process.cwd(), GitMarkers);
process.chdir(repoRoot);See docs/guides/crucible-assets.md for deeper guidance.
Application Identity
Load application identity from .fulmen/app.yaml for vendor/app-specific configuration:
import {
loadIdentity,
getBinaryName,
buildEnvVar,
} from "@fulmenhq/tsfulmen/appidentity";
// Load identity (cached after first call)
const identity = await loadIdentity();
console.log(`Binary: ${identity.app.binary_name}`);
console.log(`Vendor: ${identity.app.vendor}`);
// Build environment variable names
const dbUrl = await buildEnvVar("database-url");
// Returns: 'MYAPP_DATABASE_URL' (hyphens normalized to underscores)
// Get env var value with prefix
const logLevel = await getEnvVar("log_level", { defaultValue: "info" });Discovery order:
- Explicit path:
loadIdentity({ path: '/path/to/app.yaml' }) - Environment variable:
FULMEN_APP_IDENTITY_PATH=/path/to/app.yaml - Ancestor search: walks upward from CWD to find
.fulmen/app.yaml
CLI commands:
# Show identity details
bunx tsfulmen-schema identity-show
bunx tsfulmen-schema identity-show --json
bunx tsfulmen-schema identity-show --path /custom/path/app.yaml
# Validate identity file
bunx tsfulmen-schema identity-validate
bunx tsfulmen-schema identity-validate fixtures/app.yaml
# Validate in CI
make validate-app-identitySee App Identity Standard for schema details.
Error Handling
import { FulmenError } from "@fulmenhq/tsfulmen/errors";
// Wrap existing errors
try {
await loadConfig();
} catch (err) {
throw FulmenError.fromError(err, {
code: "CONFIG_LOAD_FAILED",
severity: "high",
context: { file: "/path/to/config.json" },
});
}
// Create structured errors
const error = new FulmenError({
code: "VALIDATION_FAILED",
message: "Schema validation failed",
severity: "medium",
correlation_id: "550e8400-e29b-41d4-a716-446655440000",
context: { schema_id: "metrics-event", error_count: 3 },
});Fulencode (Encoding/Decoding)
Canonical encoding/decoding facade for binary and text formats:
import { fulencode } from "@fulmenhq/tsfulmen/fulencode";
// Encode binary to base64
const encoded = await fulencode.encode(
new Uint8Array([72, 101, 108, 108, 111]),
"base64",
);
console.log(encoded.data); // "SGVsbG8="
// Decode base64 to binary
const decoded = await fulencode.decode("SGVsbG8=", "base64");
console.log(new TextDecoder().decode(decoded.data)); // "Hello"
// Hex encoding with checksum
const hex = await fulencode.encode(data, "hex", {
hexCase: "upper",
computeChecksum: "sha256",
});
console.log(hex.data); // "48656C6C6F"
console.log(hex.checksum); // "sha256:2cf24dba..."Supported Formats:
| Binary | Text | | ----------------- | ------------------ | | base64 | utf-8 | | base64url | utf-16le, utf-16be | | base64_raw | iso-8859-1, ascii | | hex | | | base32, base32hex | |
Features: Padding control, line wrapping, whitespace handling, checksum computation (sha256, xxh3-128), structured FulencodeError with error codes.
Telemetry & Metrics
import { metrics } from "@fulmenhq/tsfulmen/telemetry";
// Counter: monotonic incrementing values
metrics.counter("schema_validations").inc();
metrics.counter("config_load_errors").inc(2);
// Gauge: arbitrary values
metrics.gauge("foundry_lookup_count").set(42);
// Histogram: distribution with automatic bucketing
const startTime = performance.now();
await performOperation();
metrics
.histogram("operation_duration_ms")
.observe(performance.now() - startTime);
// Export all metrics
const events = await metrics.export();
// Flush: export and clear
await metrics.flush({
emit: (events) => logger.info({ metrics: events }),
});HTTP Server Metrics
Type-safe HTTP instrumentation with automatic route normalization and cardinality protection:
import {
recordHttpRequest,
trackActiveRequest,
createHttpMetricsMiddleware,
} from "@fulmenhq/tsfulmen/telemetry/http";
// Express middleware (automatic instrumentation)
app.use(
createHttpMetricsMiddleware({
serviceName: "api-server",
routeNormalizer: (req) => req.route?.path || normalizeRoute(req.path),
trackBodySizes: true, // Optional: track request/response sizes
}),
);
// Manual instrumentation
const start = performance.now();
const release = trackActiveRequest("api-server");
try {
await handleRequest();
recordHttpRequest({
method: "GET",
route: "/users/:id", // Pre-normalized route
status: 200,
durationMs: performance.now() - start,
requestBytes: 512,
responseBytes: 2048,
});
} finally {
release();
}Metrics emitted (Crucible v0.2.18 taxonomy):
http_requests_total- Request counter with method/route/status/service labelshttp_request_duration_seconds- Duration histogram (auto-converts ms → seconds)http_request_size_bytes- Request size histogram (optional)http_response_size_bytes- Response size histogram (optional)http_active_requests- Active requests gauge
⚠️ Critical: Routes must be normalized to prevent cardinality explosion. Use normalizeRoute() or framework route templates. See HTTP Metrics Guide.
Framework support: Express, Fastify, Bun.serve, Node.js HTTP
Progressive Logging
TSFulmen provides profile-based logging from simple CLI tools to enterprise observability with secure redaction middleware.
import {
createSimpleLogger,
createStructuredLogger,
createStructuredLoggerWithRedaction,
} from "@fulmenhq/tsfulmen/logging";
// Simple: Human-readable for CLI tools
const cli = createSimpleLogger("mycli");
cli.info("Processing file", { path: "/data/input.csv" });
// Structured: JSON output for APIs
const api = createStructuredLogger("api-server");
api.info("Request processed", { requestId: "req-456", duration: 42 });
// Structured + Redaction: Secure by default
const secure = createStructuredLoggerWithRedaction("auth-service");
secure.info("User login", {
userId: "user-123",
email: "[email protected]", // ← Automatically redacted
password: "secret123", // ← Automatically redacted
apiKey: "sk_live_1234567890", // ← Automatically redacted
});Output (with redaction):
{
"severity": "INFO",
"timestamp": "2025-11-18T12:00:00.000Z",
"service": "auth-service",
"message": "User login",
"userId": "user-123",
"email": "[REDACTED]",
"password": "[REDACTED]",
"apiKey": "[REDACTED]"
}Security Model: createStructuredLoggerWithRedaction() enables redaction by default with gofulmen-aligned patterns for common secrets (passwords, tokens, API keys, emails, credit cards).
Performance: Pattern scanning automatically skips strings larger than 10KB to avoid performance impact on large payloads. Field-based redaction (O(1) lookup) always applies.
Customization:
// Add organization-specific patterns
const logger = createStructuredLoggerWithRedaction("api-server", {
customPatterns: [/INTERNAL_ID_\d+/g, /CUST_[A-Z0-9]{10}/g],
customFields: ["internalKey", "customerSecret"],
});
// Opt-out of defaults for full control
const customLogger = createStructuredLoggerWithRedaction("api-server", {
useDefaultPatterns: false,
customPatterns: [/MY_SECRET/g],
});Child Loggers: Child loggers inherit middleware and bindings from parent.
const parent = createStructuredLoggerWithRedaction("api-server");
const child = parent.child({ requestId: "req-789" });
child.info("Processing", { password: "secret" }); // ← Still redacted!See Logging README for complete documentation including middleware, profiles, and policy enforcement.
Config Path API
import { getAppConfigDir, getFulmenConfigDir } from "@fulmenhq/tsfulmen/config";
// Get XDG-compliant config directory for your app
const configDir = getAppConfigDir("myapp");
// ~/.config/myapp (Linux/macOS) or %APPDATA%\myapp (Windows)
// Get Fulmen ecosystem config directory
const fulmenDir = getFulmenConfigDir();
// ~/.config/fulmenThree-Layer Configuration
Enterprise-grade configuration loader implementing the Defaults → User Config → Environment Variables pattern.
import { loadConfig, ConfigValidationError } from "@fulmenhq/tsfulmen/config";
import { loadIdentity } from "@fulmenhq/tsfulmen/appidentity";
import { join } from "node:path";
// 1. Load Identity
const identity = await loadIdentity();
// 2. Load Config
const { config, metadata } = await loadConfig<AppConfig>({
identity,
defaultsPath: join(__dirname, "defaults.yaml"),
schemaPath: join(__dirname, "schema.json"), // Optional validation
});
console.log(config); // Merged configuration
console.log(metadata.activeLayers); // ["defaults", "user", "env"]Features:
- Defaults: Required base configuration file
- User Config: Optional overrides from XDG-compliant paths (YAML/JSON)
- Env Vars: Optional overrides via prefixed environment variables (e.g.
MYAPP_SERVER_PORT=9000->server.port=9000) - Validation: Schema validation via AJV (if schemaPath provided)
- Metadata: Traceability of active layers and paths
Schema Validation
Multi-dialect JSON Schema validation supporting draft-04, draft-06, draft-07, draft-2019-09, and draft-2020-12:
import {
getGlobalRegistry,
compileSchemaById,
validateDataBySchemaId,
validateFileBySchemaId,
validateSchema,
normalizeSchema,
} from "@fulmenhq/tsfulmen/schema";
// List available schemas from Crucible SSOT
const registry = getGlobalRegistry();
const schemas = registry.listSchemas("config/");
// Validate data against a schema
const result = await validateDataBySchemaId(
"config/sync-consumer-config",
configData,
);
if (!result.valid) {
console.error("Validation errors:", result.errors);
}
// Validate a file (JSON or YAML)
const fileResult = await validateFileBySchemaId(
"config/sync-consumer-config",
"./my-config.yaml",
);
// Validate a schema document against its declared dialect
const schemaResult = await validateSchema({
$schema: "http://json-schema.org/draft-07/schema#",
type: "object",
properties: { name: { type: "string" } },
});
// Normalize schema for comparison
const normalized = await normalizeSchema("./schema.yaml");Supported Dialects: draft-04, draft-06, draft-07, draft-2019-09, draft-2020-12. Dialect is auto-detected from the schema's $schema field (defaults to draft-2020-12).
Schema Validation CLI (Developer Tool)
TSFulmen includes a CLI for schema exploration and validation during development:
# List available schemas
bunx tsfulmen-schema list
bunx tsfulmen-schema list config/
# Show schema details
bunx tsfulmen-schema show --id config/sync-consumer-config
# Validate data against schema
bunx tsfulmen-schema validate --schema-id config/sync-consumer-config config.yaml
# Validate schema document itself
bunx tsfulmen-schema validate-schema ./my-schema.json
# Normalize schema for comparison
bunx tsfulmen-schema normalize schema.yaml
# Compare AJV vs goneat validation (requires goneat installed)
bunx tsfulmen-schema compare --schema-id config/sync-consumer-config config.yaml
# Export schema with provenance metadata
bunx tsfulmen-schema export \
--schema-id library/foundry/v1.0.0/exit-codes \
--out vendor/crucible/schemas/exit-codes.schema.jsonNote: The CLI is a developer aid for exploring schemas and debugging validation. Production applications should use the library API directly.
Schema Export
Export schemas from the Crucible registry with embedded provenance metadata for dual-hosting or downstream tooling.
import { exportSchema } from "@fulmenhq/tsfulmen/schema";
await exportSchema({
schemaId: "library/foundry/v1.0.0/exit-codes",
outPath: "vendor/crucible/schemas/exit-codes.schema.json",
includeProvenance: true, // default: true
validate: true, // default: true
});The export writes a deterministic schema file and stores provenance (Crucible version, git revision, export timestamp) under the $comment["x-crucible-source"] key. To compare exports against the runtime registry, call stripProvenance() before diffing:
import { readFile } from "node:fs/promises";
import { stripProvenance } from "@fulmenhq/tsfulmen/schema";
const exported = await readFile(
"vendor/crucible/schemas/exit-codes.schema.json",
"utf-8",
);
const normalized = stripProvenance(exported);The CLI exposes the same functionality for quick workflows:
bunx tsfulmen-schema export \
--schema-id library/foundry/v1.0.0/exit-codes \
--out vendor/crucible/schemas/exit-codes.schema.yaml \
--format yaml \
--no-provenance # optional
--force # overwrite existing fileExit Codes
TSFulmen provides standardized process exit codes (54 codes across 11 categories) from the Crucible Foundry catalog:
import { exitCodes, getExitCodeInfo } from "@fulmenhq/tsfulmen/foundry";
// Use specific exit codes for observable failures
async function main() {
const config = await loadConfig();
if (!config) {
console.error("Configuration validation failed");
process.exit(exitCodes.EXIT_CONFIG_INVALID); // 20
}
const server = await startServer(config.port);
if (!server) {
console.error("Port already in use");
process.exit(exitCodes.EXIT_PORT_IN_USE); // 10
}
process.exit(exitCodes.EXIT_SUCCESS); // 0
}Simplified Modes for basic CLI tools or Windows compatibility:
import {
exitCodes,
mapExitCodeToSimplified,
SimplifiedMode,
supportsSignalExitCodes,
} from "@fulmenhq/tsfulmen/foundry";
let exitCode = exitCodes.EXIT_CONFIG_INVALID; // 20
// BASIC mode: collapse to 0 (success) or 1 (failure)
const basicCode = mapExitCodeToSimplified(exitCode, SimplifiedMode.BASIC);
// Returns: 1 (failure)
// SEVERITY mode: categorize by retry strategy
const severityCode = mapExitCodeToSimplified(exitCode, SimplifiedMode.SEVERITY);
// Returns: 2 (config error - fix before retry)
// Platform capability detection
if (!supportsSignalExitCodes()) {
// On Windows: use simplified mode since signal codes (128+) don't apply
exitCode = mapExitCodeToSimplified(exitCode, SimplifiedMode.BASIC);
}Exit Code Metadata for observability and retry logic:
import { getExitCodeInfo, exitCodes } from "@fulmenhq/tsfulmen/foundry";
const info = getExitCodeInfo(exitCodes.EXIT_DATABASE_UNAVAILABLE);
console.log(info.code); // 31
console.log(info.name); // "EXIT_DATABASE_UNAVAILABLE"
console.log(info.category); // "runtime"
console.log(info.retryHint); // "retry" (safe to retry)
// Use retry hint for automation
if (info.retryHint === "retry") {
await retryWithBackoff();
} else if (info.retryHint === "no_retry") {
logger.error("Permanent failure, manual intervention required");
}Categories: standard (0-1), networking (10-19), configuration (20-29), runtime (30-39), usage (40-49), permissions (50-59), data (60-69), security (70-79), observability (80-89), testing (91-99), signals (128-165)
See Exit Codes Application Guide for complete documentation.
Signals CLI (Developer Tool)
TSFulmen includes a CLI for signal catalog exploration and debugging:
# List all signals with platform support
bunx tsx src/foundry/signals/cli.ts show
# Show specific signal details
bunx tsx src/foundry/signals/cli.ts show SIGTERM
bunx tsx src/foundry/signals/cli.ts show HUP # Name normalization supported
# Show behaviors
bunx tsx src/foundry/signals/cli.ts show --behaviors
bunx tsx src/foundry/signals/cli.ts show graceful_shutdown --behaviors
# Show platform capabilities
bunx tsx src/foundry/signals/cli.ts platform
bunx tsx src/foundry/signals/cli.ts platform --json
# Validate signal configuration file
bunx tsx src/foundry/signals/cli.ts validate config/signals.yamlMakefile Targets:
# Validate signal catalog integrity
make validate-signals
# Verify signals parity with Crucible
make verify-signals-parityNote: The CLI is a developer tool for exploring the signal catalog and debugging configurations. Production applications should use the library API directly (@fulmenhq/tsfulmen/signals).
MIME Type Detection
TSFulmen provides content-based MIME type detection using magic numbers and heuristic analysis:
import {
detectMimeType,
detectMimeTypeFromFile,
detectMimeTypeFromBuffer,
getMimeTypeByExtension,
} from "@fulmenhq/tsfulmen/foundry";
// Detect from buffer (magic number detection)
const buffer = Buffer.from('{"key": "value"}');
const type = await detectMimeType(buffer);
console.log(type?.mime); // 'application/json'
// Detect from file path
const fileType = await detectMimeTypeFromFile("./data.yaml");
console.log(fileType?.mime); // 'application/yaml'
// Detect from stream
const stream = fs.createReadStream("./document.xml");
const streamType = await detectMimeType(stream);
console.log(streamType?.mime); // 'application/xml'
// Extension-based lookup (fast, no content analysis)
const csvType = await getMimeTypeByExtension(".csv");
console.log(csvType?.mime); // 'text/csv'Supported Formats:
- JSON: Magic number detection (
{,[) - YAML: Magic number detection (
---,%YAML) - XML: Magic number detection (
<?xml) - NDJSON: Heuristic detection (newline-delimited JSON)
- CSV: Heuristic detection (consistent delimiters)
- Protocol Buffers: Heuristic detection (binary format)
- Plain Text: Heuristic detection (UTF-8/ASCII)
Detection Options:
const type = await detectMimeType(buffer, {
bytesToRead: 512, // Bytes to analyze (default: 512)
fallbackToExtension: true, // Use extension hint if magic fails
extensionHint: ".json", // Extension for fallback
});Fulpack - Archive Operations
Secure archive creation, extraction, and inspection with security-first design for TAR, TAR.GZ, ZIP, and GZIP formats.
Features:
- Five canonical operations:
create(),extract(),scan(),verify(),info() - Four archive formats: TAR (uncompressed), TAR.GZ (gzip), ZIP (deflate), GZIP (single file)
- Security-first design: path traversal protection, decompression bomb detection, symlink safety validation
- Checksum verification via fulhash integration (SHA-256, xxh3-128)
- Pathfinder integration:
scan()backend for archive discovery - Streaming architecture for memory-efficient operations
Basic Usage:
import {
create,
extract,
scan,
verify,
info,
ArchiveFormat,
} from "@fulmenhq/tsfulmen/fulpack";
// Create TAR.GZ archive
const archiveInfo = await create(
"./src", // Source directory
"./dist/app.tar.gz", // Output archive
ArchiveFormat.TAR_GZ, // Format
{ compression_level: 9 }, // Options
);
// Extract archive with security checks
const result = await extract(
"./dist/app.tar.gz", // Archive path
"./output", // Destination
{ overwrite: "skip" }, // Skip existing files
);
console.log(
`Extracted ${result.extracted_count} files, skipped ${result.skipped_count}`,
);Scan Archives (Pathfinder Integration):
// Scan archive contents without extraction
const entries = await scan("./data.tar.gz");
// Filter entries using standard JavaScript
const csvFiles = entries.filter(
(e) => e.type === "file" && e.path.endsWith(".csv"),
);
console.log(`Found ${csvFiles.length} CSV files`);
// Check for specific files
const hasConfig = entries.some((e) => e.path === "config/app.json");
// Get total uncompressed size
const totalSize = entries.reduce((sum, e) => sum + e.size, 0);
console.log(`Total size: ${(totalSize / 1024 / 1024).toFixed(2)} MB`);Security Validation:
// Verify archive integrity before extraction
const validation = await verify("./untrusted.tar.gz");
if (!validation.valid) {
console.error("Archive validation failed:");
validation.errors.forEach((err) =>
console.error(` - ${err.code}: ${err.message}`),
);
process.exit(1);
}
// Check security validations performed
console.log("Security checks:", validation.checks_performed.join(", "));
// Output: structure_valid, no_path_traversal, symlinks_safe, no_decompression_bombArchive Metadata:
// Get quick metadata without extraction
const metadata = await info("./backup.tar.gz");
console.log(`Format: ${metadata.format}`);
console.log(`Entries: ${metadata.entry_count}`);
console.log(
`Compressed: ${(metadata.compressed_size / 1024 / 1024).toFixed(2)} MB`,
);
console.log(
`Uncompressed: ${(metadata.total_size / 1024 / 1024).toFixed(2)} MB`,
);
console.log(`Ratio: ${metadata.compression_ratio.toFixed(2)}:1`);Format Selection:
// TAR (uncompressed) - Fastest for pre-compressed data
await create("./images", "./backup.tar", ArchiveFormat.TAR);
// TAR.GZ - General purpose, best compatibility
await create("./src", "./release.tar.gz", ArchiveFormat.TAR_GZ, {
compression_level: 9,
exclude_patterns: ["**/*.test.ts", "**/node_modules/**"],
});
// ZIP - Windows compatibility, random access
await create(["./config", "./scripts"], "./deploy.zip", ArchiveFormat.ZIP);
// GZIP - Single file compression
await create("./large-data.csv", "./large-data.csv.gz", ArchiveFormat.GZIP);Pathfinder - Repository Root Discovery
Find repository markers (.git, package.json, etc.) by walking up the directory tree with security-first design, boundary enforcement, and comprehensive error handling.
Features:
- Security-first design with boundary enforcement (home directory/explicit/filesystem root ceilings)
- Max depth limiting (default 10 levels) to prevent excessive traversal
- Symlink loop detection with opt-in following
- Path constraint validation for workspace boundaries
- Cross-platform support (POSIX root, Windows drives, UNC paths)
- Predefined marker sets for common ecosystems (Git, Node, Python, Go, Monorepo)
- Structured errors with context for debugging
Basic Usage:
import {
findRepositoryRoot,
GitMarkers,
NodeMarkers,
} from "@fulmenhq/tsfulmen/pathfinder";
// Find Git repository root from current directory
const gitRoot = await findRepositoryRoot(process.cwd(), GitMarkers);
console.log(`Git root: ${gitRoot}`);
// Find Node.js project root
const nodeRoot = await findRepositoryRoot("./src/components", NodeMarkers);
console.log(`Node root: ${nodeRoot}`);Predefined Marker Sets:
import {
GitMarkers, // [".git"]
NodeMarkers, // ["package.json", "package-lock.json"]
PythonMarkers, // ["pyproject.toml", "setup.py", "requirements.txt", "Pipfile"]
GoModMarkers, // ["go.mod"]
MonorepoMarkers, // ["lerna.json", "pnpm-workspace.yaml", "nx.json", "turbo.json", "rush.json"]
} from "@fulmenhq/tsfulmen/pathfinder";Security Boundaries:
import {
ConstraintType,
EnforcementLevel,
} from "@fulmenhq/tsfulmen/pathfinder";
// Secure search within project boundary
const root = await findRepositoryRoot("./src/components", GitMarkers, {
boundary: "/home/user/projects/myapp", // Don't search above project
constraint: {
root: "/home/user/projects", // Enforce workspace constraint
type: ConstraintType.WORKSPACE,
enforcementLevel: EnforcementLevel.STRICT,
},
});Monorepo Support:
// Find deepest marker (closest to filesystem root) for monorepo roots
// Directory: /monorepo/.git and /monorepo/packages/app/.git
// stopAtFirst=true (default) - finds /monorepo/packages/app
const packageRoot = await findRepositoryRoot(
"/monorepo/packages/app/src/index.ts",
GitMarkers,
);
// stopAtFirst=false - finds /monorepo (deepest/monorepo root)
const monorepoRoot = await findRepositoryRoot(
"/monorepo/packages/app/src/index.ts",
GitMarkers,
{ stopAtFirst: false },
);Error Handling:
import { PathfinderErrorCode } from "@fulmenhq/tsfulmen/pathfinder";
try {
const root = await findRepositoryRoot("./src", GitMarkers);
console.log(`Found: ${root}`);
} catch (error) {
if (error.data?.code === PathfinderErrorCode.REPOSITORY_NOT_FOUND) {
console.error("No repository marker found");
console.error(`Searched from: ${error.data.context.startPath}`);
console.error(`Max depth: ${error.data.context.maxDepth}`);
} else if (error.data?.code === PathfinderErrorCode.TRAVERSAL_LOOP) {
console.error("Cyclic symlink detected:", error.data.context);
} else {
throw error; // Re-throw unexpected errors
}
}Default Behavior:
maxDepth:10- Prevents excessive traversalboundary: User home directory (if start path under home), otherwise filesystem rootstopAtFirst:true- Returns first marker found (closest to start path)followSymlinks:false- Security: symlinks not followed by defaultconstraint:undefined- No additional path constraints
See Pathfinder README for complete API documentation.
Pathfinder - Filesystem Discovery
Enterprise filesystem traversal with pattern matching, ignore files, optional checksums, and comprehensive observability.
Features:
- Recursive directory scanning with glob patterns
.fulmenignoreand.gitignoresupport with nested precedence- Optional FulHash checksums (xxh3-128, sha256) with streaming calculation
- Path constraint enforcement (WARN, STRICT, PERMISSIVE)
- Structured errors with correlation IDs and severity levels
- Telemetry metrics for observability (
pathfinder_find_ms,pathfinder_security_warnings) - Streaming results via async iterables for large directories
- Cross-platform support (Linux, macOS, Windows)
Basic Usage:
import { Pathfinder } from "@fulmenhq/tsfulmen/pathfinder";
// Find all TypeScript files
const finder = new Pathfinder({
root: "./src",
includePatterns: ["**/*.ts"],
excludePatterns: ["**/*.test.ts"],
});
const results = await finder.find();
console.log(`Found ${results.length} files`);With Checksums and Observability:
import { Pathfinder } from "@fulmenhq/tsfulmen/pathfinder";
import { createLogger } from "@fulmenhq/tsfulmen/logging";
const logger = createLogger({ service: "file-discovery" });
const finder = new Pathfinder(
{
root: "./data",
calculateChecksums: true,
checksumAlgorithm: "xxh3-128",
enforcementLevel: "STRICT",
},
{ logger, correlationId: "scan-123" },
);
for await (const result of finder.findIterable()) {
console.log(`${result.path}: ${result.metadata.checksum}`);
}PathfinderOptions Integration:
The PathfinderOptions interface provides enterprise-grade configuration:
import type { PathfinderOptions } from "@fulmenhq/tsfulmen/pathfinder";
const options: PathfinderOptions = {
logger: createLogger({ service: "scanner" }),
correlationId: "batch-scan-001",
metrics: customMetricsRegistry,
};
const finder = new Pathfinder(config, options);Convenience Helpers:
import {
findConfigFiles,
findSchemaFiles,
findByExtensions,
} from "@fulmenhq/tsfulmen/pathfinder";
// Find all config files (YAML, JSON by default)
const configs = await findConfigFiles("./config");
// Find all schema files
const schemas = await findSchemaFiles("./schemas");
// Find files by specific extensions
const markdownFiles = await findByExtensions("./docs", [".md", ".markdown"]);Enterprise Observability:
Pathfinder automatically emits telemetry metrics:
pathfinder_find_ms- Histogram of find operation durationpathfinder_security_warnings- Counter for constraint violations- Structured errors with correlation IDs for distributed tracing
- Integration with TSFulmen's progressive logging system
Testing
make test # Run all tests
make test-watch # Watch mode
make test-coverage # With coverage reportSupply Chain & Security
tsfulmen is designed for environments where dependency hygiene matters.
Dependency Transparency:
- Auditable: Run
bun pm lsornpm lsto inspect dependencies - SBOM-ready: Compatible with
cyclonedx-npmand standard Node.js tooling - License-clean: All dependencies use MIT, Apache-2.0, or compatible licenses
Embedded Data:
- All Crucible catalogs (country codes, exit codes, HTTP statuses) are embedded at build time
- No runtime network calls for reference data
- Version and provenance tracked in
.crucible/metadata/metadata.yaml
Security Practices:
- Full TypeScript strict mode throughout
- Pattern matching uses bounded execution (no ReDoS vulnerabilities)
- Vulnerability scanning via
bun auditornpm audit
Audit Commands:
# View dependency tree
bun pm ls
# or
npm ls
# Check for known vulnerabilities
npm audit
# Generate SBOM (requires @cyclonedx/cyclonedx-npm)
npx @cyclonedx/cyclonedx-npm --output-file sbom.jsonSee SECURITY.md for vulnerability reporting and our full security policy.
Contributing
Contributions are welcome! Please ensure:
- Code follows TypeScript standards and conventions
- Tests are included for new functionality
- Documentation is updated
- Changes are consistent with Crucible standards
See CONTRIBUTING.md for development guidelines, MAINTAINERS.md for governance, and SECURITY.md for vulnerability reporting.
Licensing
tsfulmen is licensed under MIT license - see LICENSE for complete details.
Trademarks: "Fulmen" and "3 Leaps" are trademarks of 3 Leaps, LLC. While code is open source, please use distinct names for derivative works to prevent confusion.
Status
Lifecycle Phase: beta (Repository Lifecycle Standard)
- Quality Bar: 60% minimum test coverage (currently: 71%)
- Stability: Feature-complete; stabilizing behavior
- Breaking Changes: Addressed promptly with migration guidance
- Documentation: Kept current
See LIFECYCLE_PHASE file and CHANGELOG.md for version history.
Built by the 3 Leaps team
Part of the Fulmen Ecosystem — Enterprise-grade libraries that thrive on scale
