@effected/package-json 0.16.0 → 0.18.0-next-20260927062352

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.
@@ -3,11 +3,90 @@ import { Effect, Exit, Option, Result, Schema, SchemaIssue, SchemaTransformation
3
3
  import { Range, SemVer } from "@effected/semver";
4
4
 
5
5
  //#region src/PackageManagerRange.ts
6
+ const SINGLE_COMPARATOR_RE = /^([\^~]?)(.+)$/;
7
+ const singleComparator = (range) => {
8
+ const match = SINGLE_COMPARATOR_RE.exec(range);
9
+ if (match === null) return Option.none();
10
+ const operator = match[1];
11
+ const version = match[2];
12
+ return SemVer.isPinnable(version) ? Option.some({
13
+ operator,
14
+ version
15
+ }) : Option.none();
16
+ };
6
17
  const PACKAGE_MANAGER_NAME_RE = /^[a-z]+$/;
7
- const invalid = (input, message) => Effect.fail(new SchemaIssue.InvalidValue({ message }, input));
8
18
  /** `Schema.String` refined to parse as a semver range (`Range.parseResult` succeeds). The check is erased from the built type. */
9
19
  const SemVerRangeString = Schema.String.pipe(Schema.check(Schema.makeFilter((value) => value.length > 0 && Result.isSuccess(Range.parseResult(value)) ? void 0 : "Expected a semver range (an exact version, a caret/tilde range, a comparator set, ...)")));
10
20
  /**
21
+ * Indicates that a `packageManager` value, or a `devEngines.packageManager`
22
+ * entry, could not be read as a {@link PackageManagerRange}.
23
+ *
24
+ * Raised by {@link PackageManagerRange.parse},
25
+ * {@link PackageManagerRange.parseResult},
26
+ * {@link PackageManagerRange.fromDevEngine} and
27
+ * {@link PackageManagerRange.fromDevEngineResult}; the decode direction of
28
+ * {@link PackageManagerRange.FromString} reports the same failure through a
29
+ * generic `Schema` parse error carrying the same message.
30
+ *
31
+ * @public
32
+ */
33
+ var InvalidPackageManagerRangeError = class extends Schema.TaggedError()("InvalidPackageManagerRangeError", {
34
+ /**
35
+ * The offending value: the raw `packageManager` string, or for a
36
+ * `devEngines` entry its `name` and `version` joined as
37
+ * `<name>@<version>` (just `<name>` when `version` is absent).
38
+ */
39
+ input: Schema.String,
40
+ /**
41
+ * Which component failed: `format` (a `packageManager` string with no
42
+ * `@`), `name` (not a lowercase name), `range` (absent, empty, or not a
43
+ * semver range) or `integrity` (the tail after the first `+` is not a
44
+ * corepack `<algo>.<hex>` hash).
45
+ */
46
+ reason: Schema.Literals([
47
+ "format",
48
+ "name",
49
+ "range",
50
+ "integrity"
51
+ ])
52
+ }) {
53
+ get message() {
54
+ return this.reason === "format" ? `Invalid packageManager format: "${this.input}": expected <name>@<range>[+<integrity>]` : this.reason === "name" ? `Invalid packageManager name: "${this.input}": name must be a lowercase package-manager name` : this.reason === "range" ? `Invalid packageManager range: "${this.input}": the range is missing or is not a semver range` : `Invalid packageManager integrity: "${this.input}": integrity must be a corepack <algo>.<hex> hash`;
55
+ }
56
+ };
57
+ /**
58
+ * The shared component validation behind both entry points: `name`, and the
59
+ * `<range>[+<integrity>]` tail split on its FIRST `+`. `input` is only the
60
+ * text the error reports.
61
+ */
62
+ const fromParts = (input, name, tail) => {
63
+ if (!PACKAGE_MANAGER_NAME_RE.test(name)) return Result.fail(new InvalidPackageManagerRangeError({
64
+ input,
65
+ reason: "name"
66
+ }));
67
+ const plus = tail.indexOf("+");
68
+ const range = plus === -1 ? tail : tail.slice(0, plus);
69
+ if (range.length === 0 || Result.isFailure(Range.parseResult(range))) return Result.fail(new InvalidPackageManagerRangeError({
70
+ input,
71
+ reason: "range"
72
+ }));
73
+ if (plus === -1) return Result.succeed(PackageManagerRange.make({
74
+ name,
75
+ range,
76
+ integrity: Option.none()
77
+ }));
78
+ const decoded = Schema.decodeUnknownExit(CorepackIntegrityHash)(tail.slice(plus + 1));
79
+ if (Exit.isFailure(decoded)) return Result.fail(new InvalidPackageManagerRangeError({
80
+ input,
81
+ reason: "integrity"
82
+ }));
83
+ return Result.succeed(PackageManagerRange.make({
84
+ name,
85
+ range,
86
+ integrity: Option.some(decoded.value)
87
+ }));
88
+ };
89
+ /**
11
90
  * A structured `packageManager` value whose version position is a semver
12
91
  * **range**, carried verbatim: `name`, `range` and an optional `integrity`
13
92
  * hash.
@@ -67,44 +146,111 @@ var PackageManagerRange = class PackageManagerRange extends Schema.Class("Packag
67
146
  * {@link PackageManagerRange}.
68
147
  *
69
148
  * @remarks
70
- * Decoding splits on the first `@`, then on the first `+` — which always
71
- * begins the integrity, never semver build metadata — and validates each
72
- * component: the name against the lowercase grammar, the range through
73
- * `@effected/semver`'s `Range.parseResult`, the integrity through
74
- * `CorepackIntegrityHash`. Every failure is a typed decode failure naming
75
- * the component that failed. Encoding reconstructs the string from the
76
- * verbatim parts, so it is byte-identical to any input this codec accepts.
149
+ * Decoding parses via {@link PackageManagerRange.parseResult}, so every
150
+ * failure is a typed decode failure naming the component that failed.
151
+ * Encoding prints `toString()`, reconstructed from the verbatim parts, so
152
+ * it is byte-identical to any input this codec accepts.
77
153
  */
78
154
  static FromString = Schema.String.pipe(Schema.decodeTo(Schema.instanceOf(PackageManagerRange), SchemaTransformation.transformEffect({
79
155
  decode: (input) => {
80
- const at = input.indexOf("@");
81
- if (at === -1) return invalid(input, `Invalid packageManager format: "${input}"`);
82
- const name = input.slice(0, at);
83
- if (!PACKAGE_MANAGER_NAME_RE.test(name)) return invalid(input, `Invalid packageManager name: "${name}"`);
84
- const rest = input.slice(at + 1);
85
- const plus = rest.indexOf("+");
86
- const range = plus === -1 ? rest : rest.slice(0, plus);
87
- if (range.length === 0 || Result.isFailure(Range.parseResult(range))) return invalid(input, `Invalid packageManager range: "${range}"`);
88
- if (plus === -1) return Effect.succeed(PackageManagerRange.make({
89
- name,
90
- range,
91
- integrity: Option.none()
92
- }));
93
- const rawIntegrity = rest.slice(plus + 1);
94
- const decoded = Schema.decodeUnknownExit(CorepackIntegrityHash)(rawIntegrity);
95
- if (Exit.isFailure(decoded)) return invalid(input, `Invalid packageManager integrity: "${rawIntegrity}"`);
96
- return Effect.succeed(PackageManagerRange.make({
97
- name,
98
- range,
99
- integrity: Option.some(decoded.value)
100
- }));
156
+ const parsed = PackageManagerRange.parseResult(input);
157
+ return Result.isSuccess(parsed) ? Effect.succeed(parsed.success) : Effect.fail(new SchemaIssue.InvalidValue({ message: parsed.failure.message }, input));
101
158
  },
102
- encode: (pm) => Effect.succeed(Option.match(pm.integrity, {
103
- onNone: () => `${pm.name}@${pm.range}`,
104
- onSome: (integrity) => `${pm.name}@${pm.range}+${integrity}`
105
- }))
159
+ encode: (pm) => Effect.succeed(pm.toString())
106
160
  })));
107
161
  /**
162
+ * Parse a `packageManager` string (`name@range[+integrity]`), synchronously,
163
+ * returning a `Result` instead of an `Effect`.
164
+ *
165
+ * @remarks
166
+ * Splits on the first `@`, then on the first `+` — which always begins the
167
+ * integrity, never semver build metadata — and validates each component:
168
+ * the name against the lowercase grammar, the range through
169
+ * `@effected/semver`'s `Range.parseResult`, the integrity through
170
+ * `CorepackIntegrityHash`. {@link PackageManagerRange.parse} is defined in
171
+ * terms of this function.
172
+ *
173
+ * @param input - the `packageManager` value to parse
174
+ * @returns a `Result` succeeding with the parsed {@link PackageManagerRange},
175
+ * or failing with {@link InvalidPackageManagerRangeError} naming the
176
+ * component that failed.
177
+ */
178
+ static parseResult(input) {
179
+ const at = input.indexOf("@");
180
+ return at === -1 ? Result.fail(new InvalidPackageManagerRangeError({
181
+ input,
182
+ reason: "format"
183
+ })) : fromParts(input, input.slice(0, at), input.slice(at + 1));
184
+ }
185
+ /**
186
+ * Parse a `packageManager` string. Defined in terms of
187
+ * {@link PackageManagerRange.parseResult}.
188
+ *
189
+ * @param input - the `packageManager` value to parse
190
+ * @returns the parsed {@link PackageManagerRange}. Fails with
191
+ * {@link InvalidPackageManagerRangeError} when `input` is malformed.
192
+ */
193
+ static parse = Effect.fn("PackageManagerRange.parse")((input) => Effect.fromResult(PackageManagerRange.parseResult(input)));
194
+ /**
195
+ * Read a `devEngines.packageManager` entry onto this model, synchronously,
196
+ * returning a `Result`.
197
+ *
198
+ * @remarks
199
+ * The entry's `name` is the package-manager name and its `version` is the
200
+ * same `<range>[+<integrity>]` tail the `packageManager` field carries after
201
+ * its `@` — `^12.6.0`, `12.6.0`, or `12.6.0+sha512.<hex>` as
202
+ * `pnpm self-update` historically wrote it. Both are validated exactly as
203
+ * {@link PackageManagerRange.parseResult} validates them. The `version`
204
+ * slot is optional on a {@link DevEngine}, but an entry without one names
205
+ * no range, so it fails with `reason: "range"`; `onFail` is ignored. The
206
+ * parameter is the encoded {@link DevEnginePackageManagerEntry} shape, so a
207
+ * plain object read off disk needs no `DevEngine` construction first.
208
+ *
209
+ * To write the entry back without its integrity (the bare form pnpm 11+
210
+ * writes), use `range` — the verbatim range with its operator kept and the
211
+ * integrity dropped.
212
+ *
213
+ * @param engine - the `devEngines.packageManager` entry to read
214
+ * @returns a `Result` succeeding with the {@link PackageManagerRange}, or
215
+ * failing with {@link InvalidPackageManagerRangeError}.
216
+ */
217
+ static fromDevEngineResult(engine) {
218
+ if (engine.version === void 0) return Result.fail(new InvalidPackageManagerRangeError({
219
+ input: engine.name,
220
+ reason: "range"
221
+ }));
222
+ return fromParts(`${engine.name}@${engine.version}`, engine.name, engine.version);
223
+ }
224
+ /**
225
+ * Read a `devEngines.packageManager` entry onto this model. Defined in
226
+ * terms of {@link PackageManagerRange.fromDevEngineResult}.
227
+ *
228
+ * @param engine - the `devEngines.packageManager` entry to read
229
+ * @returns the {@link PackageManagerRange}. Fails with
230
+ * {@link InvalidPackageManagerRangeError} when the name, range or integrity
231
+ * is malformed, or the entry has no `version`.
232
+ */
233
+ static fromDevEngine = Effect.fn("PackageManagerRange.fromDevEngine")((engine) => Effect.fromResult(PackageManagerRange.fromDevEngineResult(engine)));
234
+ /**
235
+ * The `packageManager` value without its integrity: `<name>@<range>`, range
236
+ * operator kept (`pnpm@^12.6.0+sha512.<hex>` → `pnpm@^12.6.0`). The
237
+ * `devEngines` counterpart is `range` itself.
238
+ */
239
+ get bare() {
240
+ return `${this.name}@${this.range}`;
241
+ }
242
+ /**
243
+ * The value as parsed: `<name>@<range>` or `<name>@<range>+<integrity>`.
244
+ * The encode direction of {@link PackageManagerRange.FromString} prints
245
+ * exactly this.
246
+ */
247
+ toString() {
248
+ return Option.match(this.integrity, {
249
+ onNone: () => this.bare,
250
+ onSome: (integrity) => `${this.bare}+${integrity}`
251
+ });
252
+ }
253
+ /**
108
254
  * Whether the range is an exact, pinnable version (`11.2.0`) rather than a
109
255
  * genuine range (`^11.20.0`) — decided by `SemVer.isPinnable` over the
110
256
  * verbatim text, so `=11.2.0` and other range spellings of a single
@@ -114,6 +260,64 @@ var PackageManagerRange = class PackageManagerRange extends Schema.Class("Packag
114
260
  get isExact() {
115
261
  return SemVer.isPinnable(this.range);
116
262
  }
263
+ /**
264
+ * The range's operator when it is a single exact, caret or tilde comparator
265
+ * over a pinnable version: `""` for `12.6.0`, `"^"` for `^12.6.0`, `"~"` for
266
+ * `~12.6.0`. `Option.none()` for anything else (`>=12 <13`, `12.x`,
267
+ * `=12.6.0`, `^12`), whose operator cannot be carried onto a new version
268
+ * unambiguously.
269
+ */
270
+ get operator() {
271
+ return Option.map(singleComparator(this.range), (parts) => parts.operator);
272
+ }
273
+ /**
274
+ * The version a single exact, caret or tilde comparator is anchored on
275
+ * (`^12.6.0` → `12.6.0`); `Option.none()` exactly when
276
+ * {@link PackageManagerRange.operator} is.
277
+ */
278
+ get baseVersion() {
279
+ return Option.map(singleComparator(this.range), (parts) => parts.version);
280
+ }
281
+ /**
282
+ * The same range re-anchored on `version`, operator kept and integrity
283
+ * dropped (`^12.6.0+sha512.<hex>` with `12.8.1` → `^12.8.1`), synchronously,
284
+ * returning a `Result`.
285
+ *
286
+ * @remarks
287
+ * Only a single exact, caret or tilde comparator can be re-anchored (see
288
+ * {@link PackageManagerRange.operator}); any other range, or a `version`
289
+ * that is not a pinnable semver version, fails with `reason: "range"`. The
290
+ * integrity is dropped because it named the old version's artifact.
291
+ *
292
+ * @param version - the pinnable version to anchor the range on
293
+ * @returns a `Result` succeeding with the re-anchored
294
+ * {@link PackageManagerRange}, or failing with
295
+ * {@link InvalidPackageManagerRangeError}.
296
+ */
297
+ withVersionResult(version) {
298
+ const parts = singleComparator(this.range);
299
+ if (Option.isNone(parts) || !SemVer.isPinnable(version)) return Result.fail(new InvalidPackageManagerRangeError({
300
+ input: `${this.name}@${this.range} -> ${version}`,
301
+ reason: "range"
302
+ }));
303
+ return Result.succeed(PackageManagerRange.make({
304
+ name: this.name,
305
+ range: `${parts.value.operator}${version}`,
306
+ integrity: Option.none()
307
+ }));
308
+ }
309
+ /**
310
+ * The same range re-anchored on `version`. Defined in terms of
311
+ * {@link PackageManagerRange.withVersionResult}.
312
+ *
313
+ * @param version - the pinnable version to anchor the range on
314
+ * @returns the re-anchored {@link PackageManagerRange}. Fails with
315
+ * {@link InvalidPackageManagerRangeError} when the range is not a single
316
+ * exact, caret or tilde comparator, or `version` is not pinnable.
317
+ */
318
+ withVersion(version) {
319
+ return Effect.fromResult(this.withVersionResult(version));
320
+ }
117
321
  /** Whether an integrity hash is present. */
118
322
  get hasIntegrity() {
119
323
  return Option.isSome(this.integrity);
@@ -121,4 +325,4 @@ var PackageManagerRange = class PackageManagerRange extends Schema.Class("Packag
121
325
  };
122
326
 
123
327
  //#endregion
124
- export { PackageManagerRange };
328
+ export { InvalidPackageManagerRangeError, PackageManagerRange };
package/index.d.ts CHANGED
@@ -1395,6 +1395,49 @@ export declare class LenientManifest extends LenientManifest_base {
1395
1395
  }
1396
1396
  //#endregion
1397
1397
  //#region src/PackageManagerRange.d.ts
1398
+ /**
1399
+ * The encoded shape of a `devEngines.packageManager` entry: what a raw
1400
+ * `package.json` object carries. A {@link DevEngine} instance satisfies it
1401
+ * structurally, so both a decoded entry and the plain object read straight off
1402
+ * disk are accepted.
1403
+ *
1404
+ * @public
1405
+ */
1406
+ interface DevEnginePackageManagerEntry {
1407
+ readonly name: string;
1408
+ readonly version?: string | undefined;
1409
+ }
1410
+ declare const InvalidPackageManagerRangeError_base: Schema.Class<InvalidPackageManagerRangeError, Schema.TaggedStruct<"InvalidPackageManagerRangeError", {
1411
+ /**
1412
+ * The offending value: the raw `packageManager` string, or for a
1413
+ * `devEngines` entry its `name` and `version` joined as
1414
+ * `<name>@<version>` (just `<name>` when `version` is absent).
1415
+ */
1416
+ readonly input: Schema.String;
1417
+ /**
1418
+ * Which component failed: `format` (a `packageManager` string with no
1419
+ * `@`), `name` (not a lowercase name), `range` (absent, empty, or not a
1420
+ * semver range) or `integrity` (the tail after the first `+` is not a
1421
+ * corepack `<algo>.<hex>` hash).
1422
+ */
1423
+ readonly reason: Schema.Literals<readonly ["format", "name", "range", "integrity"]>;
1424
+ }>, import("effect/Cause").YieldableError>;
1425
+ /**
1426
+ * Indicates that a `packageManager` value, or a `devEngines.packageManager`
1427
+ * entry, could not be read as a {@link PackageManagerRange}.
1428
+ *
1429
+ * Raised by {@link PackageManagerRange.parse},
1430
+ * {@link PackageManagerRange.parseResult},
1431
+ * {@link PackageManagerRange.fromDevEngine} and
1432
+ * {@link PackageManagerRange.fromDevEngineResult}; the decode direction of
1433
+ * {@link PackageManagerRange.FromString} reports the same failure through a
1434
+ * generic `Schema` parse error carrying the same message.
1435
+ *
1436
+ * @public
1437
+ */
1438
+ export declare class InvalidPackageManagerRangeError extends InvalidPackageManagerRangeError_base {
1439
+ get message(): string;
1440
+ }
1398
1441
  declare const PackageManagerRange_base: Schema.Class<PackageManagerRange, Schema.Struct<{
1399
1442
  /** The package-manager name (e.g. `pnpm`). Any lowercase name — the same latitude as {@link PackageManager}, for the same evidence. */
1400
1443
  readonly name: Schema.String;
@@ -1459,15 +1502,85 @@ export declare class PackageManagerRange extends PackageManagerRange_base {
1459
1502
  * {@link PackageManagerRange}.
1460
1503
  *
1461
1504
  * @remarks
1462
- * Decoding splits on the first `@`, then on the first `+` — which always
1463
- * begins the integrity, never semver build metadata — and validates each
1464
- * component: the name against the lowercase grammar, the range through
1465
- * `@effected/semver`'s `Range.parseResult`, the integrity through
1466
- * `CorepackIntegrityHash`. Every failure is a typed decode failure naming
1467
- * the component that failed. Encoding reconstructs the string from the
1468
- * verbatim parts, so it is byte-identical to any input this codec accepts.
1505
+ * Decoding parses via {@link PackageManagerRange.parseResult}, so every
1506
+ * failure is a typed decode failure naming the component that failed.
1507
+ * Encoding prints `toString()`, reconstructed from the verbatim parts, so
1508
+ * it is byte-identical to any input this codec accepts.
1469
1509
  */
1470
1510
  static readonly FromString: Schema.Codec<PackageManagerRange, string>;
1511
+ /**
1512
+ * Parse a `packageManager` string (`name@range[+integrity]`), synchronously,
1513
+ * returning a `Result` instead of an `Effect`.
1514
+ *
1515
+ * @remarks
1516
+ * Splits on the first `@`, then on the first `+` — which always begins the
1517
+ * integrity, never semver build metadata — and validates each component:
1518
+ * the name against the lowercase grammar, the range through
1519
+ * `@effected/semver`'s `Range.parseResult`, the integrity through
1520
+ * `CorepackIntegrityHash`. {@link PackageManagerRange.parse} is defined in
1521
+ * terms of this function.
1522
+ *
1523
+ * @param input - the `packageManager` value to parse
1524
+ * @returns a `Result` succeeding with the parsed {@link PackageManagerRange},
1525
+ * or failing with {@link InvalidPackageManagerRangeError} naming the
1526
+ * component that failed.
1527
+ */
1528
+ static parseResult(input: string): Result.Result<PackageManagerRange, InvalidPackageManagerRangeError>;
1529
+ /**
1530
+ * Parse a `packageManager` string. Defined in terms of
1531
+ * {@link PackageManagerRange.parseResult}.
1532
+ *
1533
+ * @param input - the `packageManager` value to parse
1534
+ * @returns the parsed {@link PackageManagerRange}. Fails with
1535
+ * {@link InvalidPackageManagerRangeError} when `input` is malformed.
1536
+ */
1537
+ static readonly parse: (input: string) => Effect.Effect<PackageManagerRange, InvalidPackageManagerRangeError, never>;
1538
+ /**
1539
+ * Read a `devEngines.packageManager` entry onto this model, synchronously,
1540
+ * returning a `Result`.
1541
+ *
1542
+ * @remarks
1543
+ * The entry's `name` is the package-manager name and its `version` is the
1544
+ * same `<range>[+<integrity>]` tail the `packageManager` field carries after
1545
+ * its `@` — `^12.6.0`, `12.6.0`, or `12.6.0+sha512.<hex>` as
1546
+ * `pnpm self-update` historically wrote it. Both are validated exactly as
1547
+ * {@link PackageManagerRange.parseResult} validates them. The `version`
1548
+ * slot is optional on a {@link DevEngine}, but an entry without one names
1549
+ * no range, so it fails with `reason: "range"`; `onFail` is ignored. The
1550
+ * parameter is the encoded {@link DevEnginePackageManagerEntry} shape, so a
1551
+ * plain object read off disk needs no `DevEngine` construction first.
1552
+ *
1553
+ * To write the entry back without its integrity (the bare form pnpm 11+
1554
+ * writes), use `range` — the verbatim range with its operator kept and the
1555
+ * integrity dropped.
1556
+ *
1557
+ * @param engine - the `devEngines.packageManager` entry to read
1558
+ * @returns a `Result` succeeding with the {@link PackageManagerRange}, or
1559
+ * failing with {@link InvalidPackageManagerRangeError}.
1560
+ */
1561
+ static fromDevEngineResult(engine: DevEnginePackageManagerEntry): Result.Result<PackageManagerRange, InvalidPackageManagerRangeError>;
1562
+ /**
1563
+ * Read a `devEngines.packageManager` entry onto this model. Defined in
1564
+ * terms of {@link PackageManagerRange.fromDevEngineResult}.
1565
+ *
1566
+ * @param engine - the `devEngines.packageManager` entry to read
1567
+ * @returns the {@link PackageManagerRange}. Fails with
1568
+ * {@link InvalidPackageManagerRangeError} when the name, range or integrity
1569
+ * is malformed, or the entry has no `version`.
1570
+ */
1571
+ static readonly fromDevEngine: (engine: DevEnginePackageManagerEntry) => Effect.Effect<PackageManagerRange, InvalidPackageManagerRangeError, never>;
1572
+ /**
1573
+ * The `packageManager` value without its integrity: `<name>@<range>`, range
1574
+ * operator kept (`pnpm@^12.6.0+sha512.<hex>` → `pnpm@^12.6.0`). The
1575
+ * `devEngines` counterpart is `range` itself.
1576
+ */
1577
+ get bare(): string;
1578
+ /**
1579
+ * The value as parsed: `<name>@<range>` or `<name>@<range>+<integrity>`.
1580
+ * The encode direction of {@link PackageManagerRange.FromString} prints
1581
+ * exactly this.
1582
+ */
1583
+ toString(): string;
1471
1584
  /**
1472
1585
  * Whether the range is an exact, pinnable version (`11.2.0`) rather than a
1473
1586
  * genuine range (`^11.20.0`) — decided by `SemVer.isPinnable` over the
@@ -1476,6 +1589,47 @@ export declare class PackageManagerRange extends PackageManagerRange_base {
1476
1589
  * must re-emit the same spelling it read.
1477
1590
  */
1478
1591
  get isExact(): boolean;
1592
+ /**
1593
+ * The range's operator when it is a single exact, caret or tilde comparator
1594
+ * over a pinnable version: `""` for `12.6.0`, `"^"` for `^12.6.0`, `"~"` for
1595
+ * `~12.6.0`. `Option.none()` for anything else (`>=12 <13`, `12.x`,
1596
+ * `=12.6.0`, `^12`), whose operator cannot be carried onto a new version
1597
+ * unambiguously.
1598
+ */
1599
+ get operator(): Option.Option<"" | "^" | "~">;
1600
+ /**
1601
+ * The version a single exact, caret or tilde comparator is anchored on
1602
+ * (`^12.6.0` → `12.6.0`); `Option.none()` exactly when
1603
+ * {@link PackageManagerRange.operator} is.
1604
+ */
1605
+ get baseVersion(): Option.Option<string>;
1606
+ /**
1607
+ * The same range re-anchored on `version`, operator kept and integrity
1608
+ * dropped (`^12.6.0+sha512.<hex>` with `12.8.1` → `^12.8.1`), synchronously,
1609
+ * returning a `Result`.
1610
+ *
1611
+ * @remarks
1612
+ * Only a single exact, caret or tilde comparator can be re-anchored (see
1613
+ * {@link PackageManagerRange.operator}); any other range, or a `version`
1614
+ * that is not a pinnable semver version, fails with `reason: "range"`. The
1615
+ * integrity is dropped because it named the old version's artifact.
1616
+ *
1617
+ * @param version - the pinnable version to anchor the range on
1618
+ * @returns a `Result` succeeding with the re-anchored
1619
+ * {@link PackageManagerRange}, or failing with
1620
+ * {@link InvalidPackageManagerRangeError}.
1621
+ */
1622
+ withVersionResult(version: string): Result.Result<PackageManagerRange, InvalidPackageManagerRangeError>;
1623
+ /**
1624
+ * The same range re-anchored on `version`. Defined in terms of
1625
+ * {@link PackageManagerRange.withVersionResult}.
1626
+ *
1627
+ * @param version - the pinnable version to anchor the range on
1628
+ * @returns the re-anchored {@link PackageManagerRange}. Fails with
1629
+ * {@link InvalidPackageManagerRangeError} when the range is not a single
1630
+ * exact, caret or tilde comparator, or `version` is not pinnable.
1631
+ */
1632
+ withVersion(version: string): Effect.Effect<PackageManagerRange, InvalidPackageManagerRangeError>;
1479
1633
  /** Whether an integrity hash is present. */
1480
1634
  get hasIntegrity(): boolean;
1481
1635
  }
@@ -1865,5 +2019,5 @@ export declare class PackageValidator extends PackageValidator_base {
1865
2019
  }): Layer.Layer<PackageValidator>;
1866
2020
  }
1867
2021
  //#endregion
1868
- export { type DependencyKind, type DependencyProtocol, DependencySpecifier, type DependencySpecifierBrand, type DevEngines, type EntryPointManifest, InvalidDependencySpecifierError, JsoncEdit, type JsoncPath, type LenientFieldIssue, type PackageFieldEdit, type PackageFormatOptions, type PackageFormatTextOptions, type PackageIndent, type PackageJsonFileShape, type PackagePatch, type ResolveEntryPointOptions, type RuleFailure, type UnresolvedDependency, type ValidationRule, isValidDependencySpecifier };
2022
+ export { type DependencyKind, type DependencyProtocol, DependencySpecifier, type DependencySpecifierBrand, type DevEnginePackageManagerEntry, type DevEngines, type EntryPointManifest, InvalidDependencySpecifierError, JsoncEdit, type JsoncPath, type LenientFieldIssue, type PackageFieldEdit, type PackageFormatOptions, type PackageFormatTextOptions, type PackageIndent, type PackageJsonFileShape, type PackagePatch, type ResolveEntryPointOptions, type RuleFailure, type UnresolvedDependency, type ValidationRule, isValidDependencySpecifier };
1869
2023
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -10,11 +10,11 @@ import { Bugs, Repository } from "./Repository.js";
10
10
  import { BinField, DependencyMapField, ExportsField, Package, PackageDecodeError, PeerDependenciesMetaField, PublishConfigField, RepositoryField, StringMapField } from "./Package.js";
11
11
  import { PackageJsonFormat, PackageJsonModifyError, PackageJsonSyntaxError } from "./PackageJsonFormat.js";
12
12
  import { LenientManifest } from "./LenientManifest.js";
13
- import { PackageManagerRange } from "./PackageManagerRange.js";
13
+ import { InvalidPackageManagerRangeError, PackageManagerRange } from "./PackageManagerRange.js";
14
14
  import { PackageManifest } from "./PackageManifest.js";
15
15
  import { PackageJsonFile, PackageJsonNotFoundError, PackageJsonParseError, PackageJsonReadError, PackageJsonWriteError } from "./PackageJsonFile.js";
16
16
  import { PackageValidationError, PackageValidator, defaultRules, noLocalDepsRule, noUnresolvedDepsRule } from "./PackageValidator.js";
17
17
  import { JsoncEdit } from "@effected/jsonc";
18
18
  import { DependencySpecifier, InvalidDependencySpecifierError, isValidDependencySpecifier } from "@effected/npm";
19
19
 
20
- export { BinField, Bugs, Dependency, DependencyMapField, DependencySpecifier, DevEngine, DevEngineOrArray, DevEnginesSchema, ExportsField, Funding, InvalidDependencySpecifierError, InvalidPackageNameError, InvalidSpdxLicenseError, JsoncEdit, LenientManifest, Package, PackageDecodeError, PackageJsonFile, PackageJsonFormat, PackageJsonModifyError, PackageJsonNotFoundError, PackageJsonParseError, PackageJsonReadError, PackageJsonSyntaxError, PackageJsonWriteError, PackageManager, PackageManagerRange, PackageManifest, PackageName, PackageValidationError, PackageValidator, PeerDependenciesMetaField, Person, PublishConfigField, Repository, RepositoryField, ScopedPackageName, SpdxLicense, StringMapField, UnresolvedEntryPointError, UnscopedPackageName, defaultRules, isUnresolvedDependency, isValidDependencySpecifier, isValidSpdx, licenseExpressionOf, noLocalDepsRule, noUnresolvedDepsRule, resolveEntryPoint };
20
+ export { BinField, Bugs, Dependency, DependencyMapField, DependencySpecifier, DevEngine, DevEngineOrArray, DevEnginesSchema, ExportsField, Funding, InvalidDependencySpecifierError, InvalidPackageManagerRangeError, InvalidPackageNameError, InvalidSpdxLicenseError, JsoncEdit, LenientManifest, Package, PackageDecodeError, PackageJsonFile, PackageJsonFormat, PackageJsonModifyError, PackageJsonNotFoundError, PackageJsonParseError, PackageJsonReadError, PackageJsonSyntaxError, PackageJsonWriteError, PackageManager, PackageManagerRange, PackageManifest, PackageName, PackageValidationError, PackageValidator, PeerDependenciesMetaField, Person, PublishConfigField, Repository, RepositoryField, ScopedPackageName, SpdxLicense, StringMapField, UnresolvedEntryPointError, UnscopedPackageName, defaultRules, isUnresolvedDependency, isValidDependencySpecifier, isValidSpdx, licenseExpressionOf, noLocalDepsRule, noUnresolvedDepsRule, resolveEntryPoint };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effected/package-json",
3
- "version": "0.16.0",
3
+ "version": "0.18.0-next-20260927062352",
4
4
  "private": false,
5
5
  "description": "package.json parsing, editing, validation and file IO as Effect schemas.",
6
6
  "keywords": [
@@ -38,13 +38,13 @@
38
38
  "./package.json": "./package.json"
39
39
  },
40
40
  "dependencies": {
41
- "@effected/jsonc": "^0.12.0",
42
- "@effected/npm": "^0.15.0",
43
- "@effected/semver": "^0.8.0",
44
- "@effected/spdx": "^0.8.0"
41
+ "@effected/jsonc": "^0.13.0",
42
+ "@effected/npm": "^0.17.0-next-20260927062352",
43
+ "@effected/semver": "^0.9.0",
44
+ "@effected/spdx": "^0.9.0"
45
45
  },
46
46
  "peerDependencies": {
47
- "effect": "4.0.0-rc.116"
47
+ "effect": "4.0.0-rc.117"
48
48
  },
49
49
  "engines": {
50
50
  "node": ">=24.11.0"