Utils
Shared low-level utilities for the Crust ecosystem (pre-stable).
Pre-stable
Status: pre-stable (0.0.1). Public surface may change without notice
until 0.1.0. Pin to an exact version if depending externally — bun add @crustjs/utils@0.0.1.
Internal de-duplication primitives shared across @crustjs/* packages.
Plugin authors may use these at their own risk; no stability promises are
made until the package graduates to 0.1.0.
Install
bun add @crustjs/utilsExports
| Export | Description |
|---|---|
resolveSourceDir | Resolve a source directory descriptor (file URL / absolute / relative) to a path |
BaseValueType | Shared primitive type literals: "string" | "number" | "boolean" |
ResolvePrimitive | Map primitive type literals to TypeScript primitives |
tryCoerceNumber | Coerce a string with Number(raw), returning undefined only for NaN |
coerceBooleanString | Coerce Crust boolean strings; only "true" and "1" are truthy |
resolveSourceDir
function resolveSourceDir(input: string | URL): string;Resolves an absolute filesystem path from a caller-supplied source directory descriptor. Designed for tools that ship reference assets (templates, skill bundles, etc.) alongside their published package and need a uniform way to locate them at runtime regardless of how the consumer expressed the path.
Used internally by scaffold in @crustjs/create
and installSkillBundle in @crustjs/skills — the template and
sourceDir options are resolved through this helper, so the three input
modes documented here apply uniformly to both packages.
Resolution modes
1. file: URL
Must use the file: protocol — typically constructed as
new URL("./relative/path", import.meta.url) so the path is anchored to the
calling module's location on disk. Resolved via url.fileURLToPath().
import { resolveSourceDir } from "@crustjs/utils";
const dir = resolveSourceDir(new URL("../templates/base", import.meta.url));2. Absolute string path
Returned as path.resolve(input) — . / .. segments are normalized.
const dir = resolveSourceDir("/abs/path/to/templates/base");3. Relative string path
Resolved against the nearest package.json directory walking up from
process.argv[1]. This makes "templates/base" resolve to
<consumer-package-root>/templates/base regardless of the consumer's current
working directory.
// In a package whose entrypoint is dist/cli.js, this resolves to
// <package-root>/templates/base.
const dir = resolveSourceDir("templates/base");Failure modes
resolveSourceDir throws a descriptive Error in three cases:
| Trigger | Error includes |
|---|---|
URL with non-file: protocol | The offending protocol (got "https:", etc.) |
Relative string path with process.argv[1] unset | The input + suggestion to use an absolute path or URL |
Relative string path but no package.json found walking up | The input, the entrypoint, and the same suggestion |
Type primitives
import {
coerceBooleanString,
tryCoerceNumber,
type BaseValueType,
type ResolvePrimitive,
} from "@crustjs/utils";BaseValueType is the canonical primitive vocabulary shared by Crust packages:
"string" | "number" | "boolean". ResolvePrimitive<T> maps those literals to
their TypeScript primitives and distributes over unions, so
ResolvePrimitive<"number" | "boolean"> resolves to number | boolean.
tryCoerceNumber(raw) mirrors the existing Crust number coercion behavior:
Number(raw) is returned unless it is NaN, in which case the helper returns
undefined. That means tryCoerceNumber("") returns 0 because Number("")
is 0.
coerceBooleanString(raw) preserves Crust's strict boolean string behavior:
only "true" and "1" return true; every other string returns false.
Stability
The public surface is intentionally narrow in 0.0.1. Helpers that have been
considered but deferred (until ≥2 cross-package consumers exist) include:
findNearestPackageRoot(currently private)readPackageJsonparseSemver
If you have a use case that would benefit from one of these, open an issue describing the consumer(s) you'd add to the dependency graph.
Internal subpath: @crustjs/utils/schema
Internal — do not import
Standard Schema helpers consumed by @crustjs/validate and
@crustjs/store. Not part of the public Crust API — may change in any
release without a deprecation cycle. Use @crustjs/validate instead.
Lives under @crustjs/utils/schema only so the shared Standard Schema
boundary code used by @crustjs/validate and @crustjs/store resolves for
external consumers. The subpath is core-free: it depends on the Standard Schema
spec package, not @crustjs/core, and does not wrap errors in package-specific
Crust error classes.