package-json-effect
Advanced tools
+603
| 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 }; |
+909
| /** | ||
| * 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 { } |
+60
| 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 }; |
+8
-1
| { | ||
| "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. | ||
| [](https://www.npmjs.com/package/package-json-effect) | ||
| [](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" | ||
| } | ||
| ] | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
120852
74.35%9
50%2752
72.76%73
-41.6%1
Infinity%