🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

package-json-effect

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

package-json-effect - npm Package Compare versions

Comparing version
0.1.0
to
0.2.0
+603
471.js
import { Context, Data, Effect, HashMap, Option, ParseResult, Pipeable, Schema } from "effect";
import { SemVer, parseRange, parseValidSemVer } from "semver-effect";
import { dual } from "effect/Function";
import spdx_expression_parse from "spdx-expression-parse";
const isBarePath = (s)=>s.startsWith("./") || s.startsWith("../") || s.startsWith("~/") || s.startsWith("/");
const isLocalSpecifier = (s)=>s.startsWith("file:") || s.startsWith("link:") || s.startsWith("portal:") || isBarePath(s);
const isGitHubShorthand = (s)=>!s.startsWith(".") && !s.startsWith("~") && !s.startsWith("/") && /^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+(#.*)?$/.test(s);
const isGitSpecifier = (s)=>s.startsWith("git+") || s.startsWith("git://") || s.startsWith("github:") || s.startsWith("gist:") || s.startsWith("bitbucket:") || s.startsWith("gitlab:") || isGitHubShorthand(s);
const isRangeSpecifier = (s)=>Option.isSome(parseRangeOption(s));
const isTagSpecifier = (s)=>"tag" === protocolOf(s);
const parseRangeOption = (s)=>Effect.runSync(Effect.option(parseRange(s)));
const protocolOf = (s)=>{
if (s.startsWith("catalog:")) return "catalog";
if (s.startsWith("workspace:")) return "workspace";
if (s.startsWith("link:")) return "link";
if (s.startsWith("portal:")) return "portal";
if (s.startsWith("file:") || isBarePath(s)) return "file";
if (s.startsWith("npm:")) return "npm";
if (isGitSpecifier(s)) return "git";
if (s.startsWith("http://") || s.startsWith("https://")) return "url";
if (Option.isSome(parseRangeOption(s))) return "range";
if (/^[a-zA-Z][a-zA-Z0-9._-]*$/.test(s)) return "tag";
return "unknown";
};
class Dependency extends Schema.TaggedClass()("Dependency", {
name: Schema.String,
specifier: Schema.String
}) {
get protocol() {
return 0 === this.specifier.length ? Option.none() : Option.some(protocolOf(this.specifier));
}
get range() {
return parseRangeOption(this.specifier);
}
get isLocal() {
return isLocalSpecifier(this.specifier);
}
get isLink() {
return this.specifier.startsWith("link:");
}
get isPortal() {
return this.specifier.startsWith("portal:");
}
get isCatalog() {
return this.specifier.startsWith("catalog:");
}
get isWorkspace() {
return this.specifier.startsWith("workspace:");
}
get isUnresolved() {
return this.isCatalog || this.isWorkspace;
}
get isGit() {
return isGitSpecifier(this.specifier);
}
get isRange() {
return isRangeSpecifier(this.specifier);
}
get isTag() {
return isTagSpecifier(this.specifier);
}
}
const isUnresolvedDependency = (dep)=>true === dep.isUnresolved;
class DevDependency extends Schema.TaggedClass()("DevDependency", {
name: Schema.String,
specifier: Schema.String
}) {
get protocol() {
return 0 === this.specifier.length ? Option.none() : Option.some(protocolOf(this.specifier));
}
get range() {
return parseRangeOption(this.specifier);
}
get isLocal() {
return isLocalSpecifier(this.specifier);
}
get isLink() {
return this.specifier.startsWith("link:");
}
get isPortal() {
return this.specifier.startsWith("portal:");
}
get isCatalog() {
return this.specifier.startsWith("catalog:");
}
get isWorkspace() {
return this.specifier.startsWith("workspace:");
}
get isUnresolved() {
return this.isCatalog || this.isWorkspace;
}
get isGit() {
return isGitSpecifier(this.specifier);
}
get isRange() {
return isRangeSpecifier(this.specifier);
}
get isTag() {
return isTagSpecifier(this.specifier);
}
}
class OptionalDependency extends Schema.TaggedClass()("OptionalDependency", {
name: Schema.String,
specifier: Schema.String
}) {
get protocol() {
return 0 === this.specifier.length ? Option.none() : Option.some(protocolOf(this.specifier));
}
get range() {
return parseRangeOption(this.specifier);
}
get isLocal() {
return isLocalSpecifier(this.specifier);
}
get isLink() {
return this.specifier.startsWith("link:");
}
get isPortal() {
return this.specifier.startsWith("portal:");
}
get isCatalog() {
return this.specifier.startsWith("catalog:");
}
get isWorkspace() {
return this.specifier.startsWith("workspace:");
}
get isUnresolved() {
return this.isCatalog || this.isWorkspace;
}
get isGit() {
return isGitSpecifier(this.specifier);
}
get isRange() {
return isRangeSpecifier(this.specifier);
}
get isTag() {
return isTagSpecifier(this.specifier);
}
}
const InvalidPackageNameErrorBase = Data.TaggedError("InvalidPackageNameError");
class InvalidPackageNameError extends InvalidPackageNameErrorBase {
get message() {
return `Invalid package name "${this.input}": ${this.reason}`;
}
}
const InvalidSpdxLicenseErrorBase = Data.TaggedError("InvalidSpdxLicenseError");
class InvalidSpdxLicenseError extends InvalidSpdxLicenseErrorBase {
get message() {
return `Invalid SPDX license "${this.input}": ${this.reason}`;
}
}
const BinMapSchema = Schema.transform(Schema.Record({
key: Schema.String,
value: Schema.String
}), Schema.typeSchema(Schema.HashMap({
key: Schema.String,
value: Schema.String
})), {
strict: true,
decode: (record)=>HashMap.fromIterable(Object.entries(record)),
encode: (map)=>Object.fromEntries(HashMap.toEntries(map))
});
const BinSchema = Schema.Union(Schema.String, BinMapSchema);
const StringRecord = Schema.Record({
key: Schema.String,
value: Schema.String
});
const DependencyMapSchema = Schema.transform(StringRecord, Schema.typeSchema(Schema.HashMap({
key: Schema.String,
value: Schema.String
})), {
strict: true,
decode: (record)=>HashMap.fromIterable(Object.entries(record)),
encode: (map)=>Object.fromEntries(HashMap.toEntries(map))
});
const OnFail = Schema.Literal("warn", "error", "ignore");
class DevEngine extends Schema.Class("DevEngine")({
name: Schema.String,
version: Schema.optionalWith(Schema.String, {
as: "Option"
}),
onFail: Schema.optionalWith(OnFail, {
as: "Option"
})
}) {
}
const DevEngineOrArray = Schema.Union(DevEngine, Schema.Array(DevEngine));
const DevEnginesSchema = Schema.Struct({
packageManager: Schema.optionalWith(DevEngineOrArray, {
as: "Option"
}),
runtime: Schema.optionalWith(DevEngineOrArray, {
as: "Option"
}),
os: Schema.optionalWith(DevEngineOrArray, {
as: "Option"
}),
cpu: Schema.optionalWith(DevEngineOrArray, {
as: "Option"
}),
libc: Schema.optionalWith(DevEngineOrArray, {
as: "Option"
})
});
const EnginesSchema = Schema.transform(Schema.Record({
key: Schema.String,
value: Schema.String
}), Schema.typeSchema(Schema.HashMap({
key: Schema.String,
value: Schema.String
})), {
strict: true,
decode: (record)=>HashMap.fromIterable(Object.entries(record)),
encode: (map)=>Object.fromEntries(HashMap.toEntries(map))
});
const ExportsObject = Schema.Record({
key: Schema.String,
value: Schema.Unknown
});
const ExportsFieldSchema = Schema.Union(Schema.String, ExportsObject);
const isValidSpdx = (s)=>{
if ("UNLICENSED" === s) return true;
if (s.startsWith("SEE LICENSE IN ") && s.length > 15) return true;
try {
spdx_expression_parse(s);
return true;
} catch {
return false;
}
};
const SpdxLicense = Schema.String.pipe(Schema.filter((s)=>isValidSpdx(s) || "Expected a valid SPDX license identifier or expression"), Schema.brand("SpdxLicense"));
const isValidPackageName = (s)=>{
if (0 === s.length || s.length > 214) return false;
if (s.startsWith("@")) {
const slashIndex = s.indexOf("/");
if (-1 === slashIndex || 1 === slashIndex || slashIndex === s.length - 1) return false;
if (-1 !== s.indexOf("/", slashIndex + 1)) return false;
const scope = s.slice(1, slashIndex);
const name = s.slice(slashIndex + 1);
return isValidNameChars(scope) && isValidNameChars(name);
}
if (s.startsWith(".") || s.startsWith("_")) return false;
return isValidNameChars(s);
};
const isValidNameChars = (s)=>/^[a-z0-9._-]+$/.test(s);
const ScopedPackageName = Schema.String.pipe(Schema.filter((s)=>s.startsWith("@") && isValidPackageName(s) || "Expected a scoped package name (@scope/name)"), Schema.brand("ScopedPackageName"));
const UnscopedPackageName = Schema.String.pipe(Schema.filter((s)=>!s.startsWith("@") && isValidPackageName(s) || "Expected an unscoped package name"), Schema.brand("UnscopedPackageName"));
const PackageName = Schema.Union(ScopedPackageName, UnscopedPackageName);
class PackageManager extends Schema.Class("PackageManager")({
name: Schema.String,
version: Schema.String,
integrity: Schema.optionalWith(Schema.String, {
as: "Option"
})
}) {
get hasIntegrity() {
return Option.isSome(this.integrity);
}
}
const PACKAGE_MANAGER_REGEX = /^([a-z]+)@(\d+\.\d+\.\d+(?:-[a-zA-Z0-9._-]+)?)(?:\+(.+))?$/;
const PackageManagerSchema = Schema.transformOrFail(Schema.String, Schema.typeSchema(PackageManager), {
strict: true,
decode: (s, _options, ast)=>{
const match = s.match(PACKAGE_MANAGER_REGEX);
if (!match) return ParseResult.fail(new ParseResult.Type(ast, s, `Invalid packageManager format: "${s}"`));
return ParseResult.succeed(new PackageManager({
name: match[1],
version: match[2],
integrity: match[3] ? Option.some(match[3]) : Option.none()
}, true));
},
encode: (pm)=>{
let result = `${pm.name}@${pm.version}`;
if (Option.isSome(pm.integrity)) result += `+${pm.integrity.value}`;
return ParseResult.succeed(result);
}
});
const PublishConfigSchema = Schema.Struct({
access: Schema.optionalWith(Schema.Literal("public", "restricted"), {
as: "Option"
}),
directory: Schema.optionalWith(Schema.String, {
as: "Option"
}),
registry: Schema.optionalWith(Schema.String, {
as: "Option"
}),
linkDirectory: Schema.optionalWith(Schema.Boolean, {
as: "Option"
})
}, {
key: Schema.String,
value: Schema.Unknown
});
const ScriptsSchema = DependencyMapSchema;
const VersionSchema = Schema.transformOrFail(Schema.String, Schema.typeSchema(SemVer), {
strict: true,
decode: (input, _options, ast)=>parseValidSemVer(input).pipe(Effect.mapError(()=>new ParseResult.Type(ast, input, `Invalid semver version: "${input}"`))),
encode: (semver)=>ParseResult.succeed(semver.toString())
});
class CatalogResolver extends Context.Tag("package-json-effect/CatalogResolver")() {
}
class WorkspaceResolver extends Context.Tag("package-json-effect/WorkspaceResolver")() {
}
const PackageNameUtil = {
scope: (name)=>{
if (!name.startsWith("@")) return Option.none();
const slashIndex = name.indexOf("/");
if (-1 === slashIndex) return Option.none();
return Option.some(name.slice(1, slashIndex));
},
unscoped: (name)=>{
if (!name.startsWith("@")) return name;
const slashIndex = name.indexOf("/");
return -1 === slashIndex ? name : name.slice(slashIndex + 1);
},
isScoped: (name)=>name.startsWith("@")
};
class PeerDependency extends Schema.TaggedClass()("PeerDependency", {
name: Schema.String,
specifier: Schema.String,
isOptional: Schema.Boolean
}) {
get protocol() {
return 0 === this.specifier.length ? Option.none() : Option.some(protocolOf(this.specifier));
}
get range() {
return parseRangeOption(this.specifier);
}
get isLocal() {
return isLocalSpecifier(this.specifier);
}
get isLink() {
return this.specifier.startsWith("link:");
}
get isPortal() {
return this.specifier.startsWith("portal:");
}
get isCatalog() {
return this.specifier.startsWith("catalog:");
}
get isWorkspace() {
return this.specifier.startsWith("workspace:");
}
get isUnresolved() {
return this.isCatalog || this.isWorkspace;
}
get isGit() {
return isGitSpecifier(this.specifier);
}
get isRange() {
return isRangeSpecifier(this.specifier);
}
get isTag() {
return isTagSpecifier(this.specifier);
}
}
const applyWorkspaceModifier = (specifier, version)=>{
const mod = specifier.slice(10);
if ("*" === mod || "" === mod) return version;
if ("^" === mod) return `^${version}`;
if ("~" === mod) return `~${version}`;
return mod;
};
const catalogName = (specifier)=>{
const name = specifier.slice(8);
return 0 === name.length ? Option.none() : Option.some(name);
};
class Package extends Schema.Class("Package")({
name: PackageName,
version: VersionSchema,
description: Schema.optionalWith(Schema.String, {
as: "Option"
}),
private: Schema.optionalWith(Schema.Boolean, {
as: "Option"
}),
type: Schema.optionalWith(Schema.Literal("module", "commonjs"), {
as: "Option"
}),
main: Schema.optionalWith(Schema.String, {
as: "Option"
}),
license: Schema.optionalWith(Schema.String, {
as: "Option"
}),
dependencies: Schema.optionalWith(DependencyMapSchema, {
default: ()=>HashMap.empty()
}),
devDependencies: Schema.optionalWith(DependencyMapSchema, {
default: ()=>HashMap.empty()
}),
peerDependencies: Schema.optionalWith(DependencyMapSchema, {
default: ()=>HashMap.empty()
}),
optionalDependencies: Schema.optionalWith(DependencyMapSchema, {
default: ()=>HashMap.empty()
}),
peerDependenciesMeta: Schema.optionalWith(Schema.Record({
key: Schema.String,
value: Schema.Struct({
optional: Schema.optional(Schema.Boolean)
})
}), {
as: "Option"
}),
scripts: Schema.optionalWith(ScriptsSchema, {
default: ()=>HashMap.empty()
}),
bin: Schema.optionalWith(BinSchema, {
as: "Option"
}),
engines: Schema.optionalWith(EnginesSchema, {
as: "Option"
}),
exports: Schema.optionalWith(ExportsFieldSchema, {
as: "Option"
}),
publishConfig: Schema.optionalWith(PublishConfigSchema, {
as: "Option"
}),
packageManager: Schema.optionalWith(PackageManagerSchema, {
as: "Option"
}),
devEngines: Schema.optionalWith(DevEnginesSchema, {
as: "Option"
}),
rest: Schema.optionalWith(Schema.Data(Schema.Record({
key: Schema.String,
value: Schema.Unknown
})), {
default: ()=>Data.struct({})
})
}) {
get isPrivate() {
return Option.getOrElse(this.private, ()=>false);
}
get isScoped() {
return PackageNameUtil.isScoped(this.name);
}
get isESM() {
return Option.match(this.type, {
onNone: ()=>false,
onSome: (t)=>"module" === t
});
}
pipe() {
return Pipeable.pipeArguments(this, arguments);
}
hasDependency(name) {
return HashMap.has(this.dependencies, name) || HashMap.has(this.devDependencies, name) || HashMap.has(this.peerDependencies, name) || HashMap.has(this.optionalDependencies, name);
}
getDependencies() {
return HashMap.map(this.dependencies, (specifier, name)=>new Dependency({
name,
specifier
}));
}
getDevDependencies() {
return HashMap.map(this.devDependencies, (specifier, name)=>new DevDependency({
name,
specifier
}));
}
getPeerDependencies() {
const meta = Option.getOrElse(this.peerDependenciesMeta, ()=>({}));
return HashMap.map(this.peerDependencies, (specifier, name)=>new PeerDependency({
name,
specifier,
isOptional: meta[name]?.optional ?? false
}));
}
getOptionalDependencies() {
return HashMap.map(this.optionalDependencies, (specifier, name)=>new OptionalDependency({
name,
specifier
}));
}
copyWith(patch) {
return new Package({
...this,
...patch
}, {
disableValidation: true
});
}
static fromData(data) {
return new Package(data, {
disableValidation: true
});
}
static setVersion = dual(2, (pkg, version)=>parseValidSemVer(version).pipe(Effect.map((semver)=>pkg.copyWith({
version: semver
}))));
static setName = dual(2, (pkg, name)=>isValidPackageName(name) ? Effect.succeed(pkg.copyWith({
name
})) : Effect.fail(new InvalidPackageNameError({
input: name,
reason: "Does not satisfy npm naming rules"
})));
static addDependency = dual(3, (pkg, name, specifier)=>pkg.copyWith({
dependencies: HashMap.set(pkg.dependencies, name, specifier)
}));
static removeDependency = dual(2, (pkg, name)=>pkg.copyWith({
dependencies: HashMap.remove(pkg.dependencies, name)
}));
static addDevDependency = dual(3, (pkg, name, specifier)=>pkg.copyWith({
devDependencies: HashMap.set(pkg.devDependencies, name, specifier)
}));
static removeDevDependency = dual(2, (pkg, name)=>pkg.copyWith({
devDependencies: HashMap.remove(pkg.devDependencies, name)
}));
static addPeerDependency = dual(3, (pkg, name, specifier)=>pkg.copyWith({
peerDependencies: HashMap.set(pkg.peerDependencies, name, specifier)
}));
static removePeerDependency = dual(2, (pkg, name)=>pkg.copyWith({
peerDependencies: HashMap.remove(pkg.peerDependencies, name)
}));
static addOptionalDependency = dual(3, (pkg, name, specifier)=>pkg.copyWith({
optionalDependencies: HashMap.set(pkg.optionalDependencies, name, specifier)
}));
static removeOptionalDependency = dual(2, (pkg, name)=>pkg.copyWith({
optionalDependencies: HashMap.remove(pkg.optionalDependencies, name)
}));
static setScript = dual(3, (pkg, name, command)=>pkg.copyWith({
scripts: HashMap.set(pkg.scripts, name, command)
}));
static removeScript = dual(2, (pkg, name)=>pkg.copyWith({
scripts: HashMap.remove(pkg.scripts, name)
}));
static setLicense = dual(2, (pkg, license)=>Schema.decodeUnknown(SpdxLicense)(license).pipe(Effect.mapError(()=>new InvalidSpdxLicenseError({
input: license,
reason: "Not a recognized SPDX identifier or expression"
})), Effect.map(()=>pkg.copyWith({
license: Option.some(license)
}))));
static resolve(pkg) {
return Effect.gen(function*() {
const ws = yield* WorkspaceResolver;
const cat = yield* CatalogResolver;
const resolveMap = (map)=>Effect.gen(function*() {
let next = map;
for (const [name, specifier] of HashMap.entries(map))if (specifier.startsWith("workspace:")) {
const version = yield* ws.versionOf(name);
if (Option.isSome(version)) next = HashMap.set(next, name, applyWorkspaceModifier(specifier, version.value));
} else if (specifier.startsWith("catalog:")) {
const range = yield* cat.rangeOf(name, catalogName(specifier));
if (Option.isSome(range)) next = HashMap.set(next, name, range.value);
}
return next;
});
return pkg.copyWith({
dependencies: yield* resolveMap(pkg.dependencies),
devDependencies: yield* resolveMap(pkg.devDependencies),
peerDependencies: yield* resolveMap(pkg.peerDependencies),
optionalDependencies: yield* resolveMap(pkg.optionalDependencies)
});
});
}
}
const DependencyResolutionErrorBase = Data.TaggedError("DependencyResolutionError");
class DependencyResolutionError extends DependencyResolutionErrorBase {
get message() {
return `Failed to resolve "${this.specifier}" for "${this.packageName}": ${this.reason}`;
}
}
const InvalidDependencySpecifierErrorBase = Data.TaggedError("InvalidDependencySpecifierError");
class InvalidDependencySpecifierError extends InvalidDependencySpecifierErrorBase {
get message() {
return `Invalid dependency specifier "${this.input}": ${this.reason}`;
}
}
const RawJson = Schema.Record({
key: Schema.String,
value: Schema.Unknown
});
const makePackageJsonSchema = (Class)=>{
const knownKeys = new Set(Object.keys(Class.fields).filter((k)=>"rest" !== k));
const wire = Schema.transformOrFail(RawJson, Class, {
strict: false,
decode: (raw)=>{
const known = {};
const rest = {};
for (const [k, v] of Object.entries(raw))if (knownKeys.has(k)) known[k] = v;
else rest[k] = v;
return ParseResult.succeed({
...known,
rest
});
},
encode: (encoded)=>{
const record = encoded;
const { rest, ...known } = record;
return ParseResult.succeed({
...known,
...rest ?? {}
});
}
});
return wire;
};
const PackageJsonSchema = makePackageJsonSchema(Package);
export { BinSchema, CatalogResolver, Dependency, DependencyMapSchema, DependencyResolutionError, DependencyResolutionErrorBase, DevDependency, DevEngine, DevEnginesSchema, EnginesSchema, ExportsFieldSchema, InvalidDependencySpecifierError, InvalidDependencySpecifierErrorBase, InvalidPackageNameError, InvalidPackageNameErrorBase, InvalidSpdxLicenseError, InvalidSpdxLicenseErrorBase, OptionalDependency, Package, PackageJsonSchema, PackageManager, PackageManagerSchema, PackageName, PackageNameUtil, PeerDependency, PublishConfigSchema, ScopedPackageName, ScriptsSchema, SpdxLicense, UnscopedPackageName, VersionSchema, WorkspaceResolver, isGitSpecifier, isLocalSpecifier, isRangeSpecifier, isTagSpecifier, isUnresolvedDependency, isValidPackageName, makePackageJsonSchema, parseRangeOption, protocolOf };
/**
* package-json-effect/schema
*
* Advanced schema exports for extending and customizing PackageJsonSchema.
*
* @packageDocumentation
*/
import { Brand } from 'effect/Brand';
import { Context } from 'effect';
import { Effect } from 'effect';
import { HashMap } from 'effect';
import { HashMap as HashMap_2 } from 'effect/HashMap';
import type { InvalidVersionError } from 'semver-effect';
import { Option } from 'effect';
import { Option as Option_2 } from 'effect/Option';
import type { Range } from 'semver-effect';
import { Schema } from 'effect';
import { Schema as Schema_2 } from 'effect/Schema';
import { SemVer } from 'semver-effect';
import { VoidIfEmpty } from 'effect/Types';
import { YieldableError } from 'effect/Cause';
/**
* Bin field: either a single string path or a map of command names to paths.
*/
export declare const BinSchema: Schema.Schema<string | HashMap.HashMap<string, string>, string | {
readonly [x: string]: string;
}>;
/**
* Resolves catalog: protocol specifiers. Given a package name and an optional
* catalog name (None = default catalog), returns the configured range, or None
* if it cannot be resolved (default no-op behavior).
*/
export declare class CatalogResolver extends CatalogResolver_base {
}
declare const CatalogResolver_base: Context.TagClass<CatalogResolver, "package-json-effect/CatalogResolver", {
readonly rangeOf: (packageName: string, catalog: Option.Option<string>) => Effect.Effect<Option.Option<string>, DependencyResolutionError>;
}>;
/**
* Opt-in decoder: validate a string as a DependencySpecifier, failing with a
* typed InvalidDependencySpecifierError instead of a Schema ParseError.
*/
export declare const decodeSpecifier: (input: string) => Effect.Effect<DependencySpecifier, InvalidDependencySpecifierError>;
export declare class Dependency extends Dependency_base implements DependencyProtocolGetters {
get protocol(): Option.Option<DependencyProtocol>;
get range(): Option.Option<Range>;
get isLocal(): boolean;
get isLink(): boolean;
get isPortal(): boolean;
get isCatalog(): boolean;
get isWorkspace(): boolean;
get isUnresolved(): boolean;
get isGit(): boolean;
get isRange(): boolean;
get isTag(): boolean;
}
declare const Dependency_base: Schema.TaggedClass<Dependency, "Dependency", {
readonly _tag: Schema.tag<"Dependency">;
} & {
name: typeof Schema.String;
specifier: typeof Schema.String;
}>;
/**
* A dependency map: plain JSON object decoded to/from HashMap.
*/
export declare const DependencyMapSchema: Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}>;
export declare type DependencyProtocol = "range" | "tag" | "git" | "url" | "npm" | "file" | "link" | "portal" | "catalog" | "workspace" | "unknown";
/** The shared protocol-classification getters implemented by every Dependency variant. */
export declare interface DependencyProtocolGetters {
readonly protocol: Option.Option<DependencyProtocol>;
readonly range: Option.Option<Range>;
readonly isLocal: boolean;
readonly isLink: boolean;
readonly isPortal: boolean;
readonly isCatalog: boolean;
readonly isWorkspace: boolean;
readonly isUnresolved: boolean;
readonly isGit: boolean;
readonly isRange: boolean;
readonly isTag: boolean;
}
/** Indicates that a catalog: or workspace: specifier could not be resolved. */
export declare class DependencyResolutionError extends DependencyResolutionErrorBase<{
/** The name of the package whose specifier could not be resolved. */
readonly packageName: string;
/** The specifier string (e.g. `catalog:` or `workspace:*`) that failed resolution. */
readonly specifier: string;
/** A human-readable description of why resolution failed. */
readonly reason: string;
}> {
get message(): string;
}
/**
* Tagged error base for {@link DependencyResolutionError}.
*
* @privateRemarks
* Exported because TypeScript declaration bundling requires the base class to be
* accessible when {@link DependencyResolutionError} appears in public type signatures.
* Consumers should use {@link DependencyResolutionError} directly.
*
* @internal
*/
export declare const DependencyResolutionErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & {
readonly _tag: "DependencyResolutionError";
} & Readonly<A>;
/**
* A valid dependency version specifier.
*/
declare const DependencySpecifier: Schema.brand<Schema.filter<typeof Schema.String>, "DependencySpecifier">;
/** Branded type for dependency specifiers. */
declare type DependencySpecifier = Schema.Schema.Type<typeof DependencySpecifier>;
export { DependencySpecifier }
export { DependencySpecifier as DependencySpecifierType }
export declare class DevDependency extends DevDependency_base implements DependencyProtocolGetters {
get protocol(): Option.Option<DependencyProtocol>;
get range(): Option.Option<Range>;
get isLocal(): boolean;
get isLink(): boolean;
get isPortal(): boolean;
get isCatalog(): boolean;
get isWorkspace(): boolean;
get isUnresolved(): boolean;
get isGit(): boolean;
get isRange(): boolean;
get isTag(): boolean;
}
declare const DevDependency_base: Schema.TaggedClass<DevDependency, "DevDependency", {
readonly _tag: Schema.tag<"DevDependency">;
} & {
name: typeof Schema.String;
specifier: typeof Schema.String;
}>;
/**
* A single engine constraint with name, optional version, and optional onFail behavior.
*/
export declare class DevEngine extends DevEngine_base {
}
declare const DevEngine_base: Schema.Class<DevEngine, {
name: typeof Schema.String;
version: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
onFail: Schema.optionalWith<Schema.Literal<["warn", "error", "ignore"]>, {
as: "Option";
}>;
}, Schema.Struct.Encoded<{
name: typeof Schema.String;
version: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
onFail: Schema.optionalWith<Schema.Literal<["warn", "error", "ignore"]>, {
as: "Option";
}>;
}>, never, {
readonly name: string;
} & {
readonly onFail: Option_2<"error" | "ignore" | "warn">;
} & {
readonly version: Option_2<string>;
}, {}, {}>;
export declare type DevEngines = Schema.Schema.Type<typeof DevEnginesSchema>;
/**
* Schema for the devEngines field, modeling runtime and
* package manager constraints with optional arrays.
*/
export declare const DevEnginesSchema: Schema.Struct<{
packageManager: Schema.optionalWith<Schema.Union<[typeof DevEngine, Schema.Array$<typeof DevEngine>]>, {
as: "Option";
}>;
runtime: Schema.optionalWith<Schema.Union<[typeof DevEngine, Schema.Array$<typeof DevEngine>]>, {
as: "Option";
}>;
os: Schema.optionalWith<Schema.Union<[typeof DevEngine, Schema.Array$<typeof DevEngine>]>, {
as: "Option";
}>;
cpu: Schema.optionalWith<Schema.Union<[typeof DevEngine, Schema.Array$<typeof DevEngine>]>, {
as: "Option";
}>;
libc: Schema.optionalWith<Schema.Union<[typeof DevEngine, Schema.Array$<typeof DevEngine>]>, {
as: "Option";
}>;
}>;
/**
* Engines field: map of engine names to semver ranges.
*/
export declare const EnginesSchema: Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}>;
export declare type ExportsField = Schema.Schema.Type<typeof ExportsFieldSchema>;
/**
* Exports field: either a single string entry point or an object
* mapping subpaths to file paths (or null to block).
*
* The object form accepts any value shape to support conditional
* exports with nested objects (e.g. `{ "import": "./esm.js", "require": "./cjs.js" }`).
*/
export declare const ExportsFieldSchema: Schema.Union<[typeof Schema.String, Schema.Record$<typeof Schema.String, typeof Schema.Unknown>]>;
/**
* Indicates that a string could not be parsed as a valid dependency specifier.
*/
export declare class InvalidDependencySpecifierError extends InvalidDependencySpecifierErrorBase<{
/** The raw input string that failed validation. */
readonly input: string;
/** A human-readable description of why the specifier is invalid. */
readonly reason: string;
}> {
get message(): string;
}
/**
* Tagged error base for {@link InvalidDependencySpecifierError}.
*
* @privateRemarks
* Exported because TypeScript declaration bundling requires the base class to be
* accessible when {@link InvalidDependencySpecifierError} appears in public type signatures.
* Consumers should use {@link InvalidDependencySpecifierError} directly.
*
* @internal
*/
export declare const InvalidDependencySpecifierErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & {
readonly _tag: "InvalidDependencySpecifierError";
} & Readonly<A>;
/**
* Indicates that a string could not be used as a valid npm package name.
*/
export declare class InvalidPackageNameError extends InvalidPackageNameErrorBase<{
/** The raw input string that failed validation. */
readonly input: string;
/** A human-readable description of why the name is invalid. */
readonly reason: string;
}> {
get message(): string;
}
/**
* Tagged error base for {@link InvalidPackageNameError}.
*
* @privateRemarks
* Exported because TypeScript declaration bundling requires the base class to be
* accessible when {@link InvalidPackageNameError} appears in public type signatures.
* Consumers should use {@link InvalidPackageNameError} directly.
*
* @internal
*/
export declare const InvalidPackageNameErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & {
readonly _tag: "InvalidPackageNameError";
} & Readonly<A>;
/** Indicates that a string is not a valid SPDX license identifier or expression. */
export declare class InvalidSpdxLicenseError extends InvalidSpdxLicenseErrorBase<{
/** The raw input string that failed validation. */
readonly input: string;
/** A human-readable description of why the license identifier is invalid. */
readonly reason: string;
}> {
get message(): string;
}
/**
* Tagged error base for {@link InvalidSpdxLicenseError}.
*
* @privateRemarks
* Exported because TypeScript declaration bundling requires the base class to be
* accessible when {@link InvalidSpdxLicenseError} appears in public type signatures.
* Consumers should use {@link InvalidSpdxLicenseError} directly.
*
* @internal
*/
export declare const InvalidSpdxLicenseErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & {
readonly _tag: "InvalidSpdxLicenseError";
} & Readonly<A>;
/**
* Validates a dependency version specifier.
*
* Accepts semver ranges, exact versions, dist-tags, URLs,
* git refs, GitHub shorthand, file paths, and npm/catalog/workspace protocols.
*/
export declare const isValidDependencySpecifier: (s: string) => boolean;
/**
* Validates a string as a valid npm package name.
*
* Rules: max 214 chars, lowercase, URL-safe characters only,
* cannot start with . or _ (unless scoped).
*/
export declare const isValidPackageName: (s: string) => boolean;
/**
* Build the wire schema (open JSON object ↔ Package class) for the given
* Package class or any `.extend()`ed subclass. Reads `Class.fields` so extended
* fields are decoded as typed members and excluded from `rest`.
*
* The transform sits between an open JSON record (`RawJson`) and the class. On
* decode it partitions raw keys into known fields and a `rest` record, handing
* Effect the class's encoded shape so the class decodes itself. On encode it
* receives the class's encoded shape and flattens the literal `rest` key back
* into top-level fields, so the on-disk shape never contains a `rest` key.
*/
export declare const makePackageJsonSchema: <Self extends Package>(Class: Schema.Schema<Self, any, never> & {
readonly fields: Schema.Struct.Fields;
}) => Schema.Schema<Self, {
readonly [k: string]: unknown;
}, never>;
export declare class OptionalDependency extends OptionalDependency_base implements DependencyProtocolGetters {
get protocol(): Option.Option<DependencyProtocol>;
get range(): Option.Option<Range>;
get isLocal(): boolean;
get isLink(): boolean;
get isPortal(): boolean;
get isCatalog(): boolean;
get isWorkspace(): boolean;
get isUnresolved(): boolean;
get isGit(): boolean;
get isRange(): boolean;
get isTag(): boolean;
}
declare const OptionalDependency_base: Schema.TaggedClass<OptionalDependency, "OptionalDependency", {
readonly _tag: Schema.tag<"OptionalDependency">;
} & {
name: typeof Schema.String;
specifier: typeof Schema.String;
}>;
/**
* Domain model for a package.json document. A Schema.Class carrying typed
* known fields plus a `rest` catch-all that preserves any unmodeled top-level
* fields for round-trip fidelity. The literal `rest` key is flattened away by
* the wire transform in src/schemas/package-json.ts.
*/
export declare class Package extends Package_base {
get isPrivate(): boolean;
get isScoped(): boolean;
get isESM(): boolean;
pipe<A>(this: A): A;
pipe<A, B>(this: A, ab: (_: A) => B): B;
pipe<A, B, C>(this: A, ab: (_: A) => B, bc: (_: B) => C): C;
pipe<A, B, C, D>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D;
pipe<A, B, C, D, E>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D, de: (_: D) => E): E;
hasDependency(name: string): boolean;
getDependencies(): HashMap.HashMap<string, Dependency>;
getDevDependencies(): HashMap.HashMap<string, DevDependency>;
getPeerDependencies(): HashMap.HashMap<string, PeerDependency>;
getOptionalDependencies(): HashMap.HashMap<string, OptionalDependency>;
/** Return a new Package with the given fields replaced. */
copyWith(patch: Partial<{
name: string;
version: SemVer;
license: Option.Option<string>;
dependencies: HashMap.HashMap<string, string>;
devDependencies: HashMap.HashMap<string, string>;
peerDependencies: HashMap.HashMap<string, string>;
optionalDependencies: HashMap.HashMap<string, string>;
scripts: HashMap.HashMap<string, string>;
}>): Package;
/** Construct a Package from an already-decoded data record. */
static fromData(data: ConstructorParameters<typeof Package>[0]): Package;
static setVersion: {
(version: string): (pkg: Package) => Effect.Effect<Package, InvalidVersionError>;
(pkg: Package, version: string): Effect.Effect<Package, InvalidVersionError>;
};
static setName: {
(name: string): (pkg: Package) => Effect.Effect<Package, InvalidPackageNameError>;
(pkg: Package, name: string): Effect.Effect<Package, InvalidPackageNameError>;
};
static addDependency: {
(name: string, specifier: string): (pkg: Package) => Package;
(pkg: Package, name: string, specifier: string): Package;
};
static removeDependency: {
(name: string): (pkg: Package) => Package;
(pkg: Package, name: string): Package;
};
static addDevDependency: {
(name: string, specifier: string): (pkg: Package) => Package;
(pkg: Package, name: string, specifier: string): Package;
};
static removeDevDependency: {
(name: string): (pkg: Package) => Package;
(pkg: Package, name: string): Package;
};
static addPeerDependency: {
(name: string, specifier: string): (pkg: Package) => Package;
(pkg: Package, name: string, specifier: string): Package;
};
static removePeerDependency: {
(name: string): (pkg: Package) => Package;
(pkg: Package, name: string): Package;
};
static addOptionalDependency: {
(name: string, specifier: string): (pkg: Package) => Package;
(pkg: Package, name: string, specifier: string): Package;
};
static removeOptionalDependency: {
(name: string): (pkg: Package) => Package;
(pkg: Package, name: string): Package;
};
static setScript: {
(name: string, command: string): (pkg: Package) => Package;
(pkg: Package, name: string, command: string): Package;
};
static removeScript: {
(name: string): (pkg: Package) => Package;
(pkg: Package, name: string): Package;
};
static setLicense: {
(license: string): (pkg: Package) => Effect.Effect<Package, InvalidSpdxLicenseError>;
(pkg: Package, license: string): Effect.Effect<Package, InvalidSpdxLicenseError>;
};
/**
* Resolve catalog: and workspace: specifiers across all four dependency maps
* using the CatalogResolver and WorkspaceResolver from context. Returns a new
* Package. Specifiers the resolvers return None for are left unchanged.
*/
static resolve(pkg: Package): Effect.Effect<Package, DependencyResolutionError, WorkspaceResolver | CatalogResolver>;
}
declare const Package_base: Schema.Class<Package, {
name: Schema.Union<[Schema.brand<Schema.filter<typeof Schema.String>, "ScopedPackageName">, Schema.brand<Schema.filter<typeof Schema.String>, "UnscopedPackageName">]>;
version: Schema.Schema<SemVer, string, never>;
description: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
private: Schema.optionalWith<typeof Schema.Boolean, {
as: "Option";
}>;
type: Schema.optionalWith<Schema.Literal<["module", "commonjs"]>, {
as: "Option";
}>;
main: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
license: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
dependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
devDependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
peerDependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
optionalDependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
peerDependenciesMeta: Schema.optionalWith<Schema.Record$<typeof Schema.String, Schema.Struct<{
optional: Schema.optional<typeof Schema.Boolean>;
}>>, {
as: "Option";
}>;
scripts: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
bin: Schema.optionalWith<Schema.Schema<string | HashMap.HashMap<string, string>, string | {
readonly [x: string]: string;
}, never>, {
as: "Option";
}>;
engines: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
as: "Option";
}>;
exports: Schema.optionalWith<Schema.Union<[typeof Schema.String, Schema.Record$<typeof Schema.String, typeof Schema.Unknown>]>, {
as: "Option";
}>;
publishConfig: Schema.optionalWith<Schema.TypeLiteral<{
access: Schema.optionalWith<Schema.Literal<["public", "restricted"]>, {
as: "Option";
}>;
directory: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
registry: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
linkDirectory: Schema.optionalWith<typeof Schema.Boolean, {
as: "Option";
}>;
}, readonly [{
readonly key: typeof Schema.String;
readonly value: typeof Schema.Unknown;
}]>, {
as: "Option";
}>;
packageManager: Schema.optionalWith<Schema.Schema<PackageManager, string, never>, {
as: "Option";
}>;
devEngines: Schema.optionalWith<Schema.Struct<{
packageManager: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
runtime: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
os: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
cpu: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
libc: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
}>, {
as: "Option";
}>;
rest: Schema.optionalWith<Schema.Data<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>, {
default: () => Record<string, unknown>;
}>;
}, Schema.Struct.Encoded<{
name: Schema.Union<[Schema.brand<Schema.filter<typeof Schema.String>, "ScopedPackageName">, Schema.brand<Schema.filter<typeof Schema.String>, "UnscopedPackageName">]>;
version: Schema.Schema<SemVer, string, never>;
description: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
private: Schema.optionalWith<typeof Schema.Boolean, {
as: "Option";
}>;
type: Schema.optionalWith<Schema.Literal<["module", "commonjs"]>, {
as: "Option";
}>;
main: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
license: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
dependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
devDependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
peerDependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
optionalDependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
peerDependenciesMeta: Schema.optionalWith<Schema.Record$<typeof Schema.String, Schema.Struct<{
optional: Schema.optional<typeof Schema.Boolean>;
}>>, {
as: "Option";
}>;
scripts: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
bin: Schema.optionalWith<Schema.Schema<string | HashMap.HashMap<string, string>, string | {
readonly [x: string]: string;
}, never>, {
as: "Option";
}>;
engines: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
as: "Option";
}>;
exports: Schema.optionalWith<Schema.Union<[typeof Schema.String, Schema.Record$<typeof Schema.String, typeof Schema.Unknown>]>, {
as: "Option";
}>;
publishConfig: Schema.optionalWith<Schema.TypeLiteral<{
access: Schema.optionalWith<Schema.Literal<["public", "restricted"]>, {
as: "Option";
}>;
directory: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
registry: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
linkDirectory: Schema.optionalWith<typeof Schema.Boolean, {
as: "Option";
}>;
}, readonly [{
readonly key: typeof Schema.String;
readonly value: typeof Schema.Unknown;
}]>, {
as: "Option";
}>;
packageManager: Schema.optionalWith<Schema.Schema<PackageManager, string, never>, {
as: "Option";
}>;
devEngines: Schema.optionalWith<Schema.Struct<{
packageManager: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
runtime: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
os: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
cpu: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
libc: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
}>, {
as: "Option";
}>;
rest: Schema.optionalWith<Schema.Data<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>, {
default: () => Record<string, unknown>;
}>;
}>, never, {
readonly dependencies?: HashMap.HashMap<string, string>;
} & {
readonly devDependencies?: HashMap.HashMap<string, string>;
} & {
readonly optionalDependencies?: HashMap.HashMap<string, string>;
} & {
readonly peerDependencies?: HashMap.HashMap<string, string>;
} & {
readonly rest?: {
readonly [x: string]: unknown;
};
} & {
readonly scripts?: HashMap.HashMap<string, string>;
} & {
readonly bin: Option.Option<string | HashMap.HashMap<string, string>>;
} & {
readonly description: Option.Option<string>;
} & {
readonly devEngines: Option.Option<{
readonly packageManager: Option.Option<DevEngine | readonly DevEngine[]>;
readonly runtime: Option.Option<DevEngine | readonly DevEngine[]>;
readonly os: Option.Option<DevEngine | readonly DevEngine[]>;
readonly cpu: Option.Option<DevEngine | readonly DevEngine[]>;
readonly libc: Option.Option<DevEngine | readonly DevEngine[]>;
}>;
} & {
readonly engines: Option.Option<HashMap.HashMap<string, string>>;
} & {
readonly exports: Option.Option<string | {
readonly [x: string]: unknown;
}>;
} & {
readonly license: Option.Option<string>;
} & {
readonly main: Option.Option<string>;
} & {
readonly name: (string & Brand<"ScopedPackageName">) | (string & Brand<"UnscopedPackageName">);
} & {
readonly packageManager: Option.Option<PackageManager>;
} & {
readonly peerDependenciesMeta: Option.Option<{
readonly [x: string]: {
readonly optional?: boolean | undefined;
};
}>;
} & {
readonly private: Option.Option<boolean>;
} & {
readonly publishConfig: Option.Option<{
readonly [x: string]: unknown;
readonly access: Option.Option<"public" | "restricted">;
readonly directory: Option.Option<string>;
readonly registry: Option.Option<string>;
readonly linkDirectory: Option.Option<boolean>;
}>;
} & {
readonly type: Option.Option<"commonjs" | "module">;
} & {
readonly version: SemVer;
}, {}, {}>;
/** The default wire schema: decodes JSON to a Package instance and back. */
export declare const PackageJsonSchema: Schema.Schema<Package, {
readonly [k: string]: unknown;
}, never>;
/** Encoded (plain JSON) type for PackageJsonSchema. */
export declare interface PackageJsonSchemaEncoded {
readonly [k: string]: unknown;
}
/** Decoded type for PackageJsonSchema. */
export declare type PackageJsonSchemaType = Package;
export declare class PackageManager extends PackageManager_base {
get hasIntegrity(): boolean;
}
declare const PackageManager_base: Schema.Class<PackageManager, {
name: typeof Schema.String;
version: typeof Schema.String;
integrity: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
}, Schema.Struct.Encoded<{
name: typeof Schema.String;
version: typeof Schema.String;
integrity: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
}>, never, {
readonly integrity: Option.Option<string>;
} & {
readonly name: string;
} & {
readonly version: string;
}, {}, {}>;
/**
* Parses a packageManager string (e.g. "pnpm\@10.33.0+sha512.abc")
* into a PackageManager class instance with name, version, and
* optional integrity fields.
*/
export declare const PackageManagerSchema: Schema.Schema<PackageManager, string>;
/**
* A valid npm package name, either scoped or unscoped.
*/
declare const PackageName: Schema.Union<[Schema.brand<Schema.filter<typeof Schema.String>, "ScopedPackageName">, Schema.brand<Schema.filter<typeof Schema.String>, "UnscopedPackageName">]>;
/** Branded type for any valid package name. */
declare type PackageName = Schema.Schema.Type<typeof PackageName>;
export { PackageName }
export { PackageName as PackageNameType }
export declare class PeerDependency extends PeerDependency_base implements DependencyProtocolGetters {
get protocol(): Option.Option<DependencyProtocol>;
get range(): Option.Option<Range>;
get isLocal(): boolean;
get isLink(): boolean;
get isPortal(): boolean;
get isCatalog(): boolean;
get isWorkspace(): boolean;
get isUnresolved(): boolean;
get isGit(): boolean;
get isRange(): boolean;
get isTag(): boolean;
}
declare const PeerDependency_base: Schema.TaggedClass<PeerDependency, "PeerDependency", {
readonly _tag: Schema.tag<"PeerDependency">;
} & {
name: typeof Schema.String;
specifier: typeof Schema.String;
isOptional: typeof Schema.Boolean;
}>;
/**
* Structured person object with name, optional email and url.
*/
export declare class Person extends Person_base {
}
declare const Person_base: Schema.Class<Person, {
name: typeof Schema.String;
email: Schema.optional<typeof Schema.String>;
url: Schema.optional<typeof Schema.String>;
}, Schema.Struct.Encoded<{
name: typeof Schema.String;
email: Schema.optional<typeof Schema.String>;
url: Schema.optional<typeof Schema.String>;
}>, never, {
readonly email?: string | undefined;
} & {
readonly url?: string | undefined;
} & {
readonly name: string;
}, {}, {}>;
/**
* Person field: either a string "Name email (url)" shorthand or a
* structured object with name, email, and url fields.
* Always decoded to the object form.
*/
export declare const PersonSchema: Schema.Union<[typeof Person, Schema.transform<typeof Schema.String, Schema.SchemaClass<Person, Person, never>>]>;
export declare type PublishConfig = Schema.Schema.Type<typeof PublishConfigSchema>;
/**
* Schema for the publishConfig field with typed known fields
* and an open record for arbitrary extensions like targets.
*/
export declare const PublishConfigSchema: Schema.TypeLiteral<{
access: Schema.optionalWith<Schema.Literal<["public", "restricted"]>, {
as: "Option";
}>;
directory: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
registry: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
linkDirectory: Schema.optionalWith<typeof Schema.Boolean, {
as: "Option";
}>;
}, readonly [{
readonly key: typeof Schema.String;
readonly value: typeof Schema.Unknown;
}]>;
/**
* A valid npm scoped package name (starts with \@scope/).
*/
declare const ScopedPackageName: Schema.brand<Schema.filter<typeof Schema.String>, "ScopedPackageName">;
/** Branded type for scoped package names. */
declare type ScopedPackageName = Schema.Schema.Type<typeof ScopedPackageName>;
export { ScopedPackageName }
export { ScopedPackageName as ScopedPackageNameType }
/**
* Scripts field: plain JSON object decoded to/from HashMap of string to string.
* Same structure as DependencyMapSchema.
*/
export declare const ScriptsSchema: Schema_2<HashMap_2<string, string>, {
readonly [x: string]: string;
}, never>;
/**
* A valid SPDX license identifier, expression, "UNLICENSED",
* or "SEE LICENSE IN filename".
*/
declare const SpdxLicense: Schema.brand<Schema.filter<typeof Schema.String>, "SpdxLicense">;
/** Branded type for SPDX license strings. */
declare type SpdxLicense = Schema.Schema.Type<typeof SpdxLicense>;
export { SpdxLicense }
export { SpdxLicense as SpdxLicenseType }
/** A Dependency whose specifier is an unresolved catalog: or workspace: protocol. */
export declare type UnresolvedDependency = Dependency & {
readonly isUnresolved: true;
};
/**
* A valid npm unscoped package name (does not start with \@).
*/
declare const UnscopedPackageName: Schema.brand<Schema.filter<typeof Schema.String>, "UnscopedPackageName">;
/** Branded type for unscoped package names. */
declare type UnscopedPackageName = Schema.Schema.Type<typeof UnscopedPackageName>;
export { UnscopedPackageName }
export { UnscopedPackageName as UnscopedPackageNameType }
/**
* Schema that decodes a version string into a SemVer instance
* and encodes it back to a string. Uses semver-effect for parsing.
*/
export declare const VersionSchema: Schema.Schema<SemVer, string>;
/**
* Resolves workspace: protocol specifiers. Given a workspace package name,
* returns its concrete version (without range modifier), or None if it cannot
* be resolved (default no-op behavior).
*/
export declare class WorkspaceResolver extends WorkspaceResolver_base {
}
declare const WorkspaceResolver_base: Context.TagClass<WorkspaceResolver, "package-json-effect/WorkspaceResolver", {
readonly versionOf: (packageName: string) => Effect.Effect<Option.Option<string>, DependencyResolutionError>;
}>;
export { }
import { Effect, Schema } from "effect";
import { InvalidDependencySpecifierError } from "./471.js";
const isValidDependencySpecifier = (s)=>{
if (0 === s.length) return false;
if (s.startsWith("file:")) return true;
if (s.startsWith("link:")) return true;
if (s.startsWith("portal:")) return true;
if (s.startsWith("git+")) return true;
if (s.startsWith("git://")) return true;
if (s.startsWith("github:")) return true;
if (s.startsWith("gist:")) return true;
if (s.startsWith("bitbucket:")) return true;
if (s.startsWith("gitlab:")) return true;
if (s.startsWith("http://") || s.startsWith("https://")) return true;
if (s.startsWith("npm:")) return true;
if (s.startsWith("catalog:")) return true;
if (s.startsWith("workspace:")) return true;
if (/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+(#.*)?$/.test(s)) return true;
if (/^[\d^~>=<*|xX]/.test(s)) return true;
if (/^[a-zA-Z][a-zA-Z0-9._-]*$/.test(s)) return true;
return false;
};
const DependencySpecifier = Schema.String.pipe(Schema.filter((s)=>isValidDependencySpecifier(s) || "Expected a valid dependency specifier"), Schema.brand("DependencySpecifier"));
const decodeSpecifier = (input)=>Schema.decodeUnknown(DependencySpecifier)(input).pipe(Effect.mapError(()=>new InvalidDependencySpecifierError({
input,
reason: "Not a recognized dependency specifier"
})));
class Person extends Schema.Class("Person")({
name: Schema.String,
email: Schema.optional(Schema.String),
url: Schema.optional(Schema.String)
}) {
}
const parsePersonString = (s)=>{
const emailMatch = s.match(/<([^>]+)>/);
const urlMatch = s.match(/\(([^)]+)\)/);
let name = s;
if (emailMatch) name = name.replace(emailMatch[0], "");
if (urlMatch) name = name.replace(urlMatch[0], "");
name = name.trim();
const fields = {
name
};
if (emailMatch) fields.email = emailMatch[1];
if (urlMatch) fields.url = urlMatch[1];
return new Person(fields, true);
};
const PersonFromString = Schema.transform(Schema.String, Schema.typeSchema(Person), {
strict: true,
decode: (s)=>parsePersonString(s),
encode: (p)=>{
let result = p.name;
if (p.email) result += ` <${p.email}>`;
if (p.url) result += ` (${p.url})`;
return result;
}
});
const PersonSchema = Schema.Union(Person, PersonFromString);
export { BinSchema, CatalogResolver, Dependency, DependencyMapSchema, DependencyResolutionError, DependencyResolutionErrorBase, DevDependency, DevEngine, DevEnginesSchema, EnginesSchema, ExportsFieldSchema, InvalidDependencySpecifierError, InvalidDependencySpecifierErrorBase, InvalidPackageNameError, InvalidPackageNameErrorBase, InvalidSpdxLicenseError, InvalidSpdxLicenseErrorBase, OptionalDependency, Package, PackageJsonSchema, PackageManager, PackageManagerSchema, PackageName, PeerDependency, PublishConfigSchema, ScopedPackageName, ScriptsSchema, SpdxLicense, UnscopedPackageName, VersionSchema, WorkspaceResolver, isValidPackageName, makePackageJsonSchema } from "./471.js";
export { DependencySpecifier, Person, PersonSchema, decodeSpecifier, isValidDependencySpecifier };
+424
-386

@@ -10,2 +10,3 @@ /**

import { Brand } from 'effect/Brand';
import { Context } from 'effect';

@@ -16,3 +17,2 @@ import { Effect } from 'effect';

import { HashMap } from 'effect';
import { HashMap as HashMap_2 } from 'effect/HashMap';
import type { InvalidVersionError } from 'semver-effect';

@@ -22,5 +22,5 @@ import { Layer } from 'effect';

import { Option as Option_2 } from 'effect/Option';
import type { Range } from 'semver-effect';
import { Schema } from 'effect';
import { Schema as Schema_2 } from 'effect/Schema';
import { SemVer } from 'semver-effect';
import type { SemVer } from 'semver-effect';
import { VoidIfEmpty } from 'effect/Types';

@@ -30,13 +30,6 @@ import { YieldableError } from 'effect/Cause';

/**
* Bin field: either a single string path or a map of command names to paths.
* Resolves catalog: protocol specifiers. Given a package name and an optional
* catalog name (None = default catalog), returns the configured range, or None
* if it cannot be resolved (default no-op behavior).
*/
export declare const BinSchema: Schema.Schema<string | HashMap.HashMap<string, string>, string | {
readonly [x: string]: string;
}>;
/**
* Service for resolving catalog: protocol references in dependency maps.
* Operates on the encoded JSON object before formatting.
* Default implementation is a no-op passthrough.
*/
export declare class CatalogResolver extends CatalogResolver_base {

@@ -46,8 +39,8 @@ }

declare const CatalogResolver_base: Context.TagClass<CatalogResolver, "package-json-effect/CatalogResolver", {
readonly resolve: (raw: Record<string, unknown>) => Effect.Effect<Record<string, unknown>>;
readonly rangeOf: (packageName: string, catalog: Option.Option<string>) => Effect.Effect<Option.Option<string>, DependencyResolutionError>;
}>;
/**
* Default CatalogResolver: no-op passthrough.
* Replace with a real implementation to resolve catalog: protocol specifiers.
* Default CatalogResolver: resolves nothing. Provide a real implementation
* (e.g. backed by workspaces-effect) to resolve catalog: specifiers.
*/

@@ -58,4 +51,11 @@ export declare const CatalogResolverLive: Layer.Layer<CatalogResolver>;

export declare class Dependency extends Dependency_base {
export declare class Dependency extends Dependency_base implements DependencyProtocolGetters {
get protocol(): Option.Option<DependencyProtocol>;
get range(): Option.Option<Range>;
get isLocal(): boolean;
get isLink(): boolean;
get isPortal(): boolean;
get isCatalog(): boolean;
get isWorkspace(): boolean;
get isUnresolved(): boolean;
get isGit(): boolean;

@@ -73,19 +73,54 @@ get isRange(): boolean;

/**
* A dependency map: plain JSON object decoded to/from HashMap.
*/
export declare const DependencyMapSchema: Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}>;
export declare type DependencyProtocol = "range" | "tag" | "git" | "url" | "npm" | "file" | "link" | "portal" | "catalog" | "workspace" | "unknown";
/** The shared protocol-classification getters implemented by every Dependency variant. */
export declare interface DependencyProtocolGetters {
readonly protocol: Option.Option<DependencyProtocol>;
readonly range: Option.Option<Range>;
readonly isLocal: boolean;
readonly isLink: boolean;
readonly isPortal: boolean;
readonly isCatalog: boolean;
readonly isWorkspace: boolean;
readonly isUnresolved: boolean;
readonly isGit: boolean;
readonly isRange: boolean;
readonly isTag: boolean;
}
/** Indicates that a catalog: or workspace: specifier could not be resolved. */
export declare class DependencyResolutionError extends DependencyResolutionErrorBase<{
/** The name of the package whose specifier could not be resolved. */
readonly packageName: string;
/** The specifier string (e.g. `catalog:` or `workspace:*`) that failed resolution. */
readonly specifier: string;
/** A human-readable description of why resolution failed. */
readonly reason: string;
}> {
get message(): string;
}
/**
* A valid dependency version specifier.
* Tagged error base for {@link DependencyResolutionError}.
*
* @privateRemarks
* Exported because TypeScript declaration bundling requires the base class to be
* accessible when {@link DependencyResolutionError} appears in public type signatures.
* Consumers should use {@link DependencyResolutionError} directly.
*
* @internal
*/
export declare const DependencySpecifier: Schema.brand<Schema.filter<typeof Schema.String>, "DependencySpecifier">;
export declare const DependencyResolutionErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & {
readonly _tag: "DependencyResolutionError";
} & Readonly<A>;
/** Branded type for dependency specifiers. */
export declare type DependencySpecifier = Schema.Schema.Type<typeof DependencySpecifier>;
export declare class DevDependency extends DevDependency_base {
export declare class DevDependency extends DevDependency_base implements DependencyProtocolGetters {
get protocol(): Option.Option<DependencyProtocol>;
get range(): Option.Option<Range>;
get isLocal(): boolean;
get isLink(): boolean;
get isPortal(): boolean;
get isCatalog(): boolean;
get isWorkspace(): boolean;
get isUnresolved(): boolean;
get isGit(): boolean;

@@ -106,3 +141,3 @@ get isRange(): boolean;

*/
export declare class DevEngine extends DevEngine_base {
declare class DevEngine extends DevEngine_base {
}

@@ -134,45 +169,3 @@

export declare type DevEngines = Schema.Schema.Type<typeof DevEnginesSchema>;
/**
* Schema for the devEngines field, modeling runtime and
* package manager constraints with optional arrays.
*/
export declare const DevEnginesSchema: Schema.Struct<{
packageManager: Schema.optionalWith<Schema.Union<[typeof DevEngine, Schema.Array$<typeof DevEngine>]>, {
as: "Option";
}>;
runtime: Schema.optionalWith<Schema.Union<[typeof DevEngine, Schema.Array$<typeof DevEngine>]>, {
as: "Option";
}>;
os: Schema.optionalWith<Schema.Union<[typeof DevEngine, Schema.Array$<typeof DevEngine>]>, {
as: "Option";
}>;
cpu: Schema.optionalWith<Schema.Union<[typeof DevEngine, Schema.Array$<typeof DevEngine>]>, {
as: "Option";
}>;
libc: Schema.optionalWith<Schema.Union<[typeof DevEngine, Schema.Array$<typeof DevEngine>]>, {
as: "Option";
}>;
}>;
/**
* Engines field: map of engine names to semver ranges.
*/
export declare const EnginesSchema: Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}>;
export declare type ExportsField = Schema.Schema.Type<typeof ExportsFieldSchema>;
/**
* Exports field: either a single string entry point or an object
* mapping subpaths to file paths (or null to block).
*
* The object form accepts any value shape to support conditional
* exports with nested objects (e.g. `{ "import": "./esm.js", "require": "./cjs.js" }`).
*/
export declare const ExportsFieldSchema: Schema.Union<[typeof Schema.String, Schema.Record$<typeof Schema.String, typeof Schema.Unknown>]>;
/**
* Indicates that a string could not be parsed as a valid dependency specifier.

@@ -229,9 +222,33 @@ */

/** Returns true if the specifier points to a git repository. */
/** Indicates that a string is not a valid SPDX license identifier or expression. */
export declare class InvalidSpdxLicenseError extends InvalidSpdxLicenseErrorBase<{
/** The raw input string that failed validation. */
readonly input: string;
/** A human-readable description of why the license identifier is invalid. */
readonly reason: string;
}> {
get message(): string;
}
/**
* Tagged error base for {@link InvalidSpdxLicenseError}.
*
* @privateRemarks
* Exported because TypeScript declaration bundling requires the base class to be
* accessible when {@link InvalidSpdxLicenseError} appears in public type signatures.
* Consumers should use {@link InvalidSpdxLicenseError} directly.
*
* @internal
*/
export declare const InvalidSpdxLicenseErrorBase: new <A extends Record<string, any> = {}>(args: VoidIfEmpty< { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & {
readonly _tag: "InvalidSpdxLicenseError";
} & Readonly<A>;
/** Returns true if the specifier resolves to a git source (git URLs and hosted-git shorthands). */
export declare const isGitSpecifier: (s: string) => boolean;
/** Returns true if the specifier points to a local file path. */
/** Returns true if the specifier points to a local path (file:, link:, portal:, or a bare path). */
export declare const isLocalSpecifier: (s: string) => boolean;
/** Returns true if the specifier is a semver range. */
/** Returns true if the specifier is a parseable semver range. */
export declare const isRangeSpecifier: (s: string) => boolean;

@@ -242,24 +259,132 @@

/**
* Validates a dependency version specifier.
*
* Accepts semver ranges, exact versions, dist-tags, URLs,
* git refs, GitHub shorthand, file paths, and npm/catalog/workspace protocols.
*/
export declare const isValidDependencySpecifier: (s: string) => boolean;
/** Type guard narrowing any dependency-like value to UnresolvedDependency, preserving the concrete type. */
export declare const isUnresolvedDependency: <T extends {
readonly isUnresolved: boolean;
}>(dep: T) => dep is T & {
readonly isUnresolved: true;
};
export declare const makePackageJsonValidatorLive: (config: {
rules: ReadonlyArray<ValidationRule>;
}) => Layer.Layer<PackageJsonValidator>;
export declare const noLocalDepsRule: ValidationRule;
export declare const noUnresolvedDepsRule: ValidationRule;
export declare class OptionalDependency extends OptionalDependency_base implements DependencyProtocolGetters {
get protocol(): Option.Option<DependencyProtocol>;
get range(): Option.Option<Range>;
get isLocal(): boolean;
get isLink(): boolean;
get isPortal(): boolean;
get isCatalog(): boolean;
get isWorkspace(): boolean;
get isUnresolved(): boolean;
get isGit(): boolean;
get isRange(): boolean;
get isTag(): boolean;
}
declare const OptionalDependency_base: Schema.TaggedClass<OptionalDependency, "OptionalDependency", {
readonly _tag: Schema.tag<"OptionalDependency">;
} & {
name: typeof Schema.String;
specifier: typeof Schema.String;
}>;
/**
* Validates a string as a valid npm package name.
*
* Rules: max 214 chars, lowercase, URL-safe characters only,
* cannot start with . or _ (unless scoped).
* Domain model for a package.json document. A Schema.Class carrying typed
* known fields plus a `rest` catch-all that preserves any unmodeled top-level
* fields for round-trip fidelity. The literal `rest` key is flattened away by
* the wire transform in src/schemas/package-json.ts.
*/
export declare const isValidPackageName: (s: string) => boolean;
export declare class Package extends Package_base {
get isPrivate(): boolean;
get isScoped(): boolean;
get isESM(): boolean;
pipe<A>(this: A): A;
pipe<A, B>(this: A, ab: (_: A) => B): B;
pipe<A, B, C>(this: A, ab: (_: A) => B, bc: (_: B) => C): C;
pipe<A, B, C, D>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D;
pipe<A, B, C, D, E>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D, de: (_: D) => E): E;
hasDependency(name: string): boolean;
getDependencies(): HashMap.HashMap<string, Dependency>;
getDevDependencies(): HashMap.HashMap<string, DevDependency>;
getPeerDependencies(): HashMap.HashMap<string, PeerDependency>;
getOptionalDependencies(): HashMap.HashMap<string, OptionalDependency>;
/** Return a new Package with the given fields replaced. */
copyWith(patch: Partial<{
name: string;
version: SemVer;
license: Option.Option<string>;
dependencies: HashMap.HashMap<string, string>;
devDependencies: HashMap.HashMap<string, string>;
peerDependencies: HashMap.HashMap<string, string>;
optionalDependencies: HashMap.HashMap<string, string>;
scripts: HashMap.HashMap<string, string>;
}>): Package;
/** Construct a Package from an already-decoded data record. */
static fromData(data: ConstructorParameters<typeof Package>[0]): Package;
static setVersion: {
(version: string): (pkg: Package) => Effect.Effect<Package, InvalidVersionError>;
(pkg: Package, version: string): Effect.Effect<Package, InvalidVersionError>;
};
static setName: {
(name: string): (pkg: Package) => Effect.Effect<Package, InvalidPackageNameError>;
(pkg: Package, name: string): Effect.Effect<Package, InvalidPackageNameError>;
};
static addDependency: {
(name: string, specifier: string): (pkg: Package) => Package;
(pkg: Package, name: string, specifier: string): Package;
};
static removeDependency: {
(name: string): (pkg: Package) => Package;
(pkg: Package, name: string): Package;
};
static addDevDependency: {
(name: string, specifier: string): (pkg: Package) => Package;
(pkg: Package, name: string, specifier: string): Package;
};
static removeDevDependency: {
(name: string): (pkg: Package) => Package;
(pkg: Package, name: string): Package;
};
static addPeerDependency: {
(name: string, specifier: string): (pkg: Package) => Package;
(pkg: Package, name: string, specifier: string): Package;
};
static removePeerDependency: {
(name: string): (pkg: Package) => Package;
(pkg: Package, name: string): Package;
};
static addOptionalDependency: {
(name: string, specifier: string): (pkg: Package) => Package;
(pkg: Package, name: string, specifier: string): Package;
};
static removeOptionalDependency: {
(name: string): (pkg: Package) => Package;
(pkg: Package, name: string): Package;
};
static setScript: {
(name: string, command: string): (pkg: Package) => Package;
(pkg: Package, name: string, command: string): Package;
};
static removeScript: {
(name: string): (pkg: Package) => Package;
(pkg: Package, name: string): Package;
};
static setLicense: {
(license: string): (pkg: Package) => Effect.Effect<Package, InvalidSpdxLicenseError>;
(pkg: Package, license: string): Effect.Effect<Package, InvalidSpdxLicenseError>;
};
/**
* Resolve catalog: and workspace: specifiers across all four dependency maps
* using the CatalogResolver and WorkspaceResolver from context. Returns a new
* Package. Specifiers the resolvers return None for are left unchanged.
*/
static resolve(pkg: Package): Effect.Effect<Package, DependencyResolutionError, WorkspaceResolver | CatalogResolver>;
}
/**
* Factory to create a PackageJsonSchema with custom field overrides.
* Overridden fields replace the default schema for that field name.
* Unknown fields are still preserved via the open record.
*/
export declare const makePackageJsonSchema: <F extends Schema.Struct.Fields>(overrides: F) => Schema.TypeLiteral<{
declare const Package_base: Schema.Class<Package, {
name: Schema.Union<[Schema.brand<Schema.filter<typeof Schema.String>, "ScopedPackageName">, Schema.brand<Schema.filter<typeof Schema.String>, "UnscopedPackageName">]>;

@@ -366,75 +491,171 @@ version: Schema.Schema<SemVer, string, never>;

}>;
} & F, readonly [{
readonly key: typeof Schema.String;
readonly value: typeof Schema.Unknown;
}]>;
export declare const makePackageJsonValidatorLive: (config: {
rules: ReadonlyArray<ValidationRule>;
}) => Layer.Layer<PackageJsonValidator>;
export declare class OptionalDependency extends OptionalDependency_base {
}
declare const OptionalDependency_base: Schema.TaggedClass<OptionalDependency, "OptionalDependency", {
readonly _tag: Schema.tag<"OptionalDependency">;
rest: Schema.optionalWith<Schema.Data<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>, {
default: () => Record<string, unknown>;
}>;
}, Schema.Struct.Encoded<{
name: Schema.Union<[Schema.brand<Schema.filter<typeof Schema.String>, "ScopedPackageName">, Schema.brand<Schema.filter<typeof Schema.String>, "UnscopedPackageName">]>;
version: Schema.Schema<SemVer, string, never>;
description: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
private: Schema.optionalWith<typeof Schema.Boolean, {
as: "Option";
}>;
type: Schema.optionalWith<Schema.Literal<["module", "commonjs"]>, {
as: "Option";
}>;
main: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
license: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
dependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
devDependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
peerDependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
optionalDependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
peerDependenciesMeta: Schema.optionalWith<Schema.Record$<typeof Schema.String, Schema.Struct<{
optional: Schema.optional<typeof Schema.Boolean>;
}>>, {
as: "Option";
}>;
scripts: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
bin: Schema.optionalWith<Schema.Schema<string | HashMap.HashMap<string, string>, string | {
readonly [x: string]: string;
}, never>, {
as: "Option";
}>;
engines: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
as: "Option";
}>;
exports: Schema.optionalWith<Schema.Union<[typeof Schema.String, Schema.Record$<typeof Schema.String, typeof Schema.Unknown>]>, {
as: "Option";
}>;
publishConfig: Schema.optionalWith<Schema.TypeLiteral<{
access: Schema.optionalWith<Schema.Literal<["public", "restricted"]>, {
as: "Option";
}>;
directory: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
registry: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
linkDirectory: Schema.optionalWith<typeof Schema.Boolean, {
as: "Option";
}>;
}, readonly [{
readonly key: typeof Schema.String;
readonly value: typeof Schema.Unknown;
}]>, {
as: "Option";
}>;
packageManager: Schema.optionalWith<Schema.Schema<PackageManager, string, never>, {
as: "Option";
}>;
devEngines: Schema.optionalWith<Schema.Struct<{
packageManager: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
runtime: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
os: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
cpu: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
libc: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
}>, {
as: "Option";
}>;
rest: Schema.optionalWith<Schema.Data<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>, {
default: () => Record<string, unknown>;
}>;
}>, never, {
readonly dependencies?: HashMap.HashMap<string, string>;
} & {
name: typeof Schema.String;
specifier: typeof Schema.String;
}>;
/**
* Domain model wrapping decoded PackageJson data. Provides getters
* for property access and helper methods for querying the package.
*/
export declare class Package {
readonly _data: PackageJsonSchemaType;
constructor(data: PackageJsonSchemaType);
get name(): string;
get version(): SemVer;
get description(): Option.Option<string>;
get isPrivate(): boolean;
get isScoped(): boolean;
get isESM(): boolean;
get license(): Option.Option<string>;
get scripts(): HashMap.HashMap<string, string>;
get dependencies(): HashMap.HashMap<string, string>;
get devDependencies(): HashMap.HashMap<string, string>;
get peerDependencies(): HashMap.HashMap<string, string>;
get optionalDependencies(): HashMap.HashMap<string, string>;
hasDependency(name: string): boolean;
static setVersion: {
(version: string): (pkg: Package) => Effect.Effect<Package, InvalidVersionError>;
(pkg: Package, version: string): Effect.Effect<Package, InvalidVersionError>;
readonly devDependencies?: HashMap.HashMap<string, string>;
} & {
readonly optionalDependencies?: HashMap.HashMap<string, string>;
} & {
readonly peerDependencies?: HashMap.HashMap<string, string>;
} & {
readonly rest?: {
readonly [x: string]: unknown;
};
static setName: {
(name: string): (pkg: Package) => Effect.Effect<Package, InvalidPackageNameError>;
(pkg: Package, name: string): Effect.Effect<Package, InvalidPackageNameError>;
};
static addDependency: {
(name: string, specifier: string): (pkg: Package) => Package;
(pkg: Package, name: string, specifier: string): Package;
};
static removeDependency: {
(name: string): (pkg: Package) => Package;
(pkg: Package, name: string): Package;
};
static setScript: {
(name: string, command: string): (pkg: Package) => Package;
(pkg: Package, name: string, command: string): Package;
};
static removeScript: {
(name: string): (pkg: Package) => Package;
(pkg: Package, name: string): Package;
};
static setLicense: {
(license: string): (pkg: Package) => Effect.Effect<Package, Error>;
(pkg: Package, license: string): Effect.Effect<Package, Error>;
};
/**
* Create a Package from any decoded PackageJson-shaped data.
* Use this when working with custom schemas from makePackageJsonSchema.
*/
static fromData(data: Record<string, unknown>): Package;
}
} & {
readonly scripts?: HashMap.HashMap<string, string>;
} & {
readonly bin: Option.Option<string | HashMap.HashMap<string, string>>;
} & {
readonly description: Option.Option<string>;
} & {
readonly devEngines: Option.Option<{
readonly packageManager: Option.Option<DevEngine | readonly DevEngine[]>;
readonly runtime: Option.Option<DevEngine | readonly DevEngine[]>;
readonly os: Option.Option<DevEngine | readonly DevEngine[]>;
readonly cpu: Option.Option<DevEngine | readonly DevEngine[]>;
readonly libc: Option.Option<DevEngine | readonly DevEngine[]>;
}>;
} & {
readonly engines: Option.Option<HashMap.HashMap<string, string>>;
} & {
readonly exports: Option.Option<string | {
readonly [x: string]: unknown;
}>;
} & {
readonly license: Option.Option<string>;
} & {
readonly main: Option.Option<string>;
} & {
readonly name: (string & Brand<"ScopedPackageName">) | (string & Brand<"UnscopedPackageName">);
} & {
readonly packageManager: Option.Option<PackageManager>;
} & {
readonly peerDependenciesMeta: Option.Option<{
readonly [x: string]: {
readonly optional?: boolean | undefined;
};
}>;
} & {
readonly private: Option.Option<boolean>;
} & {
readonly publishConfig: Option.Option<{
readonly [x: string]: unknown;
readonly access: Option.Option<"public" | "restricted">;
readonly directory: Option.Option<string>;
readonly registry: Option.Option<string>;
readonly linkDirectory: Option.Option<boolean>;
}>;
} & {
readonly type: Option.Option<"commonjs" | "module">;
} & {
readonly version: SemVer;
}, {}, {}>;

@@ -574,119 +795,2 @@ /**

/**
* The core PackageJsonSchema with typed known fields and an open record
* index signature that preserves unknown fields as unknown values.
*/
export declare const PackageJsonSchema: Schema.TypeLiteral<{
name: Schema.Union<[Schema.brand<Schema.filter<typeof Schema.String>, "ScopedPackageName">, Schema.brand<Schema.filter<typeof Schema.String>, "UnscopedPackageName">]>;
version: Schema.Schema<SemVer, string, never>;
description: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
private: Schema.optionalWith<typeof Schema.Boolean, {
as: "Option";
}>;
type: Schema.optionalWith<Schema.Literal<["module", "commonjs"]>, {
as: "Option";
}>;
main: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
license: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
dependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
devDependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
peerDependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
optionalDependencies: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
peerDependenciesMeta: Schema.optionalWith<Schema.Record$<typeof Schema.String, Schema.Struct<{
optional: Schema.optional<typeof Schema.Boolean>;
}>>, {
as: "Option";
}>;
scripts: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
default: () => HashMap.HashMap<string, string>;
}>;
bin: Schema.optionalWith<Schema.Schema<string | HashMap.HashMap<string, string>, string | {
readonly [x: string]: string;
}, never>, {
as: "Option";
}>;
engines: Schema.optionalWith<Schema.Schema<HashMap.HashMap<string, string>, {
readonly [x: string]: string;
}, never>, {
as: "Option";
}>;
exports: Schema.optionalWith<Schema.Union<[typeof Schema.String, Schema.Record$<typeof Schema.String, typeof Schema.Unknown>]>, {
as: "Option";
}>;
publishConfig: Schema.optionalWith<Schema.TypeLiteral<{
access: Schema.optionalWith<Schema.Literal<["public", "restricted"]>, {
as: "Option";
}>;
directory: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
registry: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
linkDirectory: Schema.optionalWith<typeof Schema.Boolean, {
as: "Option";
}>;
}, readonly [{
readonly key: typeof Schema.String;
readonly value: typeof Schema.Unknown;
}]>, {
as: "Option";
}>;
packageManager: Schema.optionalWith<Schema.Schema<PackageManager, string, never>, {
as: "Option";
}>;
devEngines: Schema.optionalWith<Schema.Struct<{
packageManager: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
runtime: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
os: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
cpu: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
libc: Schema.optionalWith<Schema.Union<[DevEngine, Schema.Array$<DevEngine>]>, {
as: "Option";
}>;
}>, {
as: "Option";
}>;
}, readonly [{
readonly key: typeof Schema.String;
readonly value: typeof Schema.Unknown;
}]>;
/** Encoded type for PackageJsonSchema (plain JSON). */
export declare type PackageJsonSchemaEncoded = Schema.Schema.Encoded<typeof PackageJsonSchema>;
/** Decoded type for PackageJsonSchema. */
export declare type PackageJsonSchemaType = Schema.Schema.Type<typeof PackageJsonSchema>;
/**
* Service for transforming the encoded package.json object before formatting.

@@ -782,3 +886,3 @@ * Default implementation removes empty dependency map fields.

export declare class PackageManager extends PackageManager_base {
declare class PackageManager extends PackageManager_base {
get hasIntegrity(): boolean;

@@ -808,17 +912,8 @@ }

/**
* Parses a packageManager string (e.g. "pnpm\@10.33.0+sha512.abc")
* into a PackageManager class instance with name, version, and
* optional integrity fields.
*/
export declare const PackageManagerSchema: Schema.Schema<PackageManager, string>;
/**
* A valid npm package name, either scoped or unscoped.
*/
declare const PackageName: Schema.Union<[Schema.brand<Schema.filter<typeof Schema.String>, "ScopedPackageName">, Schema.brand<Schema.filter<typeof Schema.String>, "UnscopedPackageName">]>;
export declare const PackageName: Schema.Union<[Schema.brand<Schema.filter<typeof Schema.String>, "ScopedPackageName">, Schema.brand<Schema.filter<typeof Schema.String>, "UnscopedPackageName">]>;
/** Branded type for any valid package name. */
declare type PackageName = Schema.Schema.Type<typeof PackageName>;
export { PackageName }
export { PackageName as PackageNameType }
export declare type PackageName = Schema.Schema.Type<typeof PackageName>;

@@ -834,3 +929,17 @@ /**

export declare class PeerDependency extends PeerDependency_base {
/** Parse the specifier as a semver Range, returning None when it is not a range. */
export declare const parseRangeOption: (s: string) => Option.Option<Range>;
export declare class PeerDependency extends PeerDependency_base implements DependencyProtocolGetters {
get protocol(): Option.Option<DependencyProtocol>;
get range(): Option.Option<Range>;
get isLocal(): boolean;
get isLink(): boolean;
get isPortal(): boolean;
get isCatalog(): boolean;
get isWorkspace(): boolean;
get isUnresolved(): boolean;
get isGit(): boolean;
get isRange(): boolean;
get isTag(): boolean;
}

@@ -846,93 +955,30 @@

/**
* Structured person object with name, optional email and url.
*/
export declare class Person extends Person_base {
/** Classify a specifier string into a single protocol; "unknown" for unrecognized input. */
export declare const protocolOf: (s: string) => DependencyProtocol;
export declare interface RuleFailure {
readonly message: string;
readonly path?: Option.Option<string>;
}
declare const Person_base: Schema.Class<Person, {
name: typeof Schema.String;
email: Schema.optional<typeof Schema.String>;
url: Schema.optional<typeof Schema.String>;
}, Schema.Struct.Encoded<{
name: typeof Schema.String;
email: Schema.optional<typeof Schema.String>;
url: Schema.optional<typeof Schema.String>;
}>, never, {
readonly email?: string | undefined;
} & {
readonly url?: string | undefined;
} & {
readonly name: string;
}, {}, {}>;
/**
* Person field: either a string "Name email (url)" shorthand or a
* structured object with name, email, and url fields.
* Always decoded to the object form.
*/
export declare const PersonSchema: Schema.Union<[typeof Person, Schema.transform<typeof Schema.String, Schema.SchemaClass<Person, Person, never>>]>;
export declare type PublishConfig = Schema.Schema.Type<typeof PublishConfigSchema>;
/**
* Schema for the publishConfig field with typed known fields
* and an open record for arbitrary extensions like targets.
*/
export declare const PublishConfigSchema: Schema.TypeLiteral<{
access: Schema.optionalWith<Schema.Literal<["public", "restricted"]>, {
as: "Option";
}>;
directory: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
registry: Schema.optionalWith<typeof Schema.String, {
as: "Option";
}>;
linkDirectory: Schema.optionalWith<typeof Schema.Boolean, {
as: "Option";
}>;
}, readonly [{
readonly key: typeof Schema.String;
readonly value: typeof Schema.Unknown;
}]>;
/**
* A valid npm scoped package name (starts with \@scope/).
*/
declare const ScopedPackageName: Schema.brand<Schema.filter<typeof Schema.String>, "ScopedPackageName">;
export declare const ScopedPackageName: Schema.brand<Schema.filter<typeof Schema.String>, "ScopedPackageName">;
/** Branded type for scoped package names. */
declare type ScopedPackageName = Schema.Schema.Type<typeof ScopedPackageName>;
export { ScopedPackageName }
export { ScopedPackageName as ScopedPackageNameType }
export declare type ScopedPackageName = Schema.Schema.Type<typeof ScopedPackageName>;
/**
* Scripts field: plain JSON object decoded to/from HashMap of string to string.
* Same structure as DependencyMapSchema.
*/
export declare const ScriptsSchema: Schema_2<HashMap_2<string, string>, {
readonly [x: string]: string;
}, never>;
/** A Dependency whose specifier is an unresolved catalog: or workspace: protocol. */
export declare type UnresolvedDependency = Dependency & {
readonly isUnresolved: true;
};
/**
* A valid SPDX license identifier, expression, "UNLICENSED",
* or "SEE LICENSE IN filename".
*/
declare const SpdxLicense: Schema.brand<Schema.filter<typeof Schema.String>, "SpdxLicense">;
/** Branded type for SPDX license strings. */
declare type SpdxLicense = Schema.Schema.Type<typeof SpdxLicense>;
export { SpdxLicense }
export { SpdxLicense as SpdxLicenseType }
/**
* A valid npm unscoped package name (does not start with \@).
*/
declare const UnscopedPackageName: Schema.brand<Schema.filter<typeof Schema.String>, "UnscopedPackageName">;
export declare const UnscopedPackageName: Schema.brand<Schema.filter<typeof Schema.String>, "UnscopedPackageName">;
/** Branded type for unscoped package names. */
declare type UnscopedPackageName = Schema.Schema.Type<typeof UnscopedPackageName>;
export { UnscopedPackageName }
export { UnscopedPackageName as UnscopedPackageNameType }
export declare type UnscopedPackageName = Schema.Schema.Type<typeof UnscopedPackageName>;

@@ -944,5 +990,3 @@ /**

readonly name: string;
readonly validate: (pkg: Package) => Effect.Effect<void, {
readonly message: string;
}>;
readonly validate: (pkg: Package) => Effect.Effect<void, RuleFailure>;
}

@@ -963,12 +1007,6 @@

/**
* Schema that decodes a version string into a SemVer instance
* and encodes it back to a string. Uses semver-effect for parsing.
* Resolves workspace: protocol specifiers. Given a workspace package name,
* returns its concrete version (without range modifier), or None if it cannot
* be resolved (default no-op behavior).
*/
export declare const VersionSchema: Schema.Schema<SemVer, string>;
/**
* Service for resolving workspace: protocol references in dependency maps.
* Operates on the encoded JSON object before formatting.
* Default implementation is a no-op passthrough.
*/
export declare class WorkspaceResolver extends WorkspaceResolver_base {

@@ -978,8 +1016,8 @@ }

declare const WorkspaceResolver_base: Context.TagClass<WorkspaceResolver, "package-json-effect/WorkspaceResolver", {
readonly resolve: (raw: Record<string, unknown>) => Effect.Effect<Record<string, unknown>>;
readonly versionOf: (packageName: string) => Effect.Effect<Option.Option<string>, DependencyResolutionError>;
}>;
/**
* Default WorkspaceResolver: no-op passthrough.
* Replace with a real implementation to resolve workspace: protocol specifiers.
* Default WorkspaceResolver: resolves nothing. Provide a real implementation
* (e.g. backed by workspaces-effect) to resolve workspace: specifiers.
*/

@@ -986,0 +1024,0 @@ export declare const WorkspaceResolverLive: Layer.Layer<WorkspaceResolver>;

+63
-464

@@ -1,203 +0,4 @@

import { Context, Data, Effect, HashMap, Layer, Option, ParseResult, Schema } from "effect";
import { dual } from "effect/Function";
import { SemVer, parseValidSemVer } from "semver-effect";
import spdx_expression_parse from "spdx-expression-parse";
import { Context, Data, Effect, HashMap, Layer, Option, Schema } from "effect";
import { FileSystem } from "@effect/platform";
const isLocalSpecifier = (s)=>s.startsWith("file:");
const isGitSpecifier = (s)=>s.startsWith("git+") || s.startsWith("git://") || s.startsWith("github:");
const isRangeSpecifier = (s)=>/^[\d^~>=<*xX|]/.test(s);
const isTagSpecifier = (s)=>{
if (isLocalSpecifier(s) || isGitSpecifier(s) || isRangeSpecifier(s)) return false;
if (s.startsWith("http")) return false;
if (s.startsWith("npm:") || s.startsWith("catalog:") || s.startsWith("workspace:")) return false;
if (s.includes("/")) return false;
return /^[a-zA-Z]/.test(s);
};
class Dependency extends Schema.TaggedClass()("Dependency", {
name: Schema.String,
specifier: Schema.String
}) {
get isLocal() {
return isLocalSpecifier(this.specifier);
}
get isGit() {
return isGitSpecifier(this.specifier);
}
get isRange() {
return isRangeSpecifier(this.specifier);
}
get isTag() {
return isTagSpecifier(this.specifier);
}
}
class DevDependency extends Schema.TaggedClass()("DevDependency", {
name: Schema.String,
specifier: Schema.String
}) {
get isLocal() {
return isLocalSpecifier(this.specifier);
}
get isGit() {
return isGitSpecifier(this.specifier);
}
get isRange() {
return isRangeSpecifier(this.specifier);
}
get isTag() {
return isTagSpecifier(this.specifier);
}
}
class OptionalDependency extends Schema.TaggedClass()("OptionalDependency", {
name: Schema.String,
specifier: Schema.String
}) {
}
const InvalidPackageNameErrorBase = Data.TaggedError("InvalidPackageNameError");
class InvalidPackageNameError extends InvalidPackageNameErrorBase {
get message() {
return `Invalid package name "${this.input}": ${this.reason}`;
}
}
const isValidSpdx = (s)=>{
if ("UNLICENSED" === s) return true;
if (s.startsWith("SEE LICENSE IN ") && s.length > 15) return true;
try {
spdx_expression_parse(s);
return true;
} catch {
return false;
}
};
const SpdxLicense = Schema.String.pipe(Schema.filter((s)=>isValidSpdx(s) || "Expected a valid SPDX license identifier or expression"), Schema.brand("SpdxLicense"));
const isValidPackageName = (s)=>{
if (0 === s.length || s.length > 214) return false;
if (s.startsWith("@")) {
const slashIndex = s.indexOf("/");
if (-1 === slashIndex || 1 === slashIndex || slashIndex === s.length - 1) return false;
if (-1 !== s.indexOf("/", slashIndex + 1)) return false;
const scope = s.slice(1, slashIndex);
const name = s.slice(slashIndex + 1);
return isValidNameChars(scope) && isValidNameChars(name);
}
if (s.startsWith(".") || s.startsWith("_")) return false;
return isValidNameChars(s);
};
const isValidNameChars = (s)=>/^[a-z0-9._-]+$/.test(s);
const ScopedPackageName = Schema.String.pipe(Schema.filter((s)=>s.startsWith("@") && isValidPackageName(s) || "Expected a scoped package name (@scope/name)"), Schema.brand("ScopedPackageName"));
const UnscopedPackageName = Schema.String.pipe(Schema.filter((s)=>!s.startsWith("@") && isValidPackageName(s) || "Expected an unscoped package name"), Schema.brand("UnscopedPackageName"));
const PackageName = Schema.Union(ScopedPackageName, UnscopedPackageName);
const PackageNameUtil = {
scope: (name)=>{
if (!name.startsWith("@")) return Option.none();
const slashIndex = name.indexOf("/");
if (-1 === slashIndex) return Option.none();
return Option.some(name.slice(1, slashIndex));
},
unscoped: (name)=>{
if (!name.startsWith("@")) return name;
const slashIndex = name.indexOf("/");
return -1 === slashIndex ? name : name.slice(slashIndex + 1);
},
isScoped: (name)=>name.startsWith("@")
};
class Package {
_data;
constructor(data){
this._data = data;
}
get name() {
return this._data.name;
}
get version() {
return this._data.version;
}
get description() {
return this._data.description;
}
get isPrivate() {
return Option.getOrElse(this._data.private, ()=>false);
}
get isScoped() {
return PackageNameUtil.isScoped(this._data.name);
}
get isESM() {
return Option.match(this._data.type, {
onNone: ()=>false,
onSome: (t)=>"module" === t
});
}
get license() {
return this._data.license;
}
get scripts() {
return this._data.scripts;
}
get dependencies() {
return this._data.dependencies;
}
get devDependencies() {
return this._data.devDependencies;
}
get peerDependencies() {
return this._data.peerDependencies;
}
get optionalDependencies() {
return this._data.optionalDependencies;
}
hasDependency(name) {
return HashMap.has(this._data.dependencies, name) || HashMap.has(this._data.devDependencies, name) || HashMap.has(this._data.peerDependencies, name) || HashMap.has(this._data.optionalDependencies, name);
}
static setVersion = dual(2, (pkg, version)=>parseValidSemVer(version).pipe(Effect.map((semver)=>new Package({
...pkg._data,
version: semver
}))));
static setName = dual(2, (pkg, name)=>isValidPackageName(name) ? Effect.succeed(new Package({
...pkg._data,
name
})) : Effect.fail(new InvalidPackageNameError({
input: name,
reason: "Does not satisfy npm naming rules"
})));
static addDependency = dual(3, (pkg, name, specifier)=>new Package({
...pkg._data,
dependencies: HashMap.set(pkg._data.dependencies, name, specifier)
}));
static removeDependency = dual(2, (pkg, name)=>new Package({
...pkg._data,
dependencies: HashMap.remove(pkg._data.dependencies, name)
}));
static setScript = dual(3, (pkg, name, command)=>new Package({
...pkg._data,
scripts: HashMap.set(pkg._data.scripts, name, command)
}));
static removeScript = dual(2, (pkg, name)=>new Package({
...pkg._data,
scripts: HashMap.remove(pkg._data.scripts, name)
}));
static setLicense = dual(2, (pkg, license)=>Effect["try"]({
try: ()=>{
Schema.decodeUnknownSync(SpdxLicense)(license);
return new Package({
...pkg._data,
license: Option.some(license)
});
},
catch: ()=>new Error(`Invalid SPDX license: "${license}"`)
}));
static fromData(data) {
return new Package(data);
}
}
class PeerDependency extends Schema.TaggedClass()("PeerDependency", {
name: Schema.String,
specifier: Schema.String,
isOptional: Schema.Boolean
}) {
}
const InvalidDependencySpecifierErrorBase = Data.TaggedError("InvalidDependencySpecifierError");
class InvalidDependencySpecifierError extends InvalidDependencySpecifierErrorBase {
get message() {
return `Invalid dependency specifier "${this.input}": ${this.reason}`;
}
}
import { CatalogResolver, WorkspaceResolver, Package, PackageJsonSchema } from "./471.js";
const PackageJsonDecodeErrorBase = Data.TaggedError("PackageJsonDecodeError");

@@ -247,6 +48,4 @@ class PackageJsonDecodeError extends PackageJsonDecodeErrorBase {

}
class CatalogResolver extends Context.Tag("package-json-effect/CatalogResolver")() {
}
const CatalogResolverLive = Layer.succeed(CatalogResolver, CatalogResolver.of({
resolve: (raw)=>Effect.succeed(raw)
rangeOf: ()=>Effect.succeed(Option.none())
}));

@@ -332,197 +131,2 @@ class PackageJsonFormatter extends Context.Tag("package-json-effect/PackageJsonFormatter")() {

}));
const BinMapSchema = Schema.transform(Schema.Record({
key: Schema.String,
value: Schema.String
}), Schema.typeSchema(Schema.HashMap({
key: Schema.String,
value: Schema.String
})), {
strict: true,
decode: (record)=>HashMap.fromIterable(Object.entries(record)),
encode: (map)=>Object.fromEntries(HashMap.toEntries(map))
});
const BinSchema = Schema.Union(Schema.String, BinMapSchema);
const StringRecord = Schema.Record({
key: Schema.String,
value: Schema.String
});
const DependencyMapSchema = Schema.transform(StringRecord, Schema.typeSchema(Schema.HashMap({
key: Schema.String,
value: Schema.String
})), {
strict: true,
decode: (record)=>HashMap.fromIterable(Object.entries(record)),
encode: (map)=>Object.fromEntries(HashMap.toEntries(map))
});
const OnFail = Schema.Literal("warn", "error", "ignore");
class DevEngine extends Schema.Class("DevEngine")({
name: Schema.String,
version: Schema.optionalWith(Schema.String, {
as: "Option"
}),
onFail: Schema.optionalWith(OnFail, {
as: "Option"
})
}) {
}
const DevEngineOrArray = Schema.Union(DevEngine, Schema.Array(DevEngine));
const DevEnginesSchema = Schema.Struct({
packageManager: Schema.optionalWith(DevEngineOrArray, {
as: "Option"
}),
runtime: Schema.optionalWith(DevEngineOrArray, {
as: "Option"
}),
os: Schema.optionalWith(DevEngineOrArray, {
as: "Option"
}),
cpu: Schema.optionalWith(DevEngineOrArray, {
as: "Option"
}),
libc: Schema.optionalWith(DevEngineOrArray, {
as: "Option"
})
});
const EnginesSchema = Schema.transform(Schema.Record({
key: Schema.String,
value: Schema.String
}), Schema.typeSchema(Schema.HashMap({
key: Schema.String,
value: Schema.String
})), {
strict: true,
decode: (record)=>HashMap.fromIterable(Object.entries(record)),
encode: (map)=>Object.fromEntries(HashMap.toEntries(map))
});
const ExportsObject = Schema.Record({
key: Schema.String,
value: Schema.Unknown
});
const ExportsFieldSchema = Schema.Union(Schema.String, ExportsObject);
class PackageManager extends Schema.Class("PackageManager")({
name: Schema.String,
version: Schema.String,
integrity: Schema.optionalWith(Schema.String, {
as: "Option"
})
}) {
get hasIntegrity() {
return Option.isSome(this.integrity);
}
}
const PACKAGE_MANAGER_REGEX = /^([a-z]+)@(\d+\.\d+\.\d+(?:-[a-zA-Z0-9._-]+)?)(?:\+(.+))?$/;
const PackageManagerSchema = Schema.transformOrFail(Schema.String, Schema.typeSchema(PackageManager), {
strict: true,
decode: (s, _options, ast)=>{
const match = s.match(PACKAGE_MANAGER_REGEX);
if (!match) return ParseResult.fail(new ParseResult.Type(ast, s, `Invalid packageManager format: "${s}"`));
return ParseResult.succeed(new PackageManager({
name: match[1],
version: match[2],
integrity: match[3] ? Option.some(match[3]) : Option.none()
}, true));
},
encode: (pm)=>{
let result = `${pm.name}@${pm.version}`;
if (Option.isSome(pm.integrity)) result += `+${pm.integrity.value}`;
return ParseResult.succeed(result);
}
});
const PublishConfigSchema = Schema.Struct({
access: Schema.optionalWith(Schema.Literal("public", "restricted"), {
as: "Option"
}),
directory: Schema.optionalWith(Schema.String, {
as: "Option"
}),
registry: Schema.optionalWith(Schema.String, {
as: "Option"
}),
linkDirectory: Schema.optionalWith(Schema.Boolean, {
as: "Option"
})
}, {
key: Schema.String,
value: Schema.Unknown
});
const ScriptsSchema = DependencyMapSchema;
const VersionSchema = Schema.transformOrFail(Schema.String, Schema.typeSchema(SemVer), {
strict: true,
decode: (input, _options, ast)=>parseValidSemVer(input).pipe(Effect.mapError(()=>new ParseResult.Type(ast, input, `Invalid semver version: "${input}"`))),
encode: (semver)=>ParseResult.succeed(semver.toString())
});
const defaultFields = {
name: PackageName,
version: VersionSchema,
description: Schema.optionalWith(Schema.String, {
as: "Option"
}),
private: Schema.optionalWith(Schema.Boolean, {
as: "Option"
}),
type: Schema.optionalWith(Schema.Literal("module", "commonjs"), {
as: "Option"
}),
main: Schema.optionalWith(Schema.String, {
as: "Option"
}),
license: Schema.optionalWith(Schema.String, {
as: "Option"
}),
dependencies: Schema.optionalWith(DependencyMapSchema, {
default: ()=>HashMap.empty()
}),
devDependencies: Schema.optionalWith(DependencyMapSchema, {
default: ()=>HashMap.empty()
}),
peerDependencies: Schema.optionalWith(DependencyMapSchema, {
default: ()=>HashMap.empty()
}),
optionalDependencies: Schema.optionalWith(DependencyMapSchema, {
default: ()=>HashMap.empty()
}),
peerDependenciesMeta: Schema.optionalWith(Schema.Record({
key: Schema.String,
value: Schema.Struct({
optional: Schema.optional(Schema.Boolean)
})
}), {
as: "Option"
}),
scripts: Schema.optionalWith(ScriptsSchema, {
default: ()=>HashMap.empty()
}),
bin: Schema.optionalWith(BinSchema, {
as: "Option"
}),
engines: Schema.optionalWith(EnginesSchema, {
as: "Option"
}),
exports: Schema.optionalWith(ExportsFieldSchema, {
as: "Option"
}),
publishConfig: Schema.optionalWith(PublishConfigSchema, {
as: "Option"
}),
packageManager: Schema.optionalWith(PackageManagerSchema, {
as: "Option"
}),
devEngines: Schema.optionalWith(DevEnginesSchema, {
as: "Option"
})
};
const PackageJsonSchema = Schema.Struct(defaultFields, {
key: Schema.String,
value: Schema.Unknown
});
const makePackageJsonSchema = (overrides)=>{
const merged = {
...defaultFields,
...overrides
};
return Schema.Struct(merged, {
key: Schema.String,
value: Schema.Unknown
});
};
class PackageJsonReader extends Context.Tag("package-json-effect/PackageJsonReader")() {

@@ -555,7 +159,7 @@ }

});
const decoded = yield* Schema.decodeUnknown(PackageJsonSchema)(json).pipe(Effect.mapError((cause)=>new PackageJsonDecodeError({
const pkg = yield* Schema.decodeUnknown(PackageJsonSchema)(json).pipe(Effect.mapError((cause)=>new PackageJsonDecodeError({
input: json,
message: cause.message ?? "Failed to decode package.json"
})));
return new Package(decoded);
return pkg;
})

@@ -590,3 +194,4 @@ });

validate: (pkg)=>Option.isSome(pkg.license) ? Effect["void"] : Effect.fail({
message: "Missing license field"
message: "Missing license field",
path: Option.some("license")
})

@@ -597,8 +202,48 @@ };

validate: (pkg)=>Option.isSome(pkg.description) ? Effect["void"] : Effect.fail({
message: "Missing description field"
message: "Missing description field",
path: Option.some("description")
})
};
const hasRepository = {
name: "has-repository",
validate: (pkg)=>Object.hasOwn(pkg, "repository") || Object.hasOwn(pkg.rest, "repository") ? Effect["void"] : Effect.fail({
message: "Missing repository field",
path: Option.some("repository")
})
};
const notPrivate = {
name: "not-private",
validate: (pkg)=>pkg.isPrivate ? Effect.fail({
message: "Package is private",
path: Option.some("private")
}) : Effect["void"]
};
const anyDepMatches = (pkg, pred)=>{
const maps = [
pkg.dependencies,
pkg.devDependencies,
pkg.peerDependencies,
pkg.optionalDependencies
];
return maps.some((m)=>Array.from(HashMap.values(m)).some(pred));
};
const noUnresolvedDepsRule = {
name: "no-unresolved-deps",
validate: (pkg)=>anyDepMatches(pkg, (s)=>s.startsWith("workspace:") || s.startsWith("catalog:")) ? Effect.fail({
message: "Unresolved workspace:/catalog: dependency",
path: Option.none()
}) : Effect["void"]
};
const noLocalDepsRule = {
name: "no-local-deps",
validate: (pkg)=>anyDepMatches(pkg, (s)=>s.startsWith("file:") || s.startsWith("link:") || s.startsWith("portal:")) ? Effect.fail({
message: "Local file:/link:/portal: dependency",
path: Option.none()
}) : Effect["void"]
};
const defaultRules = [
hasLicense,
hasDescription
hasDescription,
hasRepository,
notPrivate
];

@@ -613,3 +258,3 @@ const runRules = (pkg, rules)=>Effect.gen(function*() {

message: err.message,
path: Option.none()
path: err.path ?? Option.none()
});

@@ -619,5 +264,5 @@ return Effect["void"];

}));
if (failures.length > 0) return yield* Effect.fail(new PackageJsonValidationError({
if (failures.length > 0) return yield* new PackageJsonValidationError({
failures
}));
});
return pkg;

@@ -633,20 +278,19 @@ });

}
class WorkspaceResolver extends Context.Tag("package-json-effect/WorkspaceResolver")() {
}
const PackageJsonWriterLive = Layer.effect(PackageJsonWriter, Effect.gen(function*() {
const fs = yield* FileSystem.FileSystem;
const formatter = yield* PackageJsonFormatter;
const catalogResolver = yield* CatalogResolver;
const workspaceResolver = yield* WorkspaceResolver;
const transformer = yield* PackageJsonTransformer;
const catalog = yield* CatalogResolver;
const workspace = yield* WorkspaceResolver;
return PackageJsonWriter.of({
write: (target, pkg)=>Effect.gen(function*() {
const encoded = yield* Schema.encode(PackageJsonSchema)(pkg._data).pipe(Effect.mapError((cause)=>new PackageJsonWriteError({
const resolved = yield* Package.resolve(pkg).pipe(Effect.provideService(CatalogResolver, catalog), Effect.provideService(WorkspaceResolver, workspace), Effect.mapError((cause)=>new PackageJsonWriteError({
target,
cause
})));
let raw = encoded;
raw = yield* catalogResolver.resolve(raw);
raw = yield* workspaceResolver.resolve(raw);
raw = yield* transformer.transform(raw);
const encoded = yield* Schema.encode(PackageJsonSchema)(resolved).pipe(Effect.mapError((cause)=>new PackageJsonWriteError({
target,
cause
})));
const raw = yield* transformer.transform(encoded);
const formatted = formatter.format(raw);

@@ -662,51 +306,6 @@ const json = `${JSON.stringify(formatted, null, 2)}\n`;

const WorkspaceResolverLive = Layer.succeed(WorkspaceResolver, WorkspaceResolver.of({
resolve: (raw)=>Effect.succeed(raw)
versionOf: ()=>Effect.succeed(Option.none())
}));
const PackageJsonLive = Layer.mergeAll(PackageJsonReaderLive, PackageJsonWriterLive.pipe(Layer.provide(PackageJsonFormatterLive), Layer.provide(CatalogResolverLive), Layer.provide(WorkspaceResolverLive), Layer.provide(PackageJsonTransformerLive)), PackageJsonFormatterLive, CatalogResolverLive, WorkspaceResolverLive, PackageJsonTransformerLive, PackageJsonValidatorLive);
const isValidDependencySpecifier = (s)=>{
if (0 === s.length) return false;
if (s.startsWith("file:")) return true;
if (s.startsWith("git+")) return true;
if (s.startsWith("git://")) return true;
if (s.startsWith("http://") || s.startsWith("https://")) return true;
if (s.startsWith("npm:")) return true;
if (s.startsWith("catalog:")) return true;
if (s.startsWith("workspace:")) return true;
if (/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+(#.*)?$/.test(s)) return true;
if (/^[\d^~>=<*|xX]/.test(s)) return true;
if (/^[a-zA-Z][a-zA-Z0-9._-]*$/.test(s)) return true;
return false;
};
Schema.String.pipe(Schema.filter((s)=>isValidDependencySpecifier(s) || "Expected a valid dependency specifier"), Schema.brand("DependencySpecifier"));
class Person extends Schema.Class("Person")({
name: Schema.String,
email: Schema.optional(Schema.String),
url: Schema.optional(Schema.String)
}) {
}
const parsePersonString = (s)=>{
const emailMatch = s.match(/<([^>]+)>/);
const urlMatch = s.match(/\(([^)]+)\)/);
let name = s;
if (emailMatch) name = name.replace(emailMatch[0], "");
if (urlMatch) name = name.replace(urlMatch[0], "");
name = name.trim();
const fields = {
name
};
if (emailMatch) fields.email = emailMatch[1];
if (urlMatch) fields.url = urlMatch[1];
return new Person(fields, true);
};
const PersonFromString = Schema.transform(Schema.String, Schema.typeSchema(Person), {
strict: true,
decode: (s)=>parsePersonString(s),
encode: (p)=>{
let result = p.name;
if (p.email) result += ` <${p.email}>`;
if (p.url) result += ` (${p.url})`;
return result;
}
});
const PersonSchema = Schema.Union(Person, PersonFromString);
export { BinSchema, CatalogResolver, CatalogResolverLive, Dependency, DependencyMapSchema, DevDependency, DevEngine, DevEnginesSchema, EnginesSchema, ExportsFieldSchema, InvalidDependencySpecifierError, InvalidDependencySpecifierErrorBase, InvalidPackageNameError, InvalidPackageNameErrorBase, OptionalDependency, Package, PackageJsonDecodeError, PackageJsonDecodeErrorBase, PackageJsonFormatter, PackageJsonFormatterLive, PackageJsonLive, PackageJsonNotFoundError, PackageJsonNotFoundErrorBase, PackageJsonParseError, PackageJsonParseErrorBase, PackageJsonReadError, PackageJsonReadErrorBase, PackageJsonReader, PackageJsonReaderLive, PackageJsonSchema, PackageJsonTransformer, PackageJsonTransformerLive, PackageJsonValidationError, PackageJsonValidationErrorBase, PackageJsonValidator, PackageJsonValidatorLive, PackageJsonWriteError, PackageJsonWriteErrorBase, PackageJsonWriter, PackageJsonWriterLive, PackageManager, PackageManagerSchema, PackageName, PackageNameUtil, PeerDependency, Person, PersonSchema, PublishConfigSchema, ScopedPackageName, ScriptsSchema, SpdxLicense, UnscopedPackageName, VersionSchema, WorkspaceResolver, WorkspaceResolverLive, defaultRules, isGitSpecifier, isLocalSpecifier, isRangeSpecifier, isTagSpecifier, isValidDependencySpecifier, isValidPackageName, makePackageJsonSchema, makePackageJsonValidatorLive };
export { CatalogResolver, Dependency, DependencyResolutionError, DependencyResolutionErrorBase, DevDependency, InvalidDependencySpecifierError, InvalidDependencySpecifierErrorBase, InvalidPackageNameError, InvalidPackageNameErrorBase, InvalidSpdxLicenseError, InvalidSpdxLicenseErrorBase, OptionalDependency, Package, PackageNameUtil, PeerDependency, WorkspaceResolver, isGitSpecifier, isLocalSpecifier, isRangeSpecifier, isTagSpecifier, isUnresolvedDependency, parseRangeOption, protocolOf } from "./471.js";
export { CatalogResolverLive, PackageJsonDecodeError, PackageJsonDecodeErrorBase, PackageJsonFormatter, PackageJsonFormatterLive, PackageJsonLive, PackageJsonNotFoundError, PackageJsonNotFoundErrorBase, PackageJsonParseError, PackageJsonParseErrorBase, PackageJsonReadError, PackageJsonReadErrorBase, PackageJsonReader, PackageJsonReaderLive, PackageJsonTransformer, PackageJsonTransformerLive, PackageJsonValidationError, PackageJsonValidationErrorBase, PackageJsonValidator, PackageJsonValidatorLive, PackageJsonWriteError, PackageJsonWriteErrorBase, PackageJsonWriter, PackageJsonWriterLive, WorkspaceResolverLive, defaultRules, makePackageJsonValidatorLive, noLocalDepsRule, noUnresolvedDepsRule };
{
"name": "package-json-effect",
"version": "0.1.0",
"version": "0.2.0",
"private": false,

@@ -22,2 +22,6 @@ "description": "Utility library for working with package.json files in Effect",

"import": "./index.js"
},
"./schema": {
"types": "./schema.d.ts",
"import": "./schema.js"
}

@@ -37,2 +41,3 @@ },

"!tsdoc.json",
"471.js",
"LICENSE",

@@ -43,4 +48,6 @@ "README.md",

"package.json",
"schema.d.ts",
"schema.js",
"tsdoc-metadata.json"
]
}
+44
-96
# package-json-effect
Effect-TS library for reading, writing, parsing, and manipulating package.json files.
[![npm](https://img.shields.io/npm/v/package-json-effect?label=npm&color=cb3837)](https://www.npmjs.com/package/package-json-effect)
[![License: MIT](https://img.shields.io/badge/License-MIT-4caf50.svg)](https://opensource.org/licenses/MIT)
An [Effect](https://effect.website) library for reading, writing, parsing, validating and normalizing `package.json` files. It decodes a file into a typed `Package` model — `version` becomes a `SemVer`, optional fields become `Option`, dependency maps become `HashMap` — and writes it back with consistent key ordering. Reading, formatting, validation and dependency resolution are each a swappable `Layer`, so you keep the parts you want and replace the rest.
## Features
- Typed schemas for all standard package.json fields, with branded types for package names, versions, and SPDX licenses
- `Package` domain class with property getters and dual-API mutation methods (data-first and pipeable)
- Swappable services for reading, writing, formatting, transforming, and validating — swap any step without touching the others
- `sort-package-json`-style key ordering and alphabetical dependency sorting on write
- `makePackageJsonSchema` factory for adding custom field schemas while preserving all standard fields
- SemVer integration via `semver-effect` — the `version` field decodes to a typed `SemVer` instance
- Typed `Package` model decoded from a `package.json` file: `version` is a `SemVer`, optional fields are `Option`, dependency and script maps are `HashMap`
- Computed getters (`isScoped`, `isESM`, `isPrivate`, `hasDependency`) and a dual-API for immutable mutations (data-first `Package.setVersion(pkg, v)`, curried `Package.setVersion(v)(pkg)` and pipeable `pkg.pipe(Package.setVersion(v))`)
- `Dependency`, `DevDependency`, `PeerDependency` and `OptionalDependency` instances with a protocol taxonomy (`isRange`, `isTag`, `isGit`, `isLocal`, `isWorkspace`, `isCatalog`) that classifies any specifier
- A reader and writer backed by `@effect/platform` FileSystem, with `sort-package-json`-style key ordering and alphabetical dependency sorting applied on write
- Unknown top-level fields preserved through a read/write round-trip, so you never lose tooling config you do not model
- A validator with publish-readiness rules and a `ValidationRule` interface for writing your own
- A `package-json-effect/schema` entry point: extend the model with `.extend()` and rebuild the wire schema with `makePackageJsonSchema` to type custom fields
- `catalog:` and `workspace:` resolution through swappable `CatalogResolver` and `WorkspaceResolver` services
## Installation
## Install
`effect` and `@effect/platform` are peer dependencies. Install them alongside a platform adapter for your runtime — `@effect/platform-node` for Node.js.
```bash
npm install package-json-effect
npm install package-json-effect effect @effect/platform @effect/platform-node
# or
pnpm add package-json-effect effect @effect/platform @effect/platform-node
```
Peer dependencies required:
## Quick start
```bash
npm install effect @effect/platform
```
Read a file, inspect it through the typed getters, make an immutable edit and write it back. The program yields the `PackageJsonReader` and `PackageJsonWriter` services and runs with the composite `PackageJsonLive` layer plus a FileSystem layer.
## Quick Start
```typescript
import { PackageJsonLive, PackageJsonReader, PackageJsonWriter, Package } from "package-json-effect";
import { NodeFileSystem } from "@effect/platform-node";
import { Effect } from "effect";
import { Package, PackageJsonLive, PackageJsonReader, PackageJsonWriter } from "package-json-effect";
const program = Effect.gen(function* () {
const reader = yield* PackageJsonReader;
const writer = yield* PackageJsonWriter;
const reader = yield* PackageJsonReader;
const writer = yield* PackageJsonWriter;
const pkg = yield* reader.read("./package.json");
console.log(pkg.name, pkg.version.toString(), pkg.isESM);
const pkg = yield* reader.read("./package.json");
console.log(pkg.name); // the "name" field, a string
console.log(pkg.version.toString()); // the "version" field as a SemVer, e.g. "1.3.0"
console.log(pkg.isESM); // true when "type": "module"
const updated = yield* pkg.pipe(Package.setVersion("1.2.0"));
yield* writer.write("./package.json", updated);
// Mutations return a new Package; the original is untouched.
const bumped = yield* Package.setVersion(pkg, "1.4.0");
const withDep = Package.addDependency(bumped, "effect", "^3.10.0");
yield* writer.write("./package.json", withDep);
});
Effect.runPromise(
program.pipe(
Effect.provide(PackageJsonLive),
Effect.provide(NodeFileSystem.layer),
),
);
Effect.runPromise(program.pipe(Effect.provide(PackageJsonLive), Effect.provide(NodeFileSystem.layer)));
```
## Package class
`PackageJsonLive` provides every service in the library and requires `FileSystem` from `@effect/platform`, which `NodeFileSystem.layer` (or `NodeContext.layer`) supplies.
Property getters:
## Documentation
```typescript
pkg.name // string
pkg.version // SemVer (from semver-effect)
pkg.isScoped // boolean — true if name starts with @
pkg.isESM // boolean — true if "type": "module"
pkg.isPrivate // boolean
pkg.hasDependency("effect") // boolean — checks all four dep maps
```
- [Getting started](./docs/01-getting-started.md) — install, the Effect mental model (Option, HashMap, Layer) and providing PackageJsonLive with a FileSystem layer
- [Reading and writing](./docs/02-reading-and-writing.md) — the reader and writer, formatter key ordering, the transformer and round-trip fidelity for unmodeled fields
- [The Package model](./docs/03-package-model.md) — computed getters, the Dependency instances and protocol taxonomy, and the dual-API for immutable mutations
- [Validation](./docs/04-validation.md) — the default rules, publish-readiness rules, writing a ValidationRule and building a validator layer
- [Extending the schema](./docs/05-extending-the-schema.md) — adding custom fields with .extend() and makePackageJsonSchema via the package-json-effect/schema entry point
- [Catalog and workspace resolution](./docs/06-catalog-workspace-resolution.md) — the no-op default, providing a real resolver, modifier semantics and resolution on read versus write
- [Testing](./docs/07-testing.md) — mock layers with Layer.succeed, asserting on typed errors and integration tests against the real filesystem
- [Errors and troubleshooting](./docs/08-errors-and-troubleshooting.md) — every error type, what triggers it and how to handle it
Mutation methods (data-first and pipeable):
```typescript
// Data-first
const v1 = yield* Package.setVersion(pkg, "2.0.0");
const v2 = Package.addDependency(pkg, "zod", "^3.0.0");
// Pipeable
const v3 = yield* pkg.pipe(Package.setVersion("2.0.0"));
const v4 = pkg.pipe(Package.addDependency("zod", "^3.0.0"));
```
Available mutation methods: `setVersion`, `setName`, `setLicense`, `addDependency`, `removeDependency`, `setScript`, `removeScript`.
## Schema extensibility
Add custom fields while keeping all standard package.json types:
```typescript
import { makePackageJsonSchema } from "package-json-effect";
import { Schema } from "effect";
const MySchema = makePackageJsonSchema({
myToolConfig: Schema.optionalWith(Schema.String, { as: "Option" }),
});
```
## Services
| Service | Description |
| ------- | ----------- |
| `PackageJsonReader` | Read and decode a package.json file into a `Package` |
| `PackageJsonWriter` | Encode and write a `Package` back to disk |
| `PackageJsonFormatter` | Sort keys and dependency entries before serialization |
| `PackageJsonTransformer` | Strip empty dependency maps before formatting |
| `PackageJsonValidator` | Run validation rules against a `Package` |
| `CatalogResolver` | Resolve `catalog:` protocol specifiers (no-op by default) |
| `WorkspaceResolver` | Resolve `workspace:` protocol specifiers (no-op by default) |
`PackageJsonLive` is a composite layer that provides all seven services. It requires `FileSystem` from `@effect/platform`.
Custom validation rules:
```typescript
import { makePackageJsonValidatorLive } from "package-json-effect";
const MyValidatorLive = makePackageJsonValidatorLive({
rules: [
{
name: "has-keywords",
validate: (pkg) =>
pkg._data.keywords ? Effect.void : Effect.fail({ message: "Missing keywords" }),
},
],
});
```
## License
[MIT](./LICENSE)
[MIT](LICENSE)

@@ -8,5 +8,5 @@ // This file is read by tools that parse documentation comments conforming to the TSDoc standard.

"packageName": "@microsoft/api-extractor",
"packageVersion": "7.58.2"
"packageVersion": "7.58.7"
}
]
}