@effected/package-json 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Dependency.js ADDED
@@ -0,0 +1,76 @@
1
+ import { DependencyKind, DependencySpecifier } from "@effected/npm";
2
+ import { Option, Schema } from "effect";
3
+
4
+ //#region src/Dependency.ts
5
+ /**
6
+ * A resolved dependency entry pairing a package name with its version
7
+ * specifier and the `kind` of map it came from (`@effected/npm`'s
8
+ * `DependencyKind`). The protocol predicates delegate to `DependencySpecifier`.
9
+ *
10
+ * @public
11
+ */
12
+ var Dependency = class extends Schema.Class("Dependency")({
13
+ /** The package name. */
14
+ name: Schema.String,
15
+ /** The raw version specifier. */
16
+ specifier: Schema.String,
17
+ /** Which dependency map this entry came from. */
18
+ kind: DependencyKind,
19
+ /** For `peer` dependencies, whether the peer is optional (from `peerDependenciesMeta`). */
20
+ isOptional: Schema.optionalKey(Schema.Boolean)
21
+ }) {
22
+ /** The classified protocol, or `None` for an empty specifier. */
23
+ get protocol() {
24
+ return this.specifier.length === 0 ? Option.none() : Option.some(DependencySpecifier.protocolOf(this.specifier));
25
+ }
26
+ /** Parse the specifier as a semver `Range`, `None` when it is not a range. */
27
+ get range() {
28
+ return DependencySpecifier.parseRange(this.specifier);
29
+ }
30
+ /** Whether the specifier points to a local path. */
31
+ get isLocal() {
32
+ return DependencySpecifier.isLocal(this.specifier);
33
+ }
34
+ /** Whether the specifier uses the `link:` protocol. */
35
+ get isLink() {
36
+ return DependencySpecifier.isLink(this.specifier);
37
+ }
38
+ /** Whether the specifier uses the `portal:` protocol. */
39
+ get isPortal() {
40
+ return DependencySpecifier.isPortal(this.specifier);
41
+ }
42
+ /** Whether the specifier uses the `catalog:` protocol. */
43
+ get isCatalog() {
44
+ return DependencySpecifier.isCatalog(this.specifier);
45
+ }
46
+ /** Whether the specifier uses the `workspace:` protocol. */
47
+ get isWorkspace() {
48
+ return DependencySpecifier.isWorkspace(this.specifier);
49
+ }
50
+ /** Whether the specifier is an unresolved `catalog:` or `workspace:` protocol. */
51
+ get isUnresolved() {
52
+ return this.isCatalog || this.isWorkspace;
53
+ }
54
+ /** Whether the specifier resolves to a git source. */
55
+ get isGit() {
56
+ return DependencySpecifier.isGit(this.specifier);
57
+ }
58
+ /** Whether the specifier is a parseable semver range. */
59
+ get isRange() {
60
+ return DependencySpecifier.isRange(this.specifier);
61
+ }
62
+ /** Whether the specifier is a dist-tag. */
63
+ get isTag() {
64
+ return DependencySpecifier.isTag(this.specifier);
65
+ }
66
+ };
67
+ /**
68
+ * Type guard narrowing any dependency-like value to
69
+ * {@link UnresolvedDependency}, preserving the concrete type.
70
+ *
71
+ * @public
72
+ */
73
+ const isUnresolvedDependency = (dependency) => dependency.isUnresolved === true;
74
+
75
+ //#endregion
76
+ export { Dependency, isUnresolvedDependency };
package/DevEngines.js ADDED
@@ -0,0 +1,42 @@
1
+ import { Schema } from "effect";
2
+
3
+ //#region src/DevEngines.ts
4
+ /**
5
+ * A single `devEngines` constraint with a name and optional `version` / `onFail`.
6
+ *
7
+ * @public
8
+ */
9
+ var DevEngine = class extends Schema.Class("DevEngine")({
10
+ /** The engine name (e.g. `node`, `pnpm`). */
11
+ name: Schema.String,
12
+ /** The optional version constraint. */
13
+ version: Schema.optionalKey(Schema.String),
14
+ /** The optional behavior when the constraint is unmet. */
15
+ onFail: Schema.optionalKey(Schema.Literals([
16
+ "warn",
17
+ "error",
18
+ "ignore"
19
+ ]))
20
+ }) {};
21
+ /**
22
+ * A `devEngines` constraint slot: a single {@link DevEngine} or an array of them.
23
+ *
24
+ * @public
25
+ */
26
+ const DevEngineOrArray = Schema.Union([DevEngine, Schema.Array(DevEngine)]);
27
+ /**
28
+ * The `devEngines` field schema, modeling runtime and package-manager
29
+ * constraints as optional {@link DevEngine} slots.
30
+ *
31
+ * @public
32
+ */
33
+ const DevEnginesSchema = Schema.Struct({
34
+ packageManager: Schema.optionalKey(DevEngineOrArray),
35
+ runtime: Schema.optionalKey(DevEngineOrArray),
36
+ os: Schema.optionalKey(DevEngineOrArray),
37
+ cpu: Schema.optionalKey(DevEngineOrArray),
38
+ libc: Schema.optionalKey(DevEngineOrArray)
39
+ });
40
+
41
+ //#endregion
42
+ export { DevEngine, DevEngineOrArray, DevEnginesSchema };
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 C. Spencer Beggs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/License.js ADDED
@@ -0,0 +1,45 @@
1
+ import { Schema } from "effect";
2
+ import spdxParse from "spdx-expression-parse";
3
+
4
+ //#region src/License.ts
5
+ /**
6
+ * Indicates that a string is not a valid SPDX license identifier or expression.
7
+ *
8
+ * Raised by {@link Package.setLicense} and the decode direction of
9
+ * `SpdxLicense`. The offending string is preserved on `input`.
10
+ *
11
+ * @public
12
+ */
13
+ var InvalidSpdxLicenseError = class extends Schema.TaggedErrorClass()("InvalidSpdxLicenseError", {
14
+ /** The raw input string that failed validation. */
15
+ input: Schema.String }) {
16
+ get message() {
17
+ return `Invalid SPDX license "${this.input}": not a recognized identifier or expression`;
18
+ }
19
+ };
20
+ /**
21
+ * Whether a string is a valid SPDX license identifier or expression, or one of
22
+ * the npm special cases `UNLICENSED` / `SEE LICENSE IN <file>`.
23
+ *
24
+ * @public
25
+ */
26
+ const isValidSpdx = (value) => {
27
+ if (value === "UNLICENSED") return true;
28
+ if (value.startsWith("SEE LICENSE IN ") && value.length > 15) return true;
29
+ try {
30
+ spdxParse(value);
31
+ return true;
32
+ } catch {
33
+ return false;
34
+ }
35
+ };
36
+ /**
37
+ * A valid SPDX license identifier, expression, `UNLICENSED`, or
38
+ * `SEE LICENSE IN <file>`.
39
+ *
40
+ * @public
41
+ */
42
+ const SpdxLicense = Schema.String.pipe(Schema.check(Schema.makeFilter((value) => isValidSpdx(value) ? void 0 : "Expected a valid SPDX license expression")), Schema.brand("SpdxLicense"));
43
+
44
+ //#endregion
45
+ export { InvalidSpdxLicenseError, SpdxLicense, isValidSpdx };
package/Package.js ADDED
@@ -0,0 +1,329 @@
1
+ import { Dependency } from "./Dependency.js";
2
+ import { DevEnginesSchema } from "./DevEngines.js";
3
+ import { InvalidSpdxLicenseError, SpdxLicense, isValidSpdx } from "./License.js";
4
+ import { renderJson } from "./internal/format.js";
5
+ import { PackageManager } from "./PackageManager.js";
6
+ import { InvalidPackageNameError, PackageName } from "./PackageName.js";
7
+ import { Person } from "./Person.js";
8
+ import { CatalogResolver, WorkspaceResolver } from "@effected/npm";
9
+ import { Effect, Function, HashMap, Option, Pipeable, Schema, SchemaTransformation } from "effect";
10
+ import { SemVer } from "@effected/semver";
11
+
12
+ //#region src/Package.ts
13
+ const toHashMap = SchemaTransformation.transform({
14
+ decode: (record) => HashMap.fromIterable(Object.entries(record)),
15
+ encode: (map) => Object.fromEntries(HashMap.toEntries(map))
16
+ });
17
+ /**
18
+ * A string→string map field decoding a plain JSON object to a `HashMap`,
19
+ * defaulting to an empty map when the key is absent. Backs the four dependency
20
+ * maps and `scripts`. Not meant to be referenced directly.
21
+ *
22
+ * @public
23
+ */
24
+ const DependencyMapField = Schema.Record(Schema.String, Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed({})), Schema.decodeTo(Schema.HashMap(Schema.String, Schema.String), toHashMap));
25
+ /**
26
+ * A string→string map field decoding a plain JSON object to a `HashMap`,
27
+ * with no default (an absent key stays absent). Backs `engines`. Not meant to
28
+ * be referenced directly.
29
+ *
30
+ * @public
31
+ */
32
+ const StringMapField = Schema.Record(Schema.String, Schema.String).pipe(Schema.decodeTo(Schema.HashMap(Schema.String, Schema.String), toHashMap));
33
+ /**
34
+ * The `bin` field: a single string path or a name→path map. Not meant to be
35
+ * referenced directly.
36
+ *
37
+ * @public
38
+ */
39
+ const BinField = Schema.Union([Schema.String, StringMapField]);
40
+ /**
41
+ * The `exports` field: a single string entry point or an open object of
42
+ * conditional exports. Not meant to be referenced directly.
43
+ *
44
+ * @public
45
+ */
46
+ const ExportsField = Schema.Union([Schema.String, Schema.Record(Schema.String, Schema.Unknown)]);
47
+ /**
48
+ * The `publishConfig` field: an open record preserving known npm keys
49
+ * (`access`, `directory`, ...) plus extensions like `targets`. Not meant to be
50
+ * referenced directly.
51
+ *
52
+ * @public
53
+ */
54
+ const PublishConfigField = Schema.Record(Schema.String, Schema.Unknown);
55
+ /**
56
+ * The `peerDependenciesMeta` field: a map of package name to `{ optional? }`.
57
+ * Not meant to be referenced directly.
58
+ *
59
+ * @public
60
+ */
61
+ const PeerDependenciesMetaField = Schema.Record(Schema.String, Schema.Struct({ optional: Schema.optionalKey(Schema.Boolean) }));
62
+ /**
63
+ * The `repository` field: a shorthand string or an object (with `type` / `url` /
64
+ * `directory` and any extensions preserved). Not meant to be referenced
65
+ * directly.
66
+ *
67
+ * @public
68
+ */
69
+ const RepositoryField = Schema.Union([Schema.String, Schema.Record(Schema.String, Schema.Unknown)]);
70
+ /**
71
+ * Indicates that a JSON value could not be decoded into a valid {@link Package}.
72
+ *
73
+ * Raised by {@link Package.decode}. The underlying `SchemaError` is preserved on
74
+ * the structured `cause` field (never stringified), so callers keep the issue
75
+ * tree for diagnostics.
76
+ *
77
+ * @public
78
+ */
79
+ var PackageDecodeError = class extends Schema.TaggedErrorClass()("PackageDecodeError", {
80
+ /** The underlying `SchemaError`, preserved structurally rather than stringified. */
81
+ cause: Schema.Defect() }) {
82
+ get message() {
83
+ return "Failed to decode package.json";
84
+ }
85
+ };
86
+ const resolveFormatOptions = (options) => ({
87
+ indent: options?.indent ?? 2,
88
+ sort: options?.sort ?? true,
89
+ stripEmpty: options?.stripEmpty ?? true,
90
+ newline: options?.newline ?? true
91
+ });
92
+ const RawJson = Schema.Record(Schema.String, Schema.Unknown);
93
+ const makeWire = (Class) => {
94
+ const knownKeys = new Set(Object.keys(Class.fields).filter((k) => k !== "rest"));
95
+ return RawJson.pipe(Schema.decodeTo(Class, SchemaTransformation.transform({
96
+ decode: (raw) => {
97
+ const known = {};
98
+ const rest = {};
99
+ for (const [key, value] of Object.entries(raw)) if (knownKeys.has(key)) known[key] = value;
100
+ else rest[key] = value;
101
+ return {
102
+ ...known,
103
+ rest
104
+ };
105
+ },
106
+ encode: (encoded) => {
107
+ const { rest, ...known } = encoded;
108
+ return {
109
+ ...known,
110
+ ...rest ?? {}
111
+ };
112
+ }
113
+ })));
114
+ };
115
+ const applyWorkspaceModifier = (specifier, version) => {
116
+ const modifier = specifier.slice(10);
117
+ if (modifier === "*" || modifier === "") return version;
118
+ if (modifier === "^") return `^${version}`;
119
+ if (modifier === "~") return `~${version}`;
120
+ return modifier;
121
+ };
122
+ const catalogName = (specifier) => {
123
+ const name = specifier.slice(8);
124
+ return name.length === 0 ? Option.none() : Option.some(name);
125
+ };
126
+ /**
127
+ * A package.json document as a rich `Schema.Class`: typed known fields, a
128
+ * `rest` catch-all preserving unknown top-level fields across a read/edit/write
129
+ * cycle, computed getters, and immutable mutation statics.
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * import { Package } from "@effected/package-json";
134
+ * import { Effect } from "effect";
135
+ *
136
+ * const program = Effect.gen(function* () {
137
+ * const pkg = yield* Package.decode({ name: "my-pkg", version: "1.0.0" });
138
+ * const next = yield* Package.setVersion(pkg, "1.1.0");
139
+ * console.log(next.toJsonString());
140
+ * });
141
+ * ```
142
+ *
143
+ * @public
144
+ */
145
+ var Package = class Package extends Schema.Class("Package")({
146
+ name: PackageName,
147
+ version: SemVer.FromString,
148
+ description: Schema.optionalKey(Schema.String),
149
+ private: Schema.optionalKey(Schema.Boolean),
150
+ type: Schema.optionalKey(Schema.Literals(["module", "commonjs"])),
151
+ main: Schema.optionalKey(Schema.String),
152
+ license: Schema.optionalKey(SpdxLicense),
153
+ author: Schema.optionalKey(Person.FromValue),
154
+ contributors: Schema.optionalKey(Schema.Array(Person.FromValue)),
155
+ repository: Schema.optionalKey(RepositoryField),
156
+ dependencies: DependencyMapField,
157
+ devDependencies: DependencyMapField,
158
+ peerDependencies: DependencyMapField,
159
+ optionalDependencies: DependencyMapField,
160
+ peerDependenciesMeta: Schema.optionalKey(PeerDependenciesMetaField),
161
+ scripts: DependencyMapField,
162
+ bin: Schema.optionalKey(BinField),
163
+ engines: Schema.optionalKey(StringMapField),
164
+ exports: Schema.optionalKey(ExportsField),
165
+ publishConfig: Schema.optionalKey(PublishConfigField),
166
+ packageManager: Schema.optionalKey(PackageManager.FromString),
167
+ devEngines: Schema.optionalKey(DevEnginesSchema),
168
+ rest: Schema.optionalKey(Schema.Record(Schema.String, Schema.Unknown))
169
+ }) {
170
+ pipe() {
171
+ return Pipeable.pipeArguments(this, arguments);
172
+ }
173
+ /**
174
+ * The default wire codec: an open JSON object ↔ a {@link Package} instance,
175
+ * partitioning unknown keys into `rest` and flattening them back on encode.
176
+ */
177
+ static schema = makeWire(Package);
178
+ /**
179
+ * Build the wire codec for a `.extend()`ed subclass, so its custom fields
180
+ * decode as typed members and are excluded from `rest`.
181
+ *
182
+ * @param Class - the extended `Schema.Class`, carrying its own `fields`
183
+ * @returns a codec between an open JSON object and `Class` instances
184
+ */
185
+ static wireFor(Class) {
186
+ return makeWire(Class);
187
+ }
188
+ /**
189
+ * Decode an unknown JSON value into a {@link Package}, normalizing any
190
+ * `SchemaError` to a typed {@link PackageDecodeError} at the boundary.
191
+ *
192
+ * @param input - the parsed package.json JSON value (e.g. from `JSON.parse`)
193
+ * @returns an Effect resolving to the decoded `Package`
194
+ * @throws (typed) `PackageDecodeError` when `input` does not satisfy the schema
195
+ */
196
+ static decode = Effect.fn("Package.decode")(function* (input) {
197
+ return yield* Schema.decodeUnknownEffect(Package.schema)(input).pipe(Effect.catchTag("SchemaError", (cause) => new PackageDecodeError({ cause })));
198
+ });
199
+ /** Whether the package is marked private. */
200
+ get isPrivate() {
201
+ return this.private ?? false;
202
+ }
203
+ /** Whether the package name is scoped (`@scope/name`). */
204
+ get isScoped() {
205
+ return PackageName.isScoped(this.name);
206
+ }
207
+ /** Whether the package is ESM (`"type": "module"`). */
208
+ get isESM() {
209
+ return this.type === "module";
210
+ }
211
+ /** Whether any dependency map contains `name`. */
212
+ hasDependency(name) {
213
+ return HashMap.has(this.dependencies, name) || HashMap.has(this.devDependencies, name) || HashMap.has(this.peerDependencies, name) || HashMap.has(this.optionalDependencies, name);
214
+ }
215
+ /** The `dependencies` map as {@link Dependency} instances (`kind: "prod"`). */
216
+ getDependencies() {
217
+ return HashMap.map(this.dependencies, (specifier, name) => Dependency.make({
218
+ name,
219
+ specifier,
220
+ kind: "prod"
221
+ }));
222
+ }
223
+ /** The `devDependencies` map as {@link Dependency} instances (`kind: "dev"`). */
224
+ getDevDependencies() {
225
+ return HashMap.map(this.devDependencies, (specifier, name) => Dependency.make({
226
+ name,
227
+ specifier,
228
+ kind: "dev"
229
+ }));
230
+ }
231
+ /** The `peerDependencies` map as {@link Dependency} instances (`kind: "peer"`), carrying `isOptional` from `peerDependenciesMeta`. */
232
+ getPeerDependencies() {
233
+ const meta = this.peerDependenciesMeta;
234
+ return HashMap.map(this.peerDependencies, (specifier, name) => Dependency.make({
235
+ name,
236
+ specifier,
237
+ kind: "peer",
238
+ isOptional: meta?.[name]?.optional ?? false
239
+ }));
240
+ }
241
+ /** The `optionalDependencies` map as {@link Dependency} instances (`kind: "optional"`). */
242
+ getOptionalDependencies() {
243
+ return HashMap.map(this.optionalDependencies, (specifier, name) => Dependency.make({
244
+ name,
245
+ specifier,
246
+ kind: "optional"
247
+ }));
248
+ }
249
+ /** Return a new {@link Package} with the given fields replaced. */
250
+ copyWith(patch) {
251
+ return Package.make({
252
+ ...this,
253
+ ...patch
254
+ });
255
+ }
256
+ /** Set the version from a string. Fails with `InvalidVersionError`. Dual API. */
257
+ static setVersion = Function.dual(2, Effect.fn("Package.setVersion")(function* (pkg, version) {
258
+ const semver = yield* SemVer.parse(version);
259
+ return pkg.copyWith({ version: semver });
260
+ }));
261
+ /** Set the package name. Fails with `InvalidPackageNameError`. Dual API. */
262
+ static setName = Function.dual(2, Effect.fn("Package.setName")(function* (pkg, name) {
263
+ if (!PackageName.isValid(name)) return yield* new InvalidPackageNameError({ input: name });
264
+ return pkg.copyWith({ name });
265
+ }));
266
+ /** Set the license from an SPDX string. Fails with `InvalidSpdxLicenseError`. Dual API. */
267
+ static setLicense = Function.dual(2, Effect.fn("Package.setLicense")(function* (pkg, license) {
268
+ if (!isValidSpdx(license)) return yield* new InvalidSpdxLicenseError({ input: license });
269
+ return pkg.copyWith({ license });
270
+ }));
271
+ /** Add or replace a `dependencies` entry. Dual API. */
272
+ static addDependency = Function.dual(3, (pkg, name, specifier) => pkg.copyWith({ dependencies: HashMap.set(pkg.dependencies, name, specifier) }));
273
+ /** Remove a `dependencies` entry. Dual API. */
274
+ static removeDependency = Function.dual(2, (pkg, name) => pkg.copyWith({ dependencies: HashMap.remove(pkg.dependencies, name) }));
275
+ /** Add or replace a `devDependencies` entry. Dual API. */
276
+ static addDevDependency = Function.dual(3, (pkg, name, specifier) => pkg.copyWith({ devDependencies: HashMap.set(pkg.devDependencies, name, specifier) }));
277
+ /** Remove a `devDependencies` entry. Dual API. */
278
+ static removeDevDependency = Function.dual(2, (pkg, name) => pkg.copyWith({ devDependencies: HashMap.remove(pkg.devDependencies, name) }));
279
+ /** Add or replace a `peerDependencies` entry. Dual API. */
280
+ static addPeerDependency = Function.dual(3, (pkg, name, specifier) => pkg.copyWith({ peerDependencies: HashMap.set(pkg.peerDependencies, name, specifier) }));
281
+ /** Remove a `peerDependencies` entry. Dual API. */
282
+ static removePeerDependency = Function.dual(2, (pkg, name) => pkg.copyWith({ peerDependencies: HashMap.remove(pkg.peerDependencies, name) }));
283
+ /** Add or replace an `optionalDependencies` entry. Dual API. */
284
+ static addOptionalDependency = Function.dual(3, (pkg, name, specifier) => pkg.copyWith({ optionalDependencies: HashMap.set(pkg.optionalDependencies, name, specifier) }));
285
+ /** Remove an `optionalDependencies` entry. Dual API. */
286
+ static removeOptionalDependency = Function.dual(2, (pkg, name) => pkg.copyWith({ optionalDependencies: HashMap.remove(pkg.optionalDependencies, name) }));
287
+ /** Add or replace a `scripts` entry. Dual API. */
288
+ static setScript = Function.dual(3, (pkg, name, command) => pkg.copyWith({ scripts: HashMap.set(pkg.scripts, name, command) }));
289
+ /** Remove a `scripts` entry. Dual API. */
290
+ static removeScript = Function.dual(2, (pkg, name) => pkg.copyWith({ scripts: HashMap.remove(pkg.scripts, name) }));
291
+ /**
292
+ * Resolve `catalog:` and `workspace:` specifiers across all four dependency
293
+ * maps using the `CatalogResolver` and `WorkspaceResolver` from context.
294
+ * Specifiers the resolvers return `None` for are left unchanged. This is the
295
+ * explicit resolution step — `PackageJsonFile.write` never resolves.
296
+ */
297
+ static resolve = Effect.fn("Package.resolve")(function* (pkg) {
298
+ const workspace = yield* WorkspaceResolver;
299
+ const catalog = yield* CatalogResolver;
300
+ const resolveMap = Effect.fn("Package.resolve.map")(function* (map) {
301
+ let next = map;
302
+ for (const [name, specifier] of HashMap.entries(map)) if (specifier.startsWith("workspace:")) {
303
+ const version = yield* workspace.versionOf(name);
304
+ if (Option.isSome(version)) next = HashMap.set(next, name, applyWorkspaceModifier(specifier, version.value));
305
+ } else if (specifier.startsWith("catalog:")) {
306
+ const range = yield* catalog.rangeOf(name, catalogName(specifier));
307
+ if (Option.isSome(range)) next = HashMap.set(next, name, range.value);
308
+ }
309
+ return next;
310
+ });
311
+ return pkg.copyWith({
312
+ dependencies: yield* resolveMap(pkg.dependencies),
313
+ devDependencies: yield* resolveMap(pkg.devDependencies),
314
+ peerDependencies: yield* resolveMap(pkg.peerDependencies),
315
+ optionalDependencies: yield* resolveMap(pkg.optionalDependencies)
316
+ });
317
+ });
318
+ /**
319
+ * Serialize to a formatted package.json string: encode through the wire
320
+ * codec (flattening `rest`), then apply the canonical key order, dependency
321
+ * sorting and empty-map stripping unless the options opt out. Pure.
322
+ */
323
+ toJsonString(options) {
324
+ return renderJson(Schema.encodeUnknownSync(Package.schema)(this), resolveFormatOptions(options));
325
+ }
326
+ };
327
+
328
+ //#endregion
329
+ export { BinField, DependencyMapField, ExportsField, Package, PackageDecodeError, PeerDependenciesMetaField, PublishConfigField, RepositoryField, StringMapField };
@@ -0,0 +1,128 @@
1
+ import { Package } from "./Package.js";
2
+ import { Context, Effect, FileSystem, Layer, Path, Schema } from "effect";
3
+
4
+ //#region src/PackageJsonFile.ts
5
+ /**
6
+ * Indicates that a package.json file could not be read from the filesystem
7
+ * (a filesystem error other than not-found).
8
+ *
9
+ * @public
10
+ */
11
+ var PackageJsonReadError = class extends Schema.TaggedErrorClass()("PackageJsonReadError", {
12
+ /** The path that could not be read. */
13
+ path: Schema.String,
14
+ /** The underlying failure, preserved structurally. */
15
+ cause: Schema.Defect()
16
+ }) {
17
+ get message() {
18
+ return `Failed to read package.json from "${this.path}"`;
19
+ }
20
+ };
21
+ /**
22
+ * Indicates that no package.json file exists at the expected path. Carries its
23
+ * own tag for `catchTag` routing.
24
+ *
25
+ * @public
26
+ */
27
+ var PackageJsonNotFoundError = class extends Schema.TaggedErrorClass()("PackageJsonNotFoundError", {
28
+ /** The path where package.json was expected. */
29
+ path: Schema.String }) {
30
+ get message() {
31
+ return `package.json not found at "${this.path}"`;
32
+ }
33
+ };
34
+ /**
35
+ * Indicates that a package.json file's contents are not valid JSON.
36
+ *
37
+ * @public
38
+ */
39
+ var PackageJsonParseError = class extends Schema.TaggedErrorClass()("PackageJsonParseError", {
40
+ /** The path whose contents failed to parse as JSON. */
41
+ path: Schema.String,
42
+ /** The underlying `SyntaxError`, preserved structurally. */
43
+ cause: Schema.Defect()
44
+ }) {
45
+ get message() {
46
+ return `Failed to parse package.json at "${this.path}"`;
47
+ }
48
+ };
49
+ /**
50
+ * Indicates that a package.json file could not be written to the filesystem.
51
+ * Narrowed to the filesystem-write failure only — never a resolution or encode
52
+ * error.
53
+ *
54
+ * @public
55
+ */
56
+ var PackageJsonWriteError = class extends Schema.TaggedErrorClass()("PackageJsonWriteError", {
57
+ /** The path that could not be written. */
58
+ path: Schema.String,
59
+ /** The underlying filesystem failure, preserved structurally. Narrowed to the write failure only. */
60
+ cause: Schema.Defect()
61
+ }) {
62
+ get message() {
63
+ return `Failed to write package.json to "${this.path}"`;
64
+ }
65
+ };
66
+ /**
67
+ * Reads and writes package.json over core `FileSystem` / `Path`. The layer
68
+ * requires those services; provide `@effect/platform-node`'s `NodeFileSystem` /
69
+ * `NodePath` (or a bun equivalent) at the application boundary.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * import { PackageJsonFile } from "@effected/package-json";
74
+ * import { NodeFileSystem, NodePath } from "@effect/platform-node";
75
+ * import { Effect } from "effect";
76
+ *
77
+ * const program = Effect.gen(function* () {
78
+ * const files = yield* PackageJsonFile;
79
+ * const pkg = yield* files.read("./package.json");
80
+ * console.log(pkg.name);
81
+ * }).pipe(Effect.provide(PackageJsonFile.layer), Effect.provide(NodeFileSystem.layer), Effect.provide(NodePath.layer));
82
+ * ```
83
+ *
84
+ * @public
85
+ */
86
+ var PackageJsonFile = class PackageJsonFile extends Context.Service()("@effected/package-json/PackageJsonFile") {
87
+ /** Build the service implementation from `FileSystem` / `Path` in context; use {@link PackageJsonFile.layer} to provide it. */
88
+ static make = Effect.gen(function* () {
89
+ const fs = yield* FileSystem.FileSystem;
90
+ const path = yield* Path.Path;
91
+ return {
92
+ read: Effect.fn("PackageJsonFile.read")(function* (target) {
93
+ const content = yield* fs.readFileString(target).pipe(Effect.mapError((cause) => cause.reason._tag === "NotFound" ? new PackageJsonNotFoundError({ path: target }) : new PackageJsonReadError({
94
+ path: target,
95
+ cause
96
+ })));
97
+ const json = yield* Effect.try({
98
+ try: () => JSON.parse(content),
99
+ catch: (cause) => new PackageJsonParseError({
100
+ path: target,
101
+ cause
102
+ })
103
+ });
104
+ return yield* Package.decode(json);
105
+ }),
106
+ write: Effect.fn("PackageJsonFile.write")(function* (target, pkg, options) {
107
+ const json = pkg.toJsonString(options);
108
+ const directory = path.dirname(target);
109
+ yield* fs.makeDirectory(directory, { recursive: true }).pipe(Effect.mapError((cause) => new PackageJsonWriteError({
110
+ path: target,
111
+ cause
112
+ })));
113
+ yield* fs.writeFileString(target, json).pipe(Effect.mapError((cause) => new PackageJsonWriteError({
114
+ path: target,
115
+ cause
116
+ })));
117
+ })
118
+ };
119
+ });
120
+ /**
121
+ * The live layer. Requires core `FileSystem` / `Path`, provided by the
122
+ * consumer's platform implementation at the edge.
123
+ */
124
+ static layer = Layer.effect(PackageJsonFile, PackageJsonFile.make);
125
+ };
126
+
127
+ //#endregion
128
+ export { PackageJsonFile, PackageJsonNotFoundError, PackageJsonParseError, PackageJsonReadError, PackageJsonWriteError };