@effected/package-json 0.7.3 → 0.9.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/License.js +1 -1
- package/Package.js +3 -31
- package/PackageJsonFile.js +56 -31
- package/PackageJsonFormat.js +100 -5
- package/PackageManager.js +1 -1
- package/PackageManagerRange.js +124 -0
- package/PackageManifest.js +93 -0
- package/PackageName.js +1 -1
- package/PackageValidator.js +1 -1
- package/index.d.ts +433 -112
- package/index.js +5 -2
- package/internal/format.js +13 -1
- package/internal/wire.js +35 -0
- package/package.json +6 -5
package/License.js
CHANGED
|
@@ -10,7 +10,7 @@ import { isValidExpression } from "@effected/spdx";
|
|
|
10
10
|
*
|
|
11
11
|
* @public
|
|
12
12
|
*/
|
|
13
|
-
var InvalidSpdxLicenseError = class extends Schema.
|
|
13
|
+
var InvalidSpdxLicenseError = class extends Schema.TaggedError()("InvalidSpdxLicenseError", {
|
|
14
14
|
/** The raw input string that failed validation. */
|
|
15
15
|
input: Schema.String }) {
|
|
16
16
|
get message() {
|
package/Package.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Dependency } from "./Dependency.js";
|
|
2
2
|
import { DevEnginesSchema } from "./DevEngines.js";
|
|
3
3
|
import { InvalidSpdxLicenseError, SpdxLicense, isValidSpdx } from "./License.js";
|
|
4
|
-
import { renderJson,
|
|
4
|
+
import { renderJson, resolveFormatOptions } from "./internal/format.js";
|
|
5
|
+
import { makeWire } from "./internal/wire.js";
|
|
5
6
|
import { PackageManager } from "./PackageManager.js";
|
|
6
7
|
import { InvalidPackageNameError, PackageName } from "./PackageName.js";
|
|
7
8
|
import { Person } from "./Person.js";
|
|
@@ -80,42 +81,13 @@ const RepositoryField = Schema.Union([Schema.String, Schema.Record(Schema.String
|
|
|
80
81
|
*
|
|
81
82
|
* @public
|
|
82
83
|
*/
|
|
83
|
-
var PackageDecodeError = class extends Schema.
|
|
84
|
+
var PackageDecodeError = class extends Schema.TaggedError()("PackageDecodeError", {
|
|
84
85
|
/** The underlying `SchemaError`, preserved structurally rather than stringified. */
|
|
85
86
|
cause: Schema.Defect() }) {
|
|
86
87
|
get message() {
|
|
87
88
|
return "Failed to decode package.json";
|
|
88
89
|
}
|
|
89
90
|
};
|
|
90
|
-
const resolveFormatOptions = (options) => ({
|
|
91
|
-
indent: resolveIndent(options?.indent, options?.sourceText),
|
|
92
|
-
sort: options?.sort ?? true,
|
|
93
|
-
stripEmpty: options?.stripEmpty ?? true,
|
|
94
|
-
newline: options?.newline ?? true
|
|
95
|
-
});
|
|
96
|
-
const RawJson = Schema.Record(Schema.String, Schema.Unknown);
|
|
97
|
-
const makeWire = (Class) => {
|
|
98
|
-
const knownKeys = new Set(Object.keys(Class.fields).filter((k) => k !== "rest"));
|
|
99
|
-
return RawJson.pipe(Schema.decodeTo(Class, SchemaTransformation.transform({
|
|
100
|
-
decode: (raw) => {
|
|
101
|
-
const known = {};
|
|
102
|
-
const rest = {};
|
|
103
|
-
for (const [key, value] of Object.entries(raw)) if (knownKeys.has(key)) known[key] = value;
|
|
104
|
-
else rest[key] = value;
|
|
105
|
-
return {
|
|
106
|
-
...known,
|
|
107
|
-
rest
|
|
108
|
-
};
|
|
109
|
-
},
|
|
110
|
-
encode: (encoded) => {
|
|
111
|
-
const { rest, ...known } = encoded;
|
|
112
|
-
return {
|
|
113
|
-
...rest ?? {},
|
|
114
|
-
...known
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
})));
|
|
118
|
-
};
|
|
119
91
|
/**
|
|
120
92
|
* A package.json document as a rich `Schema.Class`: typed known fields, a
|
|
121
93
|
* `rest` catch-all preserving unknown top-level fields across a read/edit/write
|
package/PackageJsonFile.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Package } from "./Package.js";
|
|
2
|
+
import { PackageJsonFormat } from "./PackageJsonFormat.js";
|
|
3
|
+
import { PackageManifest } from "./PackageManifest.js";
|
|
2
4
|
import { Context, Effect, FileSystem, Layer, Path, Schema } from "effect";
|
|
3
5
|
|
|
4
6
|
//#region src/PackageJsonFile.ts
|
|
@@ -8,7 +10,7 @@ import { Context, Effect, FileSystem, Layer, Path, Schema } from "effect";
|
|
|
8
10
|
*
|
|
9
11
|
* @public
|
|
10
12
|
*/
|
|
11
|
-
var PackageJsonReadError = class extends Schema.
|
|
13
|
+
var PackageJsonReadError = class extends Schema.TaggedError()("PackageJsonReadError", {
|
|
12
14
|
/** The path that could not be read. */
|
|
13
15
|
path: Schema.String,
|
|
14
16
|
/** The underlying failure, preserved structurally. */
|
|
@@ -24,7 +26,7 @@ var PackageJsonReadError = class extends Schema.TaggedErrorClass()("PackageJsonR
|
|
|
24
26
|
*
|
|
25
27
|
* @public
|
|
26
28
|
*/
|
|
27
|
-
var PackageJsonNotFoundError = class extends Schema.
|
|
29
|
+
var PackageJsonNotFoundError = class extends Schema.TaggedError()("PackageJsonNotFoundError", {
|
|
28
30
|
/** The path where package.json was expected. */
|
|
29
31
|
path: Schema.String }) {
|
|
30
32
|
get message() {
|
|
@@ -36,7 +38,7 @@ path: Schema.String }) {
|
|
|
36
38
|
*
|
|
37
39
|
* @public
|
|
38
40
|
*/
|
|
39
|
-
var PackageJsonParseError = class extends Schema.
|
|
41
|
+
var PackageJsonParseError = class extends Schema.TaggedError()("PackageJsonParseError", {
|
|
40
42
|
/** The path whose contents failed to parse as JSON. */
|
|
41
43
|
path: Schema.String,
|
|
42
44
|
/** The underlying `SyntaxError`, preserved structurally. */
|
|
@@ -53,7 +55,7 @@ var PackageJsonParseError = class extends Schema.TaggedErrorClass()("PackageJson
|
|
|
53
55
|
*
|
|
54
56
|
* @public
|
|
55
57
|
*/
|
|
56
|
-
var PackageJsonWriteError = class extends Schema.
|
|
58
|
+
var PackageJsonWriteError = class extends Schema.TaggedError()("PackageJsonWriteError", {
|
|
57
59
|
/** The path that could not be written. */
|
|
58
60
|
path: Schema.String,
|
|
59
61
|
/** The underlying filesystem failure, preserved structurally. Narrowed to the write failure only. */
|
|
@@ -88,40 +90,63 @@ var PackageJsonFile = class PackageJsonFile extends Context.Service()("@effected
|
|
|
88
90
|
static make = Effect.gen(function* () {
|
|
89
91
|
const fs = yield* FileSystem.FileSystem;
|
|
90
92
|
const path = yield* Path.Path;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
const readText = (target) => 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 readJson = (target) => Effect.gen(function* () {
|
|
98
|
+
const content = yield* readText(target);
|
|
99
|
+
return yield* Effect.try({
|
|
100
|
+
try: () => JSON.parse(content),
|
|
101
|
+
catch: (cause) => new PackageJsonParseError({
|
|
94
102
|
path: target,
|
|
95
103
|
cause
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
})
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
const withPreservedSource = (target, options) => Effect.gen(function* () {
|
|
108
|
+
if (options?.indent !== "preserve" || options.sourceText !== void 0) return options;
|
|
109
|
+
const existing = yield* fs.readFileString(target).pipe(Effect.catch(() => Effect.succeed(void 0)));
|
|
110
|
+
return existing === void 0 ? options : {
|
|
111
|
+
...options,
|
|
112
|
+
sourceText: existing
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
const writeText = (target, json) => Effect.gen(function* () {
|
|
116
|
+
const directory = path.dirname(target);
|
|
117
|
+
yield* fs.makeDirectory(directory, { recursive: true }).pipe(Effect.mapError((cause) => new PackageJsonWriteError({
|
|
118
|
+
path: target,
|
|
119
|
+
cause
|
|
120
|
+
})));
|
|
121
|
+
yield* fs.writeFileString(target, json).pipe(Effect.mapError((cause) => new PackageJsonWriteError({
|
|
122
|
+
path: target,
|
|
123
|
+
cause
|
|
124
|
+
})));
|
|
125
|
+
});
|
|
126
|
+
return {
|
|
127
|
+
read: Effect.fn("PackageJsonFile.read")(function* (target) {
|
|
128
|
+
return yield* Package.decode(yield* readJson(target));
|
|
105
129
|
}),
|
|
106
130
|
write: Effect.fn("PackageJsonFile.write")(function* (target, pkg, options) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
yield* fs.writeFileString(target, json).pipe(Effect.mapError((cause) => new PackageJsonWriteError({
|
|
131
|
+
const effective = yield* withPreservedSource(target, options);
|
|
132
|
+
yield* writeText(target, pkg.toJsonString(effective));
|
|
133
|
+
}),
|
|
134
|
+
readManifest: Effect.fn("PackageJsonFile.readManifest")(function* (target) {
|
|
135
|
+
return yield* PackageManifest.decode(yield* readJson(target));
|
|
136
|
+
}),
|
|
137
|
+
writeManifest: Effect.fn("PackageJsonFile.writeManifest")(function* (target, manifest, options) {
|
|
138
|
+
const effective = yield* withPreservedSource(target, options);
|
|
139
|
+
yield* writeText(target, manifest.toJsonString(effective));
|
|
140
|
+
}),
|
|
141
|
+
modify: Effect.fn("PackageJsonFile.modify")(function* (target, edits) {
|
|
142
|
+
const source = yield* readText(target);
|
|
143
|
+
let text = source;
|
|
144
|
+
for (const edit of edits) text = yield* PackageJsonFormat.modifyToString(text, edit.path, edit.value).pipe(Effect.catchTag("PackageJsonSyntaxError", (cause) => new PackageJsonParseError({
|
|
122
145
|
path: target,
|
|
123
146
|
cause
|
|
124
147
|
})));
|
|
148
|
+
if (text !== source) yield* writeText(target, text);
|
|
149
|
+
return text;
|
|
125
150
|
})
|
|
126
151
|
};
|
|
127
152
|
});
|
package/PackageJsonFormat.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { renderJson, resolveIndent, sortKeys } from "./internal/format.js";
|
|
2
|
-
import {
|
|
1
|
+
import { detectIndent, renderJson, resolveIndent, sortKeys } from "./internal/format.js";
|
|
2
|
+
import { JsoncEdit, JsoncModifier } from "@effected/jsonc";
|
|
3
|
+
import { Effect, Result, Schema } from "effect";
|
|
3
4
|
|
|
4
5
|
//#region src/PackageJsonFormat.ts
|
|
5
6
|
/**
|
|
@@ -14,7 +15,7 @@ import { Result, Schema } from "effect";
|
|
|
14
15
|
*
|
|
15
16
|
* @public
|
|
16
17
|
*/
|
|
17
|
-
var PackageJsonSyntaxError = class extends Schema.
|
|
18
|
+
var PackageJsonSyntaxError = class extends Schema.TaggedError()("PackageJsonSyntaxError", {
|
|
18
19
|
/** Which syntactic precondition failed. */
|
|
19
20
|
reason: Schema.Literals(["invalid-json", "not-an-object"]),
|
|
20
21
|
/** The underlying `SyntaxError` for `"invalid-json"`, preserved structurally. */
|
|
@@ -26,6 +27,28 @@ var PackageJsonSyntaxError = class extends Schema.TaggedErrorClass()("PackageJso
|
|
|
26
27
|
};
|
|
27
28
|
const isJsonObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28
29
|
/**
|
|
30
|
+
* Indicates that a surgical modification could not be applied: the value on
|
|
31
|
+
* the navigation path is not the container kind the next path segment
|
|
32
|
+
* requires. The underlying `@effected/jsonc` `JsoncModificationError` —
|
|
33
|
+
* which names the expected container and the 1-based depth of the mismatch —
|
|
34
|
+
* is preserved on the structured `cause` field, never stringified.
|
|
35
|
+
*
|
|
36
|
+
* Raised by {@link PackageJsonFormat.modify} and
|
|
37
|
+
* {@link PackageJsonFormat.modifyToString}.
|
|
38
|
+
*
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
41
|
+
var PackageJsonModifyError = class extends Schema.TaggedError()("PackageJsonModifyError", {
|
|
42
|
+
/** The field path whose navigation failed. */
|
|
43
|
+
path: Schema.Array(Schema.Union([Schema.String, Schema.Number])),
|
|
44
|
+
/** The underlying `JsoncModificationError`, preserved structurally. */
|
|
45
|
+
cause: Schema.Defect()
|
|
46
|
+
}) {
|
|
47
|
+
get message() {
|
|
48
|
+
return `Failed to modify package.json at path [${this.path.join(", ")}]`;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
29
52
|
* Decode-free canonical sort and format statics. Not instantiable.
|
|
30
53
|
*
|
|
31
54
|
* @remarks
|
|
@@ -44,7 +67,7 @@ const isJsonObject = (value) => typeof value === "object" && value !== null && !
|
|
|
44
67
|
*
|
|
45
68
|
* @public
|
|
46
69
|
*/
|
|
47
|
-
var PackageJsonFormat = class {
|
|
70
|
+
var PackageJsonFormat = class PackageJsonFormat {
|
|
48
71
|
constructor() {}
|
|
49
72
|
/**
|
|
50
73
|
* Order a package.json object's keys canonically **without decoding it into
|
|
@@ -132,7 +155,79 @@ var PackageJsonFormat = class {
|
|
|
132
155
|
newline: options?.newline ?? true
|
|
133
156
|
}));
|
|
134
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Compute the surgical edits that set, replace or delete the value at
|
|
160
|
+
* `path` **without decoding, sorting or reformatting anything else**. The
|
|
161
|
+
* opposite posture to {@link PackageJsonFormat.formatToString}: where the
|
|
162
|
+
* formatter's job is the canonical order, the mutator's job is to leave
|
|
163
|
+
* every untouched byte untouched — key order, indentation, line endings and
|
|
164
|
+
* the trailing newline all survive, because only the edited span changes.
|
|
165
|
+
* That is what makes the result reviewable when a tool commits a one-field
|
|
166
|
+
* change to someone else's repository.
|
|
167
|
+
*
|
|
168
|
+
* Built on `@effected/jsonc`'s scanner-based edit engine. Inserted content
|
|
169
|
+
* matches the source's own style: indentation (tab vs N spaces) is detected
|
|
170
|
+
* from the first indented line and the line ending from the first `\r\n`.
|
|
171
|
+
*
|
|
172
|
+
* Passing `value === undefined` deletes the target key (including its
|
|
173
|
+
* comma) — the `@effected/jsonc` / `@effected/yaml` modify convention. A
|
|
174
|
+
* missing insertion target appends after the last key of its container.
|
|
175
|
+
*
|
|
176
|
+
* @param source - the package.json file contents (strict JSON — npm does
|
|
177
|
+
* not accept comments, and neither does this)
|
|
178
|
+
* @param path - the field path, e.g. `["packageManager"]` or
|
|
179
|
+
* `["devEngines", "runtime", "version"]`
|
|
180
|
+
* @param value - the plain JSON value to write, or `undefined` to delete
|
|
181
|
+
* @returns the edits to apply via `JsoncEdit.applyAll` — or use
|
|
182
|
+
* {@link PackageJsonFormat.modifyToString} for the applied text in one step
|
|
183
|
+
*/
|
|
184
|
+
static modify = Effect.fn("PackageJsonFormat.modify")(function* (source, path, value) {
|
|
185
|
+
let parsed;
|
|
186
|
+
try {
|
|
187
|
+
parsed = JSON.parse(source);
|
|
188
|
+
} catch (cause) {
|
|
189
|
+
return yield* new PackageJsonSyntaxError({
|
|
190
|
+
reason: "invalid-json",
|
|
191
|
+
cause
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (!isJsonObject(parsed)) return yield* new PackageJsonSyntaxError({ reason: "not-an-object" });
|
|
195
|
+
const indent = detectIndent(source);
|
|
196
|
+
const formattingOptions = {
|
|
197
|
+
insertSpaces: indent !== " ",
|
|
198
|
+
tabSize: indent === void 0 || indent === " " ? 2 : indent.length,
|
|
199
|
+
eol: source.includes("\r\n") ? "\r\n" : "\n"
|
|
200
|
+
};
|
|
201
|
+
return yield* JsoncModifier.modify(source, path, value, { formattingOptions }).pipe(Effect.catchTag("JsoncModificationError", (cause) => new PackageJsonModifyError({
|
|
202
|
+
path,
|
|
203
|
+
cause
|
|
204
|
+
})));
|
|
205
|
+
});
|
|
206
|
+
/**
|
|
207
|
+
* Modify `source` and apply the resulting edits in one step
|
|
208
|
+
* (`JsoncEdit.applyAll` composed over {@link PackageJsonFormat.modify}).
|
|
209
|
+
* Text in, text out; every byte outside the edited span is preserved.
|
|
210
|
+
* Inherits the modify error channel: {@link PackageJsonSyntaxError} when
|
|
211
|
+
* the source is not a JSON object, {@link PackageJsonModifyError} when the
|
|
212
|
+
* path cannot be navigated.
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```ts
|
|
216
|
+
* import { PackageJsonFormat } from "@effected/package-json";
|
|
217
|
+
* import { Effect } from "effect";
|
|
218
|
+
*
|
|
219
|
+
* const program = PackageJsonFormat.modifyToString(
|
|
220
|
+
* '{\n "private": true,\n "packageManager": "pnpm@11.2.0"\n}\n',
|
|
221
|
+
* ["packageManager"],
|
|
222
|
+
* "pnpm@11.3.0",
|
|
223
|
+
* ); // only the packageManager value changes; every other byte survives
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
static modifyToString = Effect.fn("PackageJsonFormat.modifyToString")(function* (source, path, value) {
|
|
227
|
+
const edits = yield* PackageJsonFormat.modify(source, path, value);
|
|
228
|
+
return JsoncEdit.applyAll(source, edits);
|
|
229
|
+
});
|
|
135
230
|
};
|
|
136
231
|
|
|
137
232
|
//#endregion
|
|
138
|
-
export { PackageJsonFormat, PackageJsonSyntaxError };
|
|
233
|
+
export { PackageJsonFormat, PackageJsonModifyError, PackageJsonSyntaxError };
|
package/PackageManager.js
CHANGED
|
@@ -4,7 +4,7 @@ import { SemVer } from "@effected/semver";
|
|
|
4
4
|
|
|
5
5
|
//#region src/PackageManager.ts
|
|
6
6
|
const PACKAGE_MANAGER_NAME_RE = /^[a-z]+$/;
|
|
7
|
-
const invalid = (input, message) => Effect.fail(new SchemaIssue.InvalidValue(
|
|
7
|
+
const invalid = (input, message) => Effect.fail(new SchemaIssue.InvalidValue({ message }, input));
|
|
8
8
|
/**
|
|
9
9
|
* A structured `packageManager` value with `name`, `version` and an optional
|
|
10
10
|
* `integrity` hash.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { CorepackIntegrityHash } from "@effected/npm";
|
|
2
|
+
import { Effect, Exit, Option, Result, Schema, SchemaIssue, SchemaTransformation } from "effect";
|
|
3
|
+
import { Range, SemVer } from "@effected/semver";
|
|
4
|
+
|
|
5
|
+
//#region src/PackageManagerRange.ts
|
|
6
|
+
const PACKAGE_MANAGER_NAME_RE = /^[a-z]+$/;
|
|
7
|
+
const invalid = (input, message) => Effect.fail(new SchemaIssue.InvalidValue({ message }, input));
|
|
8
|
+
/** `Schema.String` refined to parse as a semver range (`Range.parseResult` succeeds). The check is erased from the built type. */
|
|
9
|
+
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
|
+
/**
|
|
11
|
+
* A structured `packageManager` value whose version position is a semver
|
|
12
|
+
* **range**, carried verbatim: `name`, `range` and an optional `integrity`
|
|
13
|
+
* hash.
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* The range-tolerant sibling of {@link PackageManager}. The strict class
|
|
17
|
+
* models the corepack pin — an exact version, which is all corepack itself
|
|
18
|
+
* accepts — and stays strict; this class models the field as pnpm reads it,
|
|
19
|
+
* where a range such as `^11.20.0` is a supported spelling that pnpm resolves
|
|
20
|
+
* to a concrete version. An exact version is a valid range, so every string
|
|
21
|
+
* the strict codec accepts decodes here too; {@link PackageManagerRange.isExact}
|
|
22
|
+
* is how a caller tracks which form the manifest actually carried.
|
|
23
|
+
*
|
|
24
|
+
* The `range` field is the manifest's text **verbatim** — validated to parse
|
|
25
|
+
* as a semver range but never normalized, so encoding is byte-identical to
|
|
26
|
+
* the accepted input and reading a manifest never rewrites the field.
|
|
27
|
+
* Interpretation is a derived getter, following `Repository`'s
|
|
28
|
+
* carry-verbatim posture.
|
|
29
|
+
*
|
|
30
|
+
* The first `+` after the `@` begins the integrity component, exactly as in
|
|
31
|
+
* the strict grammar — the version position of this field never carries
|
|
32
|
+
* semver build metadata.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { PackageManagerRange } from "@effected/package-json";
|
|
37
|
+
* import { Effect, Schema } from "effect";
|
|
38
|
+
*
|
|
39
|
+
* const program = Effect.gen(function* () {
|
|
40
|
+
* const pm = yield* Schema.decodeUnknownEffect(PackageManagerRange.FromString)("pnpm@^11.20.0");
|
|
41
|
+
* console.log(pm.name, pm.range, pm.isExact); // "pnpm" "^11.20.0" false
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
var PackageManagerRange = class PackageManagerRange extends Schema.Class("PackageManagerRange")({
|
|
48
|
+
/** The package-manager name (e.g. `pnpm`). Any lowercase name — the same latitude as {@link PackageManager}, for the same evidence. */
|
|
49
|
+
name: Schema.String,
|
|
50
|
+
/**
|
|
51
|
+
* The version position, verbatim: a semver range (`^11.20.0`,
|
|
52
|
+
* `>=10 <12`, ...) or an exact version (`11.2.0`). Validated to parse
|
|
53
|
+
* through `@effected/semver`'s `Range.parseResult`; never normalized, so
|
|
54
|
+
* the field round-trips byte-identically.
|
|
55
|
+
*/
|
|
56
|
+
range: SemVerRangeString,
|
|
57
|
+
/**
|
|
58
|
+
* The optional integrity hash (e.g. `sha512.abc`): `@effected/npm`'s
|
|
59
|
+
* `CorepackIntegrityHash`. Meaningful only alongside an exact range —
|
|
60
|
+
* an integrity pins one artifact — but carried whenever the manifest
|
|
61
|
+
* carries it, because fidelity outranks plausibility in a field model.
|
|
62
|
+
*/
|
|
63
|
+
integrity: Schema.Option(CorepackIntegrityHash)
|
|
64
|
+
}) {
|
|
65
|
+
/**
|
|
66
|
+
* Schema transformation between the `"name@range[+integrity]"` string and a
|
|
67
|
+
* {@link PackageManagerRange}.
|
|
68
|
+
*
|
|
69
|
+
* @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.
|
|
77
|
+
*/
|
|
78
|
+
static FromString = Schema.String.pipe(Schema.decodeTo(Schema.instanceOf(PackageManagerRange), SchemaTransformation.transformOrFail({
|
|
79
|
+
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
|
+
}));
|
|
101
|
+
},
|
|
102
|
+
encode: (pm) => Effect.succeed(Option.match(pm.integrity, {
|
|
103
|
+
onNone: () => `${pm.name}@${pm.range}`,
|
|
104
|
+
onSome: (integrity) => `${pm.name}@${pm.range}+${integrity}`
|
|
105
|
+
}))
|
|
106
|
+
})));
|
|
107
|
+
/**
|
|
108
|
+
* Whether the range is an exact, pinnable version (`11.2.0`) rather than a
|
|
109
|
+
* genuine range (`^11.20.0`) — decided by `SemVer.isPinnable` over the
|
|
110
|
+
* verbatim text, so `=11.2.0` and other range spellings of a single
|
|
111
|
+
* version report `false`. This is the exactness a consumer tracks when it
|
|
112
|
+
* must re-emit the same spelling it read.
|
|
113
|
+
*/
|
|
114
|
+
get isExact() {
|
|
115
|
+
return SemVer.isPinnable(this.range);
|
|
116
|
+
}
|
|
117
|
+
/** Whether an integrity hash is present. */
|
|
118
|
+
get hasIntegrity() {
|
|
119
|
+
return Option.isSome(this.integrity);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
//#endregion
|
|
124
|
+
export { PackageManagerRange };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { renderJson, resolveFormatOptions } from "./internal/format.js";
|
|
2
|
+
import { makeWire } from "./internal/wire.js";
|
|
3
|
+
import { PackageName } from "./PackageName.js";
|
|
4
|
+
import { Package, PackageDecodeError } from "./Package.js";
|
|
5
|
+
import { PackageManagerRange } from "./PackageManagerRange.js";
|
|
6
|
+
import { Effect, Schema } from "effect";
|
|
7
|
+
import { SemVer } from "@effected/semver";
|
|
8
|
+
|
|
9
|
+
//#region src/PackageManifest.ts
|
|
10
|
+
/**
|
|
11
|
+
* A package.json document as it exists on disk, publishable or not: every
|
|
12
|
+
* field of {@link Package} with `name` and `version` optional and
|
|
13
|
+
* `packageManager` accepting the range spelling.
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* **Lenient about absence, strict about shape.** npm requires `name` and
|
|
17
|
+
* `version` only for a package that will be published; the idiomatic private
|
|
18
|
+
* workspace root (`{ "private": true, "packageManager": "pnpm@11.2.0" }`)
|
|
19
|
+
* carries neither, and {@link Package.decode} rightly rejects it — the strict
|
|
20
|
+
* model's contract is publishability. This class decodes that shape, and any
|
|
21
|
+
* other manifest, so long as every field that IS present satisfies its typed
|
|
22
|
+
* codec: a present `version` must still be strict semver (`"1.0"` fails
|
|
23
|
+
* typed), a present `name` must still satisfy the npm grammar, a present
|
|
24
|
+
* `packageManager` must still parse — though here the version position may be
|
|
25
|
+
* a semver range (`pnpm@^11.20.0`), decoded as {@link PackageManagerRange}.
|
|
26
|
+
* For total tolerance of malformed fields, use the decode-free
|
|
27
|
+
* {@link PackageJsonFormat} text path (or `@effected/npm`'s shape-blind
|
|
28
|
+
* `Manifest`) — silently carrying a value the type claims to have validated
|
|
29
|
+
* would be a lie, and silently dropping it would break round-trip fidelity.
|
|
30
|
+
*
|
|
31
|
+
* The model is deliberately lean — fields, the `rest` catch-all wire codec,
|
|
32
|
+
* {@link PackageManifest.decode} and {@link PackageManifest.toJsonString} —
|
|
33
|
+
* because the write half of a manifest-editing tool is the surgical
|
|
34
|
+
* {@link PackageJsonFormat.modifyToString} / `PackageJsonFile.modify` path,
|
|
35
|
+
* which never goes through a model at all. Mutation statics live on the
|
|
36
|
+
* strict {@link Package}.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { PackageManifest } from "@effected/package-json";
|
|
41
|
+
* import { Effect, Option } from "effect";
|
|
42
|
+
*
|
|
43
|
+
* const program = Effect.gen(function* () {
|
|
44
|
+
* const root = yield* PackageManifest.decode({ private: true, packageManager: "pnpm@^11.20.0" });
|
|
45
|
+
* console.log(root.isPrivate, root.packageManager?.isExact); // true false
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @public
|
|
50
|
+
*/
|
|
51
|
+
var PackageManifest = class PackageManifest extends Schema.Class("PackageManifest")({
|
|
52
|
+
...Package.fields,
|
|
53
|
+
name: Schema.optionalKey(PackageName),
|
|
54
|
+
version: Schema.optionalKey(SemVer.FromString),
|
|
55
|
+
packageManager: Schema.optionalKey(PackageManagerRange.FromString)
|
|
56
|
+
}) {
|
|
57
|
+
/**
|
|
58
|
+
* The wire codec: an open JSON object ↔ a {@link PackageManifest} instance,
|
|
59
|
+
* partitioning unknown keys into `rest` and flattening them back on encode —
|
|
60
|
+
* the same transform {@link Package.schema} uses, over this class's fields.
|
|
61
|
+
*/
|
|
62
|
+
static schema = makeWire(PackageManifest);
|
|
63
|
+
/**
|
|
64
|
+
* Decode an unknown JSON value into a {@link PackageManifest}, normalizing
|
|
65
|
+
* any `SchemaError` to a typed {@link PackageDecodeError} at the boundary.
|
|
66
|
+
*
|
|
67
|
+
* @param input - the parsed package.json JSON value (e.g. from `JSON.parse`)
|
|
68
|
+
* @returns an Effect resolving to the decoded `PackageManifest`
|
|
69
|
+
* @throws (typed) `PackageDecodeError` when a present field does not satisfy
|
|
70
|
+
* its codec
|
|
71
|
+
*/
|
|
72
|
+
static decode = Effect.fn("PackageManifest.decode")(function* (input) {
|
|
73
|
+
return yield* Schema.decodeUnknownEffect(PackageManifest.schema)(input).pipe(Effect.catchTag("SchemaError", (cause) => new PackageDecodeError({ cause })));
|
|
74
|
+
});
|
|
75
|
+
/** Whether the manifest is marked private. */
|
|
76
|
+
get isPrivate() {
|
|
77
|
+
return this.private ?? false;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Serialize to a formatted package.json string: encode through the wire
|
|
81
|
+
* codec (flattening `rest`), then apply the canonical key order, dependency
|
|
82
|
+
* sorting and empty-map stripping unless the options opt out. Pure, and
|
|
83
|
+
* shared with `Package.toJsonString` down to the same internal renderer.
|
|
84
|
+
* Absent `name` / `version` keys stay absent — nothing is invented.
|
|
85
|
+
*/
|
|
86
|
+
toJsonString(options) {
|
|
87
|
+
const raw = Schema.encodeUnknownSync(PackageManifest.schema)(this);
|
|
88
|
+
return renderJson(raw, resolveFormatOptions(options));
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
export { PackageManifest };
|
package/PackageName.js
CHANGED
|
@@ -9,7 +9,7 @@ import { Option, Schema } from "effect";
|
|
|
9
9
|
*
|
|
10
10
|
* @public
|
|
11
11
|
*/
|
|
12
|
-
var InvalidPackageNameError = class extends Schema.
|
|
12
|
+
var InvalidPackageNameError = class extends Schema.TaggedError()("InvalidPackageNameError", {
|
|
13
13
|
/** The raw input string that failed validation. */
|
|
14
14
|
input: Schema.String }) {
|
|
15
15
|
get message() {
|
package/PackageValidator.js
CHANGED
|
@@ -9,7 +9,7 @@ import { Context, Effect, HashMap, Layer, Option, Result, Schema } from "effect"
|
|
|
9
9
|
*
|
|
10
10
|
* @public
|
|
11
11
|
*/
|
|
12
|
-
var PackageValidationError = class extends Schema.
|
|
12
|
+
var PackageValidationError = class extends Schema.TaggedError()("PackageValidationError", {
|
|
13
13
|
/** The aggregated rule failures. */
|
|
14
14
|
failures: Schema.Array(Schema.Struct({
|
|
15
15
|
rule: Schema.String,
|
package/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { JsoncEdit, JsoncEdit as JsoncEdit$1, JsoncPath, JsoncPath as JsoncPath$1 } from "@effected/jsonc";
|
|
1
2
|
import { CatalogResolver, DependencyKind, DependencyProtocol, DependencyProtocol as DependencyProtocol$1, DependencySpecifier, DependencySpecifierBrand, InvalidDependencySpecifierError, WorkspaceResolver, isValidDependencySpecifier } from "@effected/npm";
|
|
2
3
|
import { InvalidVersionError, Range, SemVer } from "@effected/semver";
|
|
3
4
|
import { Brand, Context, Effect, FileSystem, HashMap, Layer, Option, Path, Result, Schema } from "effect";
|
|
@@ -745,117 +746,6 @@ declare class Package extends Package_base {
|
|
|
745
746
|
toJsonString(options?: PackageFormatOptions): string;
|
|
746
747
|
}
|
|
747
748
|
//#endregion
|
|
748
|
-
//#region src/PackageJsonFile.d.ts
|
|
749
|
-
declare const PackageJsonReadError_base: Schema.Class<PackageJsonReadError, Schema.TaggedStruct<"PackageJsonReadError", {
|
|
750
|
-
/** The path that could not be read. */
|
|
751
|
-
readonly path: Schema.String;
|
|
752
|
-
/** The underlying failure, preserved structurally. */
|
|
753
|
-
readonly cause: Schema.Defect;
|
|
754
|
-
}>, import("effect/Cause").YieldableError>;
|
|
755
|
-
/**
|
|
756
|
-
* Indicates that a package.json file could not be read from the filesystem
|
|
757
|
-
* (a filesystem error other than not-found).
|
|
758
|
-
*
|
|
759
|
-
* @public
|
|
760
|
-
*/
|
|
761
|
-
declare class PackageJsonReadError extends PackageJsonReadError_base {
|
|
762
|
-
get message(): string;
|
|
763
|
-
}
|
|
764
|
-
declare const PackageJsonNotFoundError_base: Schema.Class<PackageJsonNotFoundError, Schema.TaggedStruct<"PackageJsonNotFoundError", {
|
|
765
|
-
/** The path where package.json was expected. */
|
|
766
|
-
readonly path: Schema.String;
|
|
767
|
-
}>, import("effect/Cause").YieldableError>;
|
|
768
|
-
/**
|
|
769
|
-
* Indicates that no package.json file exists at the expected path. Carries its
|
|
770
|
-
* own tag for `catchTag` routing.
|
|
771
|
-
*
|
|
772
|
-
* @public
|
|
773
|
-
*/
|
|
774
|
-
declare class PackageJsonNotFoundError extends PackageJsonNotFoundError_base {
|
|
775
|
-
get message(): string;
|
|
776
|
-
}
|
|
777
|
-
declare const PackageJsonParseError_base: Schema.Class<PackageJsonParseError, Schema.TaggedStruct<"PackageJsonParseError", {
|
|
778
|
-
/** The path whose contents failed to parse as JSON. */
|
|
779
|
-
readonly path: Schema.String;
|
|
780
|
-
/** The underlying `SyntaxError`, preserved structurally. */
|
|
781
|
-
readonly cause: Schema.Defect;
|
|
782
|
-
}>, import("effect/Cause").YieldableError>;
|
|
783
|
-
/**
|
|
784
|
-
* Indicates that a package.json file's contents are not valid JSON.
|
|
785
|
-
*
|
|
786
|
-
* @public
|
|
787
|
-
*/
|
|
788
|
-
declare class PackageJsonParseError extends PackageJsonParseError_base {
|
|
789
|
-
get message(): string;
|
|
790
|
-
}
|
|
791
|
-
declare const PackageJsonWriteError_base: Schema.Class<PackageJsonWriteError, Schema.TaggedStruct<"PackageJsonWriteError", {
|
|
792
|
-
/** The path that could not be written. */
|
|
793
|
-
readonly path: Schema.String;
|
|
794
|
-
/** The underlying filesystem failure, preserved structurally. Narrowed to the write failure only. */
|
|
795
|
-
readonly cause: Schema.Defect;
|
|
796
|
-
}>, import("effect/Cause").YieldableError>;
|
|
797
|
-
/**
|
|
798
|
-
* Indicates that a package.json file could not be written to the filesystem.
|
|
799
|
-
* Narrowed to the filesystem-write failure only — never a resolution or encode
|
|
800
|
-
* error.
|
|
801
|
-
*
|
|
802
|
-
* @public
|
|
803
|
-
*/
|
|
804
|
-
declare class PackageJsonWriteError extends PackageJsonWriteError_base {
|
|
805
|
-
get message(): string;
|
|
806
|
-
}
|
|
807
|
-
/**
|
|
808
|
-
* The shape of the {@link PackageJsonFile} service — the value produced by
|
|
809
|
-
* {@link PackageJsonFile.make} and carried by its layer.
|
|
810
|
-
*
|
|
811
|
-
* @public
|
|
812
|
-
*/
|
|
813
|
-
interface PackageJsonFileShape {
|
|
814
|
-
/**
|
|
815
|
-
* Read and decode a package.json file. Fails with `PackageJsonNotFoundError`
|
|
816
|
-
* (ENOENT), `PackageJsonReadError` (other fs errors), `PackageJsonParseError`
|
|
817
|
-
* (invalid JSON) or `PackageDecodeError` (schema decode).
|
|
818
|
-
*/
|
|
819
|
-
readonly read: (path: string) => Effect.Effect<Package, PackageJsonReadError | PackageJsonNotFoundError | PackageJsonParseError | PackageDecodeError>;
|
|
820
|
-
/**
|
|
821
|
-
* Serialize and write a package.json file. Fails with
|
|
822
|
-
* `PackageJsonWriteError`. With `indent: "preserve"` and no explicit
|
|
823
|
-
* `sourceText`, the existing file at `path` (when readable) supplies the
|
|
824
|
-
* source text whose indentation is preserved.
|
|
825
|
-
*/
|
|
826
|
-
readonly write: (path: string, pkg: Package, options?: PackageFormatOptions) => Effect.Effect<void, PackageJsonWriteError>;
|
|
827
|
-
}
|
|
828
|
-
declare const PackageJsonFile_base: Context.ServiceClass<PackageJsonFile, "@effected/package-json/PackageJsonFile", PackageJsonFileShape>;
|
|
829
|
-
/**
|
|
830
|
-
* Reads and writes package.json over core `FileSystem` / `Path`. The layer
|
|
831
|
-
* requires those services; provide `@effect/platform-node`'s `NodeFileSystem` /
|
|
832
|
-
* `NodePath` (or a bun equivalent) at the application boundary.
|
|
833
|
-
*
|
|
834
|
-
* @example
|
|
835
|
-
* ```ts
|
|
836
|
-
* import { PackageJsonFile } from "@effected/package-json";
|
|
837
|
-
* import { NodeFileSystem, NodePath } from "@effect/platform-node";
|
|
838
|
-
* import { Effect } from "effect";
|
|
839
|
-
*
|
|
840
|
-
* const program = Effect.gen(function* () {
|
|
841
|
-
* const files = yield* PackageJsonFile;
|
|
842
|
-
* const pkg = yield* files.read("./package.json");
|
|
843
|
-
* console.log(pkg.name);
|
|
844
|
-
* }).pipe(Effect.provide(PackageJsonFile.layer), Effect.provide(NodeFileSystem.layer), Effect.provide(NodePath.layer));
|
|
845
|
-
* ```
|
|
846
|
-
*
|
|
847
|
-
* @public
|
|
848
|
-
*/
|
|
849
|
-
declare class PackageJsonFile extends PackageJsonFile_base {
|
|
850
|
-
/** Build the service implementation from `FileSystem` / `Path` in context; use {@link PackageJsonFile.layer} to provide it. */
|
|
851
|
-
static readonly make: Effect.Effect<PackageJsonFileShape, never, FileSystem.FileSystem | Path.Path>;
|
|
852
|
-
/**
|
|
853
|
-
* The live layer. Requires core `FileSystem` / `Path`, provided by the
|
|
854
|
-
* consumer's platform implementation at the edge.
|
|
855
|
-
*/
|
|
856
|
-
static readonly layer: Layer.Layer<PackageJsonFile, never, FileSystem.FileSystem | Path.Path>;
|
|
857
|
-
}
|
|
858
|
-
//#endregion
|
|
859
749
|
//#region src/PackageJsonFormat.d.ts
|
|
860
750
|
declare const PackageJsonSyntaxError_base: Schema.Class<PackageJsonSyntaxError, Schema.TaggedStruct<"PackageJsonSyntaxError", {
|
|
861
751
|
/** Which syntactic precondition failed. */
|
|
@@ -907,6 +797,27 @@ interface PackageFormatTextOptions {
|
|
|
907
797
|
/** Append a trailing newline (default `true`). */
|
|
908
798
|
readonly newline?: boolean;
|
|
909
799
|
}
|
|
800
|
+
declare const PackageJsonModifyError_base: Schema.Class<PackageJsonModifyError, Schema.TaggedStruct<"PackageJsonModifyError", {
|
|
801
|
+
/** The field path whose navigation failed. */
|
|
802
|
+
readonly path: Schema.$Array<Schema.Union<readonly [Schema.String, Schema.Number]>>;
|
|
803
|
+
/** The underlying `JsoncModificationError`, preserved structurally. */
|
|
804
|
+
readonly cause: Schema.Defect;
|
|
805
|
+
}>, import("effect/Cause").YieldableError>;
|
|
806
|
+
/**
|
|
807
|
+
* Indicates that a surgical modification could not be applied: the value on
|
|
808
|
+
* the navigation path is not the container kind the next path segment
|
|
809
|
+
* requires. The underlying `@effected/jsonc` `JsoncModificationError` —
|
|
810
|
+
* which names the expected container and the 1-based depth of the mismatch —
|
|
811
|
+
* is preserved on the structured `cause` field, never stringified.
|
|
812
|
+
*
|
|
813
|
+
* Raised by {@link PackageJsonFormat.modify} and
|
|
814
|
+
* {@link PackageJsonFormat.modifyToString}.
|
|
815
|
+
*
|
|
816
|
+
* @public
|
|
817
|
+
*/
|
|
818
|
+
declare class PackageJsonModifyError extends PackageJsonModifyError_base {
|
|
819
|
+
get message(): string;
|
|
820
|
+
}
|
|
910
821
|
/**
|
|
911
822
|
* Decode-free canonical sort and format statics. Not instantiable.
|
|
912
823
|
*
|
|
@@ -996,6 +907,416 @@ declare class PackageJsonFormat {
|
|
|
996
907
|
* ```
|
|
997
908
|
*/
|
|
998
909
|
static formatToString(source: string, options?: PackageFormatTextOptions): Result.Result<string, PackageJsonSyntaxError>;
|
|
910
|
+
/**
|
|
911
|
+
* Compute the surgical edits that set, replace or delete the value at
|
|
912
|
+
* `path` **without decoding, sorting or reformatting anything else**. The
|
|
913
|
+
* opposite posture to {@link PackageJsonFormat.formatToString}: where the
|
|
914
|
+
* formatter's job is the canonical order, the mutator's job is to leave
|
|
915
|
+
* every untouched byte untouched — key order, indentation, line endings and
|
|
916
|
+
* the trailing newline all survive, because only the edited span changes.
|
|
917
|
+
* That is what makes the result reviewable when a tool commits a one-field
|
|
918
|
+
* change to someone else's repository.
|
|
919
|
+
*
|
|
920
|
+
* Built on `@effected/jsonc`'s scanner-based edit engine. Inserted content
|
|
921
|
+
* matches the source's own style: indentation (tab vs N spaces) is detected
|
|
922
|
+
* from the first indented line and the line ending from the first `\r\n`.
|
|
923
|
+
*
|
|
924
|
+
* Passing `value === undefined` deletes the target key (including its
|
|
925
|
+
* comma) — the `@effected/jsonc` / `@effected/yaml` modify convention. A
|
|
926
|
+
* missing insertion target appends after the last key of its container.
|
|
927
|
+
*
|
|
928
|
+
* @param source - the package.json file contents (strict JSON — npm does
|
|
929
|
+
* not accept comments, and neither does this)
|
|
930
|
+
* @param path - the field path, e.g. `["packageManager"]` or
|
|
931
|
+
* `["devEngines", "runtime", "version"]`
|
|
932
|
+
* @param value - the plain JSON value to write, or `undefined` to delete
|
|
933
|
+
* @returns the edits to apply via `JsoncEdit.applyAll` — or use
|
|
934
|
+
* {@link PackageJsonFormat.modifyToString} for the applied text in one step
|
|
935
|
+
*/
|
|
936
|
+
static readonly modify: (source: string, path: JsoncPath$1, value: unknown) => Effect.Effect<readonly JsoncEdit$1[], PackageJsonModifyError | PackageJsonSyntaxError, never>;
|
|
937
|
+
/**
|
|
938
|
+
* Modify `source` and apply the resulting edits in one step
|
|
939
|
+
* (`JsoncEdit.applyAll` composed over {@link PackageJsonFormat.modify}).
|
|
940
|
+
* Text in, text out; every byte outside the edited span is preserved.
|
|
941
|
+
* Inherits the modify error channel: {@link PackageJsonSyntaxError} when
|
|
942
|
+
* the source is not a JSON object, {@link PackageJsonModifyError} when the
|
|
943
|
+
* path cannot be navigated.
|
|
944
|
+
*
|
|
945
|
+
* @example
|
|
946
|
+
* ```ts
|
|
947
|
+
* import { PackageJsonFormat } from "@effected/package-json";
|
|
948
|
+
* import { Effect } from "effect";
|
|
949
|
+
*
|
|
950
|
+
* const program = PackageJsonFormat.modifyToString(
|
|
951
|
+
* '{\n "private": true,\n "packageManager": "pnpm@11.2.0"\n}\n',
|
|
952
|
+
* ["packageManager"],
|
|
953
|
+
* "pnpm@11.3.0",
|
|
954
|
+
* ); // only the packageManager value changes; every other byte survives
|
|
955
|
+
* ```
|
|
956
|
+
*/
|
|
957
|
+
static readonly modifyToString: (source: string, path: JsoncPath$1, value: unknown) => Effect.Effect<string, PackageJsonModifyError | PackageJsonSyntaxError, never>;
|
|
958
|
+
}
|
|
959
|
+
//#endregion
|
|
960
|
+
//#region src/PackageManagerRange.d.ts
|
|
961
|
+
declare const PackageManagerRange_base: Schema.Class<PackageManagerRange, Schema.Struct<{
|
|
962
|
+
/** The package-manager name (e.g. `pnpm`). Any lowercase name — the same latitude as {@link PackageManager}, for the same evidence. */
|
|
963
|
+
readonly name: Schema.String;
|
|
964
|
+
/**
|
|
965
|
+
* The version position, verbatim: a semver range (`^11.20.0`,
|
|
966
|
+
* `>=10 <12`, ...) or an exact version (`11.2.0`). Validated to parse
|
|
967
|
+
* through `@effected/semver`'s `Range.parseResult`; never normalized, so
|
|
968
|
+
* the field round-trips byte-identically.
|
|
969
|
+
*/
|
|
970
|
+
readonly range: Schema.String;
|
|
971
|
+
/**
|
|
972
|
+
* The optional integrity hash (e.g. `sha512.abc`): `@effected/npm`'s
|
|
973
|
+
* `CorepackIntegrityHash`. Meaningful only alongside an exact range —
|
|
974
|
+
* an integrity pins one artifact — but carried whenever the manifest
|
|
975
|
+
* carries it, because fidelity outranks plausibility in a field model.
|
|
976
|
+
*/
|
|
977
|
+
readonly integrity: Schema.Option<Schema.brand<Schema.String, "IntegrityHash">>;
|
|
978
|
+
}>, {}>;
|
|
979
|
+
/**
|
|
980
|
+
* A structured `packageManager` value whose version position is a semver
|
|
981
|
+
* **range**, carried verbatim: `name`, `range` and an optional `integrity`
|
|
982
|
+
* hash.
|
|
983
|
+
*
|
|
984
|
+
* @remarks
|
|
985
|
+
* The range-tolerant sibling of {@link PackageManager}. The strict class
|
|
986
|
+
* models the corepack pin — an exact version, which is all corepack itself
|
|
987
|
+
* accepts — and stays strict; this class models the field as pnpm reads it,
|
|
988
|
+
* where a range such as `^11.20.0` is a supported spelling that pnpm resolves
|
|
989
|
+
* to a concrete version. An exact version is a valid range, so every string
|
|
990
|
+
* the strict codec accepts decodes here too; {@link PackageManagerRange.isExact}
|
|
991
|
+
* is how a caller tracks which form the manifest actually carried.
|
|
992
|
+
*
|
|
993
|
+
* The `range` field is the manifest's text **verbatim** — validated to parse
|
|
994
|
+
* as a semver range but never normalized, so encoding is byte-identical to
|
|
995
|
+
* the accepted input and reading a manifest never rewrites the field.
|
|
996
|
+
* Interpretation is a derived getter, following `Repository`'s
|
|
997
|
+
* carry-verbatim posture.
|
|
998
|
+
*
|
|
999
|
+
* The first `+` after the `@` begins the integrity component, exactly as in
|
|
1000
|
+
* the strict grammar — the version position of this field never carries
|
|
1001
|
+
* semver build metadata.
|
|
1002
|
+
*
|
|
1003
|
+
* @example
|
|
1004
|
+
* ```ts
|
|
1005
|
+
* import { PackageManagerRange } from "@effected/package-json";
|
|
1006
|
+
* import { Effect, Schema } from "effect";
|
|
1007
|
+
*
|
|
1008
|
+
* const program = Effect.gen(function* () {
|
|
1009
|
+
* const pm = yield* Schema.decodeUnknownEffect(PackageManagerRange.FromString)("pnpm@^11.20.0");
|
|
1010
|
+
* console.log(pm.name, pm.range, pm.isExact); // "pnpm" "^11.20.0" false
|
|
1011
|
+
* });
|
|
1012
|
+
* ```
|
|
1013
|
+
*
|
|
1014
|
+
* @public
|
|
1015
|
+
*/
|
|
1016
|
+
declare class PackageManagerRange extends PackageManagerRange_base {
|
|
1017
|
+
/**
|
|
1018
|
+
* Schema transformation between the `"name@range[+integrity]"` string and a
|
|
1019
|
+
* {@link PackageManagerRange}.
|
|
1020
|
+
*
|
|
1021
|
+
* @remarks
|
|
1022
|
+
* Decoding splits on the first `@`, then on the first `+` — which always
|
|
1023
|
+
* begins the integrity, never semver build metadata — and validates each
|
|
1024
|
+
* component: the name against the lowercase grammar, the range through
|
|
1025
|
+
* `@effected/semver`'s `Range.parseResult`, the integrity through
|
|
1026
|
+
* `CorepackIntegrityHash`. Every failure is a typed decode failure naming
|
|
1027
|
+
* the component that failed. Encoding reconstructs the string from the
|
|
1028
|
+
* verbatim parts, so it is byte-identical to any input this codec accepts.
|
|
1029
|
+
*/
|
|
1030
|
+
static readonly FromString: Schema.Codec<PackageManagerRange, string>;
|
|
1031
|
+
/**
|
|
1032
|
+
* Whether the range is an exact, pinnable version (`11.2.0`) rather than a
|
|
1033
|
+
* genuine range (`^11.20.0`) — decided by `SemVer.isPinnable` over the
|
|
1034
|
+
* verbatim text, so `=11.2.0` and other range spellings of a single
|
|
1035
|
+
* version report `false`. This is the exactness a consumer tracks when it
|
|
1036
|
+
* must re-emit the same spelling it read.
|
|
1037
|
+
*/
|
|
1038
|
+
get isExact(): boolean;
|
|
1039
|
+
/** Whether an integrity hash is present. */
|
|
1040
|
+
get hasIntegrity(): boolean;
|
|
1041
|
+
}
|
|
1042
|
+
//#endregion
|
|
1043
|
+
//#region src/PackageManifest.d.ts
|
|
1044
|
+
declare const PackageManifest_base: Schema.Class<PackageManifest, Schema.Struct<{
|
|
1045
|
+
readonly description: Schema.optionalKey<Schema.String>;
|
|
1046
|
+
readonly private: Schema.optionalKey<Schema.Boolean>;
|
|
1047
|
+
readonly type: Schema.optionalKey<Schema.Literals<readonly ["module", "commonjs"]>>;
|
|
1048
|
+
readonly main: Schema.optionalKey<Schema.String>;
|
|
1049
|
+
readonly license: Schema.optionalKey<Schema.brand<Schema.String, "SpdxLicense">>;
|
|
1050
|
+
readonly author: Schema.optionalKey<Schema.Codec<Person, string | {
|
|
1051
|
+
readonly [k: string]: unknown;
|
|
1052
|
+
}, never, never>>;
|
|
1053
|
+
readonly contributors: Schema.optionalKey<Schema.$Array<Schema.Codec<Person, string | {
|
|
1054
|
+
readonly [k: string]: unknown;
|
|
1055
|
+
}, never, never>>>;
|
|
1056
|
+
readonly maintainers: Schema.optionalKey<Schema.$Array<Schema.Codec<Person, string | {
|
|
1057
|
+
readonly [k: string]: unknown;
|
|
1058
|
+
}, never, never>>>;
|
|
1059
|
+
readonly keywords: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
1060
|
+
readonly repository: Schema.optionalKey<Schema.Codec<Repository, string | {
|
|
1061
|
+
readonly [k: string]: unknown;
|
|
1062
|
+
}, never, never>>;
|
|
1063
|
+
readonly bugs: Schema.optionalKey<Schema.Codec<Bugs, string | {
|
|
1064
|
+
readonly [k: string]: unknown;
|
|
1065
|
+
}, never, never>>;
|
|
1066
|
+
readonly homepage: Schema.optionalKey<Schema.String>;
|
|
1067
|
+
readonly dependencies: Schema.decodeTo<Schema.HashMap<Schema.String, Schema.String>, Schema.withDecodingDefaultKey<Schema.$Record<Schema.String, Schema.String>, never>, never, never>;
|
|
1068
|
+
readonly devDependencies: Schema.decodeTo<Schema.HashMap<Schema.String, Schema.String>, Schema.withDecodingDefaultKey<Schema.$Record<Schema.String, Schema.String>, never>, never, never>;
|
|
1069
|
+
readonly peerDependencies: Schema.decodeTo<Schema.HashMap<Schema.String, Schema.String>, Schema.withDecodingDefaultKey<Schema.$Record<Schema.String, Schema.String>, never>, never, never>;
|
|
1070
|
+
readonly optionalDependencies: Schema.decodeTo<Schema.HashMap<Schema.String, Schema.String>, Schema.withDecodingDefaultKey<Schema.$Record<Schema.String, Schema.String>, never>, never, never>;
|
|
1071
|
+
readonly peerDependenciesMeta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Struct<{
|
|
1072
|
+
readonly optional: Schema.optionalKey<Schema.Boolean>;
|
|
1073
|
+
}>>>;
|
|
1074
|
+
readonly scripts: Schema.decodeTo<Schema.HashMap<Schema.String, Schema.String>, Schema.withDecodingDefaultKey<Schema.$Record<Schema.String, Schema.String>, never>, never, never>;
|
|
1075
|
+
readonly bin: Schema.optionalKey<Schema.Union<readonly [Schema.String, Schema.decodeTo<Schema.HashMap<Schema.String, Schema.String>, Schema.$Record<Schema.String, Schema.String>, never, never>]>>;
|
|
1076
|
+
readonly engines: Schema.optionalKey<Schema.decodeTo<Schema.HashMap<Schema.String, Schema.String>, Schema.$Record<Schema.String, Schema.String>, never, never>>;
|
|
1077
|
+
readonly exports: Schema.optionalKey<Schema.Union<readonly [Schema.String, Schema.$Record<Schema.String, Schema.Unknown>]>>;
|
|
1078
|
+
readonly publishConfig: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
1079
|
+
readonly devEngines: Schema.optionalKey<Schema.Struct<{
|
|
1080
|
+
readonly packageManager: Schema.optionalKey<typeof DevEngineOrArray>;
|
|
1081
|
+
readonly runtime: Schema.optionalKey<typeof DevEngineOrArray>;
|
|
1082
|
+
readonly os: Schema.optionalKey<typeof DevEngineOrArray>;
|
|
1083
|
+
readonly cpu: Schema.optionalKey<typeof DevEngineOrArray>;
|
|
1084
|
+
readonly libc: Schema.optionalKey<typeof DevEngineOrArray>;
|
|
1085
|
+
}>>;
|
|
1086
|
+
readonly rest: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
1087
|
+
readonly name: Schema.optionalKey<Schema.Union<readonly [Schema.brand<Schema.String, "ScopedPackageName">, Schema.brand<Schema.String, "UnscopedPackageName">]> & {
|
|
1088
|
+
isValid: (name: string) => boolean;
|
|
1089
|
+
scope: (name: string) => import("effect/Option").Option<string>;
|
|
1090
|
+
unscoped: (name: string) => string;
|
|
1091
|
+
isScoped: (name: string) => boolean;
|
|
1092
|
+
}>;
|
|
1093
|
+
readonly version: Schema.optionalKey<Schema.Codec<SemVer, string, never, never>>;
|
|
1094
|
+
readonly packageManager: Schema.optionalKey<Schema.Codec<PackageManagerRange, string, never, never>>;
|
|
1095
|
+
}>, {}>;
|
|
1096
|
+
/**
|
|
1097
|
+
* A package.json document as it exists on disk, publishable or not: every
|
|
1098
|
+
* field of {@link Package} with `name` and `version` optional and
|
|
1099
|
+
* `packageManager` accepting the range spelling.
|
|
1100
|
+
*
|
|
1101
|
+
* @remarks
|
|
1102
|
+
* **Lenient about absence, strict about shape.** npm requires `name` and
|
|
1103
|
+
* `version` only for a package that will be published; the idiomatic private
|
|
1104
|
+
* workspace root (`{ "private": true, "packageManager": "pnpm@11.2.0" }`)
|
|
1105
|
+
* carries neither, and {@link Package.decode} rightly rejects it — the strict
|
|
1106
|
+
* model's contract is publishability. This class decodes that shape, and any
|
|
1107
|
+
* other manifest, so long as every field that IS present satisfies its typed
|
|
1108
|
+
* codec: a present `version` must still be strict semver (`"1.0"` fails
|
|
1109
|
+
* typed), a present `name` must still satisfy the npm grammar, a present
|
|
1110
|
+
* `packageManager` must still parse — though here the version position may be
|
|
1111
|
+
* a semver range (`pnpm@^11.20.0`), decoded as {@link PackageManagerRange}.
|
|
1112
|
+
* For total tolerance of malformed fields, use the decode-free
|
|
1113
|
+
* {@link PackageJsonFormat} text path (or `@effected/npm`'s shape-blind
|
|
1114
|
+
* `Manifest`) — silently carrying a value the type claims to have validated
|
|
1115
|
+
* would be a lie, and silently dropping it would break round-trip fidelity.
|
|
1116
|
+
*
|
|
1117
|
+
* The model is deliberately lean — fields, the `rest` catch-all wire codec,
|
|
1118
|
+
* {@link PackageManifest.decode} and {@link PackageManifest.toJsonString} —
|
|
1119
|
+
* because the write half of a manifest-editing tool is the surgical
|
|
1120
|
+
* {@link PackageJsonFormat.modifyToString} / `PackageJsonFile.modify` path,
|
|
1121
|
+
* which never goes through a model at all. Mutation statics live on the
|
|
1122
|
+
* strict {@link Package}.
|
|
1123
|
+
*
|
|
1124
|
+
* @example
|
|
1125
|
+
* ```ts
|
|
1126
|
+
* import { PackageManifest } from "@effected/package-json";
|
|
1127
|
+
* import { Effect, Option } from "effect";
|
|
1128
|
+
*
|
|
1129
|
+
* const program = Effect.gen(function* () {
|
|
1130
|
+
* const root = yield* PackageManifest.decode({ private: true, packageManager: "pnpm@^11.20.0" });
|
|
1131
|
+
* console.log(root.isPrivate, root.packageManager?.isExact); // true false
|
|
1132
|
+
* });
|
|
1133
|
+
* ```
|
|
1134
|
+
*
|
|
1135
|
+
* @public
|
|
1136
|
+
*/
|
|
1137
|
+
declare class PackageManifest extends PackageManifest_base {
|
|
1138
|
+
/**
|
|
1139
|
+
* The wire codec: an open JSON object ↔ a {@link PackageManifest} instance,
|
|
1140
|
+
* partitioning unknown keys into `rest` and flattening them back on encode —
|
|
1141
|
+
* the same transform {@link Package.schema} uses, over this class's fields.
|
|
1142
|
+
*/
|
|
1143
|
+
static readonly schema: Schema.Codec<PackageManifest, {
|
|
1144
|
+
readonly [k: string]: unknown;
|
|
1145
|
+
}>;
|
|
1146
|
+
/**
|
|
1147
|
+
* Decode an unknown JSON value into a {@link PackageManifest}, normalizing
|
|
1148
|
+
* any `SchemaError` to a typed {@link PackageDecodeError} at the boundary.
|
|
1149
|
+
*
|
|
1150
|
+
* @param input - the parsed package.json JSON value (e.g. from `JSON.parse`)
|
|
1151
|
+
* @returns an Effect resolving to the decoded `PackageManifest`
|
|
1152
|
+
* @throws (typed) `PackageDecodeError` when a present field does not satisfy
|
|
1153
|
+
* its codec
|
|
1154
|
+
*/
|
|
1155
|
+
static readonly decode: (input: unknown) => Effect.Effect<PackageManifest, PackageDecodeError, never>;
|
|
1156
|
+
/** Whether the manifest is marked private. */
|
|
1157
|
+
get isPrivate(): boolean;
|
|
1158
|
+
/**
|
|
1159
|
+
* Serialize to a formatted package.json string: encode through the wire
|
|
1160
|
+
* codec (flattening `rest`), then apply the canonical key order, dependency
|
|
1161
|
+
* sorting and empty-map stripping unless the options opt out. Pure, and
|
|
1162
|
+
* shared with `Package.toJsonString` down to the same internal renderer.
|
|
1163
|
+
* Absent `name` / `version` keys stay absent — nothing is invented.
|
|
1164
|
+
*/
|
|
1165
|
+
toJsonString(options?: PackageFormatOptions): string;
|
|
1166
|
+
}
|
|
1167
|
+
//#endregion
|
|
1168
|
+
//#region src/PackageJsonFile.d.ts
|
|
1169
|
+
declare const PackageJsonReadError_base: Schema.Class<PackageJsonReadError, Schema.TaggedStruct<"PackageJsonReadError", {
|
|
1170
|
+
/** The path that could not be read. */
|
|
1171
|
+
readonly path: Schema.String;
|
|
1172
|
+
/** The underlying failure, preserved structurally. */
|
|
1173
|
+
readonly cause: Schema.Defect;
|
|
1174
|
+
}>, import("effect/Cause").YieldableError>;
|
|
1175
|
+
/**
|
|
1176
|
+
* Indicates that a package.json file could not be read from the filesystem
|
|
1177
|
+
* (a filesystem error other than not-found).
|
|
1178
|
+
*
|
|
1179
|
+
* @public
|
|
1180
|
+
*/
|
|
1181
|
+
declare class PackageJsonReadError extends PackageJsonReadError_base {
|
|
1182
|
+
get message(): string;
|
|
1183
|
+
}
|
|
1184
|
+
declare const PackageJsonNotFoundError_base: Schema.Class<PackageJsonNotFoundError, Schema.TaggedStruct<"PackageJsonNotFoundError", {
|
|
1185
|
+
/** The path where package.json was expected. */
|
|
1186
|
+
readonly path: Schema.String;
|
|
1187
|
+
}>, import("effect/Cause").YieldableError>;
|
|
1188
|
+
/**
|
|
1189
|
+
* Indicates that no package.json file exists at the expected path. Carries its
|
|
1190
|
+
* own tag for `catchTag` routing.
|
|
1191
|
+
*
|
|
1192
|
+
* @public
|
|
1193
|
+
*/
|
|
1194
|
+
declare class PackageJsonNotFoundError extends PackageJsonNotFoundError_base {
|
|
1195
|
+
get message(): string;
|
|
1196
|
+
}
|
|
1197
|
+
declare const PackageJsonParseError_base: Schema.Class<PackageJsonParseError, Schema.TaggedStruct<"PackageJsonParseError", {
|
|
1198
|
+
/** The path whose contents failed to parse as JSON. */
|
|
1199
|
+
readonly path: Schema.String;
|
|
1200
|
+
/** The underlying `SyntaxError`, preserved structurally. */
|
|
1201
|
+
readonly cause: Schema.Defect;
|
|
1202
|
+
}>, import("effect/Cause").YieldableError>;
|
|
1203
|
+
/**
|
|
1204
|
+
* Indicates that a package.json file's contents are not valid JSON.
|
|
1205
|
+
*
|
|
1206
|
+
* @public
|
|
1207
|
+
*/
|
|
1208
|
+
declare class PackageJsonParseError extends PackageJsonParseError_base {
|
|
1209
|
+
get message(): string;
|
|
1210
|
+
}
|
|
1211
|
+
declare const PackageJsonWriteError_base: Schema.Class<PackageJsonWriteError, Schema.TaggedStruct<"PackageJsonWriteError", {
|
|
1212
|
+
/** The path that could not be written. */
|
|
1213
|
+
readonly path: Schema.String;
|
|
1214
|
+
/** The underlying filesystem failure, preserved structurally. Narrowed to the write failure only. */
|
|
1215
|
+
readonly cause: Schema.Defect;
|
|
1216
|
+
}>, import("effect/Cause").YieldableError>;
|
|
1217
|
+
/**
|
|
1218
|
+
* Indicates that a package.json file could not be written to the filesystem.
|
|
1219
|
+
* Narrowed to the filesystem-write failure only — never a resolution or encode
|
|
1220
|
+
* error.
|
|
1221
|
+
*
|
|
1222
|
+
* @public
|
|
1223
|
+
*/
|
|
1224
|
+
declare class PackageJsonWriteError extends PackageJsonWriteError_base {
|
|
1225
|
+
get message(): string;
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* One surgical field edit for {@link PackageJsonFile}'s `modify`: set `value`
|
|
1229
|
+
* at `path`, or delete the key there when `value` is `undefined` (the
|
|
1230
|
+
* `@effected/jsonc` / `@effected/yaml` modify convention — deletion is spelled
|
|
1231
|
+
* with an explicit `value: undefined`, so it is always deliberate).
|
|
1232
|
+
*
|
|
1233
|
+
* @public
|
|
1234
|
+
*/
|
|
1235
|
+
interface PackageFieldEdit {
|
|
1236
|
+
/** The field path, e.g. `["packageManager"]` or `["devEngines", "runtime", "version"]`. */
|
|
1237
|
+
readonly path: JsoncPath$1;
|
|
1238
|
+
/** The plain JSON value to write, or `undefined` to delete the target key. */
|
|
1239
|
+
readonly value: unknown;
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* The shape of the {@link PackageJsonFile} service — the value produced by
|
|
1243
|
+
* {@link PackageJsonFile.make} and carried by its layer.
|
|
1244
|
+
*
|
|
1245
|
+
* @public
|
|
1246
|
+
*/
|
|
1247
|
+
interface PackageJsonFileShape {
|
|
1248
|
+
/**
|
|
1249
|
+
* Read and decode a package.json file. Fails with `PackageJsonNotFoundError`
|
|
1250
|
+
* (ENOENT), `PackageJsonReadError` (other fs errors), `PackageJsonParseError`
|
|
1251
|
+
* (invalid JSON) or `PackageDecodeError` (schema decode).
|
|
1252
|
+
*/
|
|
1253
|
+
readonly read: (path: string) => Effect.Effect<Package, PackageJsonReadError | PackageJsonNotFoundError | PackageJsonParseError | PackageDecodeError>;
|
|
1254
|
+
/**
|
|
1255
|
+
* Serialize and write a package.json file. Fails with
|
|
1256
|
+
* `PackageJsonWriteError`. With `indent: "preserve"` and no explicit
|
|
1257
|
+
* `sourceText`, the existing file at `path` (when readable) supplies the
|
|
1258
|
+
* source text whose indentation is preserved.
|
|
1259
|
+
*/
|
|
1260
|
+
readonly write: (path: string, pkg: Package, options?: PackageFormatOptions) => Effect.Effect<void, PackageJsonWriteError>;
|
|
1261
|
+
/**
|
|
1262
|
+
* Read and decode a package.json file through the presence-lenient
|
|
1263
|
+
* {@link PackageManifest} — the read that accepts the private
|
|
1264
|
+
* workspace-root shape (`{ "private": true, "packageManager": ... }`)
|
|
1265
|
+
* `read` rejects. Same error channel as `read`; a present field that does
|
|
1266
|
+
* not satisfy its codec still fails as `PackageDecodeError`.
|
|
1267
|
+
*/
|
|
1268
|
+
readonly readManifest: (path: string) => Effect.Effect<PackageManifest, PackageJsonReadError | PackageJsonNotFoundError | PackageJsonParseError | PackageDecodeError>;
|
|
1269
|
+
/**
|
|
1270
|
+
* Serialize and write a {@link PackageManifest}. Fails with
|
|
1271
|
+
* `PackageJsonWriteError`. Shares `write`'s `indent: "preserve"` behavior:
|
|
1272
|
+
* with no explicit `sourceText`, the existing file at `path` (when
|
|
1273
|
+
* readable) supplies the source text whose indentation is preserved.
|
|
1274
|
+
*/
|
|
1275
|
+
readonly writeManifest: (path: string, manifest: PackageManifest, options?: PackageFormatOptions) => Effect.Effect<void, PackageJsonWriteError>;
|
|
1276
|
+
/**
|
|
1277
|
+
* Apply surgical field edits to a package.json file **without decoding
|
|
1278
|
+
* it**: one read, each {@link PackageFieldEdit} applied in order through
|
|
1279
|
+
* `PackageJsonFormat.modifyToString`, one write — skipped when the result
|
|
1280
|
+
* is byte-identical to what was read. Every byte outside the edited spans
|
|
1281
|
+
* is preserved (key order, indentation, line endings, trailing newline),
|
|
1282
|
+
* which is what keeps a one-field change reviewable in someone else's
|
|
1283
|
+
* repository. Succeeds with the file's final text.
|
|
1284
|
+
*
|
|
1285
|
+
* Invalid JSON at `path` fails as `PackageJsonParseError` — the same tag
|
|
1286
|
+
* `read` uses for it — and an unnavigable edit path as
|
|
1287
|
+
* `PackageJsonModifyError`.
|
|
1288
|
+
*/
|
|
1289
|
+
readonly modify: (path: string, edits: ReadonlyArray<PackageFieldEdit>) => Effect.Effect<string, PackageJsonReadError | PackageJsonNotFoundError | PackageJsonParseError | PackageJsonModifyError | PackageJsonWriteError>;
|
|
1290
|
+
}
|
|
1291
|
+
declare const PackageJsonFile_base: Context.ServiceClass<PackageJsonFile, "@effected/package-json/PackageJsonFile", PackageJsonFileShape>;
|
|
1292
|
+
/**
|
|
1293
|
+
* Reads and writes package.json over core `FileSystem` / `Path`. The layer
|
|
1294
|
+
* requires those services; provide `@effect/platform-node`'s `NodeFileSystem` /
|
|
1295
|
+
* `NodePath` (or a bun equivalent) at the application boundary.
|
|
1296
|
+
*
|
|
1297
|
+
* @example
|
|
1298
|
+
* ```ts
|
|
1299
|
+
* import { PackageJsonFile } from "@effected/package-json";
|
|
1300
|
+
* import { NodeFileSystem, NodePath } from "@effect/platform-node";
|
|
1301
|
+
* import { Effect } from "effect";
|
|
1302
|
+
*
|
|
1303
|
+
* const program = Effect.gen(function* () {
|
|
1304
|
+
* const files = yield* PackageJsonFile;
|
|
1305
|
+
* const pkg = yield* files.read("./package.json");
|
|
1306
|
+
* console.log(pkg.name);
|
|
1307
|
+
* }).pipe(Effect.provide(PackageJsonFile.layer), Effect.provide(NodeFileSystem.layer), Effect.provide(NodePath.layer));
|
|
1308
|
+
* ```
|
|
1309
|
+
*
|
|
1310
|
+
* @public
|
|
1311
|
+
*/
|
|
1312
|
+
declare class PackageJsonFile extends PackageJsonFile_base {
|
|
1313
|
+
/** Build the service implementation from `FileSystem` / `Path` in context; use {@link PackageJsonFile.layer} to provide it. */
|
|
1314
|
+
static readonly make: Effect.Effect<PackageJsonFileShape, never, FileSystem.FileSystem | Path.Path>;
|
|
1315
|
+
/**
|
|
1316
|
+
* The live layer. Requires core `FileSystem` / `Path`, provided by the
|
|
1317
|
+
* consumer's platform implementation at the edge.
|
|
1318
|
+
*/
|
|
1319
|
+
static readonly layer: Layer.Layer<PackageJsonFile, never, FileSystem.FileSystem | Path.Path>;
|
|
999
1320
|
}
|
|
1000
1321
|
//#endregion
|
|
1001
1322
|
//#region src/PackageValidator.d.ts
|
|
@@ -1097,5 +1418,5 @@ declare class PackageValidator extends PackageValidator_base {
|
|
|
1097
1418
|
}): Layer.Layer<PackageValidator>;
|
|
1098
1419
|
}
|
|
1099
1420
|
//#endregion
|
|
1100
|
-
export { BinField, Bugs, Dependency, type DependencyKind, DependencyMapField, type DependencyProtocol, DependencySpecifier, type DependencySpecifierBrand, DevEngine, DevEngineOrArray, type DevEngines, DevEnginesSchema, ExportsField, InvalidDependencySpecifierError, InvalidPackageNameError, InvalidSpdxLicenseError, Package, PackageDecodeError, type PackageFormatOptions, type PackageFormatTextOptions, type PackageIndent, PackageJsonFile, type PackageJsonFileShape, PackageJsonFormat, PackageJsonNotFoundError, PackageJsonParseError, PackageJsonReadError, PackageJsonSyntaxError, PackageJsonWriteError, PackageManager, PackageName, type PackagePatch, PackageValidationError, PackageValidator, PeerDependenciesMetaField, Person, PublishConfigField, Repository, RepositoryField, type RuleFailure, ScopedPackageName, SpdxLicense, StringMapField, type UnresolvedDependency, UnscopedPackageName, type ValidationRule, defaultRules, isUnresolvedDependency, isValidDependencySpecifier, isValidSpdx, noLocalDepsRule, noUnresolvedDepsRule };
|
|
1421
|
+
export { BinField, Bugs, Dependency, type DependencyKind, DependencyMapField, type DependencyProtocol, DependencySpecifier, type DependencySpecifierBrand, DevEngine, DevEngineOrArray, type DevEngines, DevEnginesSchema, ExportsField, InvalidDependencySpecifierError, InvalidPackageNameError, InvalidSpdxLicenseError, JsoncEdit, type JsoncPath, Package, PackageDecodeError, type PackageFieldEdit, type PackageFormatOptions, type PackageFormatTextOptions, type PackageIndent, PackageJsonFile, type PackageJsonFileShape, PackageJsonFormat, PackageJsonModifyError, PackageJsonNotFoundError, PackageJsonParseError, PackageJsonReadError, PackageJsonSyntaxError, PackageJsonWriteError, PackageManager, PackageManagerRange, PackageManifest, PackageName, type PackagePatch, PackageValidationError, PackageValidator, PeerDependenciesMetaField, Person, PublishConfigField, Repository, RepositoryField, type RuleFailure, ScopedPackageName, SpdxLicense, StringMapField, type UnresolvedDependency, UnscopedPackageName, type ValidationRule, defaultRules, isUnresolvedDependency, isValidDependencySpecifier, isValidSpdx, noLocalDepsRule, noUnresolvedDepsRule };
|
|
1101
1422
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -6,9 +6,12 @@ import { InvalidPackageNameError, PackageName, ScopedPackageName, UnscopedPackag
|
|
|
6
6
|
import { Person } from "./Person.js";
|
|
7
7
|
import { Bugs, Repository } from "./Repository.js";
|
|
8
8
|
import { BinField, DependencyMapField, ExportsField, Package, PackageDecodeError, PeerDependenciesMetaField, PublishConfigField, RepositoryField, StringMapField } from "./Package.js";
|
|
9
|
+
import { PackageJsonFormat, PackageJsonModifyError, PackageJsonSyntaxError } from "./PackageJsonFormat.js";
|
|
10
|
+
import { PackageManagerRange } from "./PackageManagerRange.js";
|
|
11
|
+
import { PackageManifest } from "./PackageManifest.js";
|
|
9
12
|
import { PackageJsonFile, PackageJsonNotFoundError, PackageJsonParseError, PackageJsonReadError, PackageJsonWriteError } from "./PackageJsonFile.js";
|
|
10
|
-
import { PackageJsonFormat, PackageJsonSyntaxError } from "./PackageJsonFormat.js";
|
|
11
13
|
import { PackageValidationError, PackageValidator, defaultRules, noLocalDepsRule, noUnresolvedDepsRule } from "./PackageValidator.js";
|
|
14
|
+
import { JsoncEdit } from "@effected/jsonc";
|
|
12
15
|
import { DependencySpecifier, InvalidDependencySpecifierError, isValidDependencySpecifier } from "@effected/npm";
|
|
13
16
|
|
|
14
|
-
export { BinField, Bugs, Dependency, DependencyMapField, DependencySpecifier, DevEngine, DevEngineOrArray, DevEnginesSchema, ExportsField, InvalidDependencySpecifierError, InvalidPackageNameError, InvalidSpdxLicenseError, Package, PackageDecodeError, PackageJsonFile, PackageJsonFormat, PackageJsonNotFoundError, PackageJsonParseError, PackageJsonReadError, PackageJsonSyntaxError, PackageJsonWriteError, PackageManager, PackageName, PackageValidationError, PackageValidator, PeerDependenciesMetaField, Person, PublishConfigField, Repository, RepositoryField, ScopedPackageName, SpdxLicense, StringMapField, UnscopedPackageName, defaultRules, isUnresolvedDependency, isValidDependencySpecifier, isValidSpdx, noLocalDepsRule, noUnresolvedDepsRule };
|
|
17
|
+
export { BinField, Bugs, Dependency, DependencyMapField, DependencySpecifier, DevEngine, DevEngineOrArray, DevEnginesSchema, ExportsField, InvalidDependencySpecifierError, InvalidPackageNameError, InvalidSpdxLicenseError, JsoncEdit, Package, PackageDecodeError, PackageJsonFile, PackageJsonFormat, PackageJsonModifyError, PackageJsonNotFoundError, PackageJsonParseError, PackageJsonReadError, PackageJsonSyntaxError, PackageJsonWriteError, PackageManager, PackageManagerRange, PackageManifest, PackageName, PackageValidationError, PackageValidator, PeerDependenciesMetaField, Person, PublishConfigField, Repository, RepositoryField, ScopedPackageName, SpdxLicense, StringMapField, UnscopedPackageName, defaultRules, isUnresolvedDependency, isValidDependencySpecifier, isValidSpdx, noLocalDepsRule, noUnresolvedDepsRule };
|
package/internal/format.js
CHANGED
|
@@ -210,6 +210,18 @@ const resolveIndent = (indent, sourceText) => {
|
|
|
210
210
|
return indent ?? DEFAULT_INDENT;
|
|
211
211
|
};
|
|
212
212
|
/**
|
|
213
|
+
* Resolve the public `PackageFormatOptions` bag (mirrored structurally here —
|
|
214
|
+
* this module cannot import `Package.ts` without closing a cycle) to the
|
|
215
|
+
* concrete {@link renderJson} options. Shared by `Package.toJsonString` and
|
|
216
|
+
* `PackageManifest.toJsonString` so the two serializers cannot drift.
|
|
217
|
+
*/
|
|
218
|
+
const resolveFormatOptions = (options) => ({
|
|
219
|
+
indent: resolveIndent(options?.indent, options?.sourceText),
|
|
220
|
+
sort: options?.sort ?? true,
|
|
221
|
+
stripEmpty: options?.stripEmpty ?? true,
|
|
222
|
+
newline: options?.newline ?? true
|
|
223
|
+
});
|
|
224
|
+
/**
|
|
213
225
|
* Render an already-encoded package.json record to a JSON string, applying the
|
|
214
226
|
* empty-map strip, canonical key ordering and a trailing newline unless the
|
|
215
227
|
* corresponding options opt out.
|
|
@@ -222,4 +234,4 @@ const renderJson = (raw, options) => {
|
|
|
222
234
|
};
|
|
223
235
|
|
|
224
236
|
//#endregion
|
|
225
|
-
export { detectIndent, renderJson, resolveIndent, sortKeys, stripEmptyDependencyMaps };
|
|
237
|
+
export { detectIndent, renderJson, resolveFormatOptions, resolveIndent, sortKeys, stripEmptyDependencyMaps };
|
package/internal/wire.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Schema, SchemaTransformation } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/internal/wire.ts
|
|
4
|
+
const RawJson = Schema.Record(Schema.String, Schema.Unknown);
|
|
5
|
+
/**
|
|
6
|
+
* Build the open-JSON ↔ class wire codec for a `Schema.Class` carrying a
|
|
7
|
+
* `rest` catch-all field. Generic over the class so `Package`, its
|
|
8
|
+
* `.extend()`ed subclasses and `PackageManifest` all share the one
|
|
9
|
+
* implementation and cannot drift.
|
|
10
|
+
*/
|
|
11
|
+
const makeWire = (Class) => {
|
|
12
|
+
const knownKeys = new Set(Object.keys(Class.fields).filter((k) => k !== "rest"));
|
|
13
|
+
return RawJson.pipe(Schema.decodeTo(Class, SchemaTransformation.transform({
|
|
14
|
+
decode: (raw) => {
|
|
15
|
+
const known = {};
|
|
16
|
+
const rest = Object.create(null);
|
|
17
|
+
for (const [key, value] of Object.entries(raw)) if (knownKeys.has(key)) known[key] = value;
|
|
18
|
+
else rest[key] = value;
|
|
19
|
+
return {
|
|
20
|
+
...known,
|
|
21
|
+
rest
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
encode: (encoded) => {
|
|
25
|
+
const { rest, ...known } = encoded;
|
|
26
|
+
return {
|
|
27
|
+
...rest ?? {},
|
|
28
|
+
...known
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
})));
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
export { makeWire };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/package-json",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "package.json parsing, editing, validation and file IO as Effect schemas.",
|
|
6
6
|
"keywords": [
|
|
@@ -38,12 +38,13 @@
|
|
|
38
38
|
"./package.json": "./package.json"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@effected/
|
|
42
|
-
"@effected/
|
|
43
|
-
"@effected/
|
|
41
|
+
"@effected/jsonc": "^0.6.0",
|
|
42
|
+
"@effected/npm": "^0.9.0",
|
|
43
|
+
"@effected/semver": "^0.4.0",
|
|
44
|
+
"@effected/spdx": "^0.2.0"
|
|
44
45
|
},
|
|
45
46
|
"peerDependencies": {
|
|
46
|
-
"effect": "4.0.0-beta.
|
|
47
|
+
"effect": "4.0.0-beta.107"
|
|
47
48
|
},
|
|
48
49
|
"engines": {
|
|
49
50
|
"node": ">=24.11.0"
|