@effected/package-json 0.10.2 → 0.12.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/EntryPoint.js ADDED
@@ -0,0 +1,156 @@
1
+ import { Result, Schema } from "effect";
2
+
3
+ //#region src/EntryPoint.ts
4
+ /**
5
+ * Raised when a manifest resolves no root entry point.
6
+ *
7
+ * @remarks
8
+ * The reason is discriminated rather than a bare "not found" because the three
9
+ * shapes call for different responses, and a caller staring at a consumer's
10
+ * plugin at 3am needs to know which one it hit. Collapsing them into one
11
+ * sentinel is the same class of quiet wrong answer as an untyped error channel.
12
+ *
13
+ * @public
14
+ */
15
+ var UnresolvedEntryPointError = class extends Schema.TaggedError()("UnresolvedEntryPointError", {
16
+ /**
17
+ * `noRootExport` — `exports` is a subpath map with no `"."` entry, so the
18
+ * package exports subpaths but no root. `noConditionMatched` — a root
19
+ * entry exists but none of the requested conditions are present, e.g. a
20
+ * `require`-only package read with `["import"]`.
21
+ * `unsupportedExportsForm` — an array fallback list, or another shape this
22
+ * resolver does not implement.
23
+ */
24
+ reason: Schema.Literals([
25
+ "noRootExport",
26
+ "noConditionMatched",
27
+ "unsupportedExportsForm"
28
+ ]),
29
+ /** The conditions that were tried, for `noConditionMatched`. */
30
+ conditions: Schema.optionalKey(Schema.Array(Schema.String))
31
+ }) {
32
+ get message() {
33
+ switch (this.reason) {
34
+ case "noRootExport": return "The manifest's \"exports\" declares subpaths but no \".\" entry, so it has no root entry point";
35
+ case "noConditionMatched": return `The manifest's "exports" matched none of the conditions ${JSON.stringify(this.conditions ?? [])}`;
36
+ default: return "The manifest's \"exports\" uses a form this resolver does not implement";
37
+ }
38
+ }
39
+ };
40
+ const DEFAULT_CONDITIONS = ["import", "default"];
41
+ /** A plain object — not an array, not `null`. */
42
+ const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
43
+ /**
44
+ * Is this `exports` object a conditions map rather than a subpath map?
45
+ *
46
+ * @remarks
47
+ * Node's rule: the two forms cannot be mixed, and a subpath map is identified
48
+ * by keys starting with `"."`. So an object with no `"."`-prefixed key is
49
+ * conditions-only sugar for the `"."` subpath. An empty object is neither — it
50
+ * exports nothing.
51
+ */
52
+ const isRootConditions = (exportsObject) => {
53
+ const keys = Object.keys(exportsObject);
54
+ return keys.length > 0 && !keys.some((key) => key.startsWith("."));
55
+ };
56
+ /**
57
+ * Resolve a conditions object to a file, honouring `conditions` in order.
58
+ *
59
+ * @remarks
60
+ * Recurses, because conditions nest: `{ "import": { "node": "./n.js" } }` is
61
+ * legal and a non-recursive reader answers an object where a path belongs.
62
+ */
63
+ const resolveConditions = (conditionsObject, conditions) => {
64
+ for (const condition of conditions) {
65
+ const matched = conditionsObject[condition];
66
+ if (typeof matched === "string") return matched;
67
+ if (isPlainObject(matched)) {
68
+ const nested = resolveConditions(matched, conditions);
69
+ if (nested !== void 0) return nested;
70
+ }
71
+ }
72
+ };
73
+ /**
74
+ * Resolve a package's root entry point from its manifest.
75
+ *
76
+ * @remarks
77
+ * The half of "read something out of a published package" that has no home
78
+ * anywhere else: given a manifest, which file is the package's `"."` entry?
79
+ * It is pure and IO-free by design — nothing here touches a filesystem, so it
80
+ * is testable against plain manifest objects with no package on disk, and it
81
+ * composes with a directory that arrived by any route.
82
+ *
83
+ * All three legal `exports` spellings are honoured, because all three appear in
84
+ * real published packages:
85
+ *
86
+ * - **String shorthand** — `"exports": "./index.js"`, sugar for `{ ".": … }`.
87
+ * - **Subpath map** — `{ ".": "./index.js" }`, or `{ ".": { import, … } }`.
88
+ * - **Root conditions** — `{ "import": "./index.js", "default": "./index.cjs" }`,
89
+ * conditions at the root with no `"."` key.
90
+ *
91
+ * **`exports` encapsulates the package.** When it is present but nothing
92
+ * matches, the answer is a typed failure and `main` is **not** consulted — that
93
+ * is Node's rule, and the lenient reading (falling through to `main`, then to
94
+ * `index.js`) is the subtly wrong one: it answers a file the package
95
+ * deliberately does not export, which loads and behaves plausibly instead of
96
+ * failing. Only when `exports` is **absent** does `main`, and then the legacy
97
+ * `index.js` default, apply.
98
+ *
99
+ * A failure is also the answer for an `exports` form this resolver does not
100
+ * implement — an array fallback list, or a subpath map with no `"."` entry.
101
+ * Both are honest "this resolver cannot tell you", never a guess, and each
102
+ * carries its own {@link UnresolvedEntryPointError} reason so a caller can log
103
+ * which shape a package actually had rather than a flat "could not resolve".
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * import { resolveEntryPoint } from "@effected/package-json";
108
+ * import { Result, Schema } from "effect";
109
+ *
110
+ * resolveEntryPoint({ exports: { import: "./esm.js", require: "./cjs.js" } });
111
+ * // Result.succeed("./esm.js")
112
+ *
113
+ * resolveEntryPoint({ exports: { require: "./cjs.js" } }, { conditions: ["require"] });
114
+ * // Result.succeed("./cjs.js")
115
+ *
116
+ * resolveEntryPoint({ exports: { require: "./cjs.js" }, main: "./legacy.js" });
117
+ * // Result.fail(UnresolvedEntryPointError { reason: "noConditionMatched" })
118
+ * ```
119
+ *
120
+ * @param manifest - A package manifest, or any object carrying `exports`/`main`.
121
+ * @param options - Which conditions to honour, in priority order.
122
+ * @returns The entry path as written in the manifest, relative to the package
123
+ * root, or a typed {@link UnresolvedEntryPointError} naming which shape
124
+ * blocked resolution.
125
+ *
126
+ * @public
127
+ */
128
+ const resolveEntryPoint = (manifest, options) => {
129
+ const conditions = options?.conditions ?? DEFAULT_CONDITIONS;
130
+ const exportsField = manifest.exports;
131
+ if (typeof exportsField === "string") return Result.succeed(exportsField);
132
+ if (isPlainObject(exportsField)) {
133
+ if (isRootConditions(exportsField)) {
134
+ const resolved = resolveConditions(exportsField, conditions);
135
+ return resolved === void 0 ? Result.fail(new UnresolvedEntryPointError({
136
+ reason: "noConditionMatched",
137
+ conditions
138
+ })) : Result.succeed(resolved);
139
+ }
140
+ const dot = exportsField["."];
141
+ if (typeof dot === "string") return Result.succeed(dot);
142
+ if (isPlainObject(dot)) {
143
+ const resolved = resolveConditions(dot, conditions);
144
+ return resolved === void 0 ? Result.fail(new UnresolvedEntryPointError({
145
+ reason: "noConditionMatched",
146
+ conditions
147
+ })) : Result.succeed(resolved);
148
+ }
149
+ return Result.fail(new UnresolvedEntryPointError({ reason: "noRootExport" }));
150
+ }
151
+ if (exportsField !== void 0) return Result.fail(new UnresolvedEntryPointError({ reason: "unsupportedExportsForm" }));
152
+ return Result.succeed(typeof manifest.main === "string" && manifest.main !== "" ? manifest.main : "index.js");
153
+ };
154
+
155
+ //#endregion
156
+ export { UnresolvedEntryPointError, resolveEntryPoint };
@@ -0,0 +1,256 @@
1
+ import { ExportsField, PackageDecodeError, PublishConfigField } from "./Package.js";
2
+ import { PackageJsonSyntaxError } from "./PackageJsonFormat.js";
3
+ import { Cause, Effect, Exit, Result, Schema } from "effect";
4
+
5
+ //#region src/LenientManifest.ts
6
+ const LenientFieldIssueSchema = Schema.Struct({
7
+ field: Schema.String,
8
+ expected: Schema.String,
9
+ value: Schema.Unknown
10
+ });
11
+ const StringRecord = Schema.Record(Schema.String, Schema.String);
12
+ const UnknownRecord = Schema.Record(Schema.String, Schema.Unknown);
13
+ const StringOrRecord = Schema.Union([Schema.String, UnknownRecord]);
14
+ const isString = (value) => typeof value === "string";
15
+ const isBoolean = (value) => typeof value === "boolean";
16
+ const isPlainRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
17
+ const isStringRecord = (value) => isPlainRecord(value) && Object.values(value).every(isString);
18
+ const isStringArray = (value) => Array.isArray(value) && value.every(isString);
19
+ const isStringOrRecord = (value) => isString(value) || isPlainRecord(value);
20
+ const isStringOrRecordArray = (value) => Array.isArray(value) && value.every(isStringOrRecord);
21
+ const stringGuard = {
22
+ expected: "a string",
23
+ test: isString
24
+ };
25
+ const stringRecordGuard = {
26
+ expected: "an object of string values",
27
+ test: isStringRecord
28
+ };
29
+ const stringOrRecordGuard = {
30
+ expected: "a string or an object",
31
+ test: isStringOrRecord
32
+ };
33
+ const recordGuard = {
34
+ expected: "an object",
35
+ test: isPlainRecord
36
+ };
37
+ const FIELD_GUARDS = /* @__PURE__ */ new Map([
38
+ ["name", stringGuard],
39
+ ["version", stringGuard],
40
+ ["description", stringGuard],
41
+ ["private", {
42
+ expected: "a boolean",
43
+ test: isBoolean
44
+ }],
45
+ ["type", stringGuard],
46
+ ["main", stringGuard],
47
+ ["license", stringGuard],
48
+ ["author", stringOrRecordGuard],
49
+ ["contributors", {
50
+ expected: "an array of strings or objects",
51
+ test: isStringOrRecordArray
52
+ }],
53
+ ["maintainers", {
54
+ expected: "an array of strings or objects",
55
+ test: isStringOrRecordArray
56
+ }],
57
+ ["keywords", {
58
+ expected: "an array of strings",
59
+ test: isStringArray
60
+ }],
61
+ ["repository", stringOrRecordGuard],
62
+ ["bugs", stringOrRecordGuard],
63
+ ["homepage", stringGuard],
64
+ ["dependencies", stringRecordGuard],
65
+ ["devDependencies", stringRecordGuard],
66
+ ["peerDependencies", stringRecordGuard],
67
+ ["optionalDependencies", stringRecordGuard],
68
+ ["peerDependenciesMeta", recordGuard],
69
+ ["scripts", stringRecordGuard],
70
+ ["bin", {
71
+ expected: "a string or an object of string values",
72
+ test: (v) => isString(v) || isStringRecord(v)
73
+ }],
74
+ ["engines", stringRecordGuard],
75
+ ["exports", stringOrRecordGuard],
76
+ ["publishConfig", recordGuard],
77
+ ["packageManager", stringGuard],
78
+ ["devEngines", recordGuard]
79
+ ]);
80
+ const sift = (raw) => {
81
+ const known = {};
82
+ const rest = Object.create(null);
83
+ const issues = [];
84
+ for (const [key, value] of Object.entries(raw)) {
85
+ const guard = FIELD_GUARDS.get(key);
86
+ if (guard === void 0) rest[key] = value;
87
+ else if (guard.test(value)) known[key] = value;
88
+ else {
89
+ rest[key] = value;
90
+ issues.push({
91
+ field: key,
92
+ expected: guard.expected,
93
+ value
94
+ });
95
+ }
96
+ }
97
+ return LenientManifest.make({
98
+ ...known,
99
+ rest,
100
+ issues
101
+ });
102
+ };
103
+ const decodeRecord = Schema.decodeUnknownExit(UnknownRecord);
104
+ /**
105
+ * The shape-lenient view of a package.json document, for discovery and
106
+ * sniffing — probing a fetched tarball's manifest, walking a `node_modules`
107
+ * tree, listing candidate packages — where the document is other people's
108
+ * data and one malformed field must not fail the read.
109
+ *
110
+ * @remarks
111
+ * **This is the discovery tier, not a validation bypass.** Every field shares
112
+ * its name with the strict `Package` model, but is typed as its plain permissive JSON
113
+ * shape: `name` and `version` are any string (a legacy uppercase name or a
114
+ * non-semver `"1.0"` is recovered, not rejected), `license` is any string (no
115
+ * SPDX check), the dependency maps and `scripts` are plain string→string
116
+ * records rather than `HashMap`s. A present field that is not even that shape
117
+ * **degrades to absence** rather than failing the document: the raw value is
118
+ * preserved verbatim in `rest` and the degradation is reported on `issues`,
119
+ * so callers can surface what was ignored. Degradation granularity is the
120
+ * top-level field — one junk entry degrades its whole map, with the raw map
121
+ * still in `rest`.
122
+ *
123
+ * Leniency is per-field, never per-syntax: text that is not valid JSON fails
124
+ * {@link LenientManifest.parseResult} as a typed
125
+ * {@link PackageJsonSyntaxError}, and a value that is not a JSON object fails
126
+ * {@link LenientManifest.decodeResult} as a typed {@link PackageDecodeError}.
127
+ *
128
+ * An empty `issues` array does **not** mean the strict tiers would accept the
129
+ * document — the permissive shapes check JSON shape, not npm semantics. The
130
+ * upgrade path is to decode the *original* input through
131
+ * `PackageManifest.decode` (presence-lenient, shape-strict) or
132
+ * `Package.decode` (strict, publishable) when validation is actually
133
+ * wanted. This class deliberately carries no mutation statics and no write
134
+ * path; editing belongs to the strict tiers and to
135
+ * `PackageJsonFormat.modifyToString` / `PackageJsonFile.modify`.
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * import { LenientManifest } from "@effected/package-json";
140
+ * import { Effect } from "effect";
141
+ *
142
+ * const program = Effect.gen(function* () {
143
+ * const sniffed = yield* LenientManifest.decode({ name: "JSONStream", version: "1.0", license: 42 });
144
+ * console.log(sniffed.name, sniffed.version); // "JSONStream" "1.0"
145
+ * console.log(sniffed.issues); // [{ field: "license", expected: "a string", value: 42 }]
146
+ * console.log(sniffed.rest?.license); // 42 — degraded, preserved verbatim
147
+ * });
148
+ * ```
149
+ *
150
+ * @public
151
+ */
152
+ var LenientManifest = class LenientManifest extends Schema.Class("LenientManifest")({
153
+ name: Schema.optionalKey(Schema.String),
154
+ version: Schema.optionalKey(Schema.String),
155
+ description: Schema.optionalKey(Schema.String),
156
+ private: Schema.optionalKey(Schema.Boolean),
157
+ type: Schema.optionalKey(Schema.String),
158
+ main: Schema.optionalKey(Schema.String),
159
+ license: Schema.optionalKey(Schema.String),
160
+ author: Schema.optionalKey(StringOrRecord),
161
+ contributors: Schema.optionalKey(Schema.Array(StringOrRecord)),
162
+ maintainers: Schema.optionalKey(Schema.Array(StringOrRecord)),
163
+ keywords: Schema.optionalKey(Schema.Array(Schema.String)),
164
+ repository: Schema.optionalKey(StringOrRecord),
165
+ bugs: Schema.optionalKey(StringOrRecord),
166
+ homepage: Schema.optionalKey(Schema.String),
167
+ dependencies: Schema.optionalKey(StringRecord),
168
+ devDependencies: Schema.optionalKey(StringRecord),
169
+ peerDependencies: Schema.optionalKey(StringRecord),
170
+ optionalDependencies: Schema.optionalKey(StringRecord),
171
+ peerDependenciesMeta: Schema.optionalKey(UnknownRecord),
172
+ scripts: Schema.optionalKey(StringRecord),
173
+ bin: Schema.optionalKey(Schema.Union([Schema.String, StringRecord])),
174
+ engines: Schema.optionalKey(StringRecord),
175
+ exports: Schema.optionalKey(ExportsField),
176
+ publishConfig: Schema.optionalKey(PublishConfigField),
177
+ packageManager: Schema.optionalKey(Schema.String),
178
+ devEngines: Schema.optionalKey(UnknownRecord),
179
+ /**
180
+ * Unknown top-level keys, plus every degraded known field's raw value,
181
+ * verbatim. Always present after a lenient decode (possibly empty).
182
+ */
183
+ rest: Schema.optionalKey(UnknownRecord),
184
+ /** The degradations collected by the decode — empty when nothing degraded. */
185
+ issues: Schema.Array(LenientFieldIssueSchema)
186
+ }) {
187
+ /**
188
+ * Decode an unknown JSON value leniently, degrading malformed fields instead
189
+ * of failing the document. The sync primitive backing
190
+ * {@link LenientManifest.decode}.
191
+ *
192
+ * @param input - the parsed package.json JSON value (e.g. from `JSON.parse`)
193
+ * @returns the lenient manifest, or a {@link PackageDecodeError} when
194
+ * `input` is not a JSON object at all (`null`, an array or a scalar) — the
195
+ * one failure leniency does not cover
196
+ */
197
+ static decodeResult(input) {
198
+ const exit = decodeRecord(input);
199
+ if (Exit.isFailure(exit)) return Result.fail(new PackageDecodeError({ cause: Cause.squash(exit.cause) }));
200
+ return Result.succeed(sift(exit.value));
201
+ }
202
+ /**
203
+ * Decode an unknown JSON value leniently, degrading malformed fields instead
204
+ * of failing the document. The `Effect` form of
205
+ * {@link LenientManifest.decodeResult}, adding the tracing span.
206
+ *
207
+ * @param input - the parsed package.json JSON value (e.g. from `JSON.parse`)
208
+ * @returns an Effect resolving to the decoded {@link LenientManifest}
209
+ * @throws (typed) `PackageDecodeError` when `input` is not a JSON object
210
+ */
211
+ static decode = Effect.fn("LenientManifest.decode")((input) => Effect.fromResult(LenientManifest.decodeResult(input)));
212
+ /**
213
+ * Parse package.json text and decode it leniently. The sync primitive
214
+ * backing {@link LenientManifest.parse}.
215
+ *
216
+ * @param text - the package.json source text
217
+ * @returns the lenient manifest, or a {@link PackageJsonSyntaxError} when
218
+ * the text is not valid JSON (`"invalid-json"`) or parses to something
219
+ * other than a JSON object (`"not-an-object"`) — leniency is per-field,
220
+ * never per-syntax
221
+ */
222
+ static parseResult(text) {
223
+ let raw;
224
+ try {
225
+ raw = JSON.parse(text);
226
+ } catch (cause) {
227
+ return Result.fail(new PackageJsonSyntaxError({
228
+ reason: "invalid-json",
229
+ cause
230
+ }));
231
+ }
232
+ if (!isPlainRecord(raw)) return Result.fail(new PackageJsonSyntaxError({ reason: "not-an-object" }));
233
+ return Result.succeed(sift(raw));
234
+ }
235
+ /**
236
+ * Parse package.json text and decode it leniently. The `Effect` form of
237
+ * {@link LenientManifest.parseResult}, adding the tracing span.
238
+ *
239
+ * @param text - the package.json source text
240
+ * @returns an Effect resolving to the decoded {@link LenientManifest}
241
+ * @throws (typed) `PackageJsonSyntaxError` when the text is not valid JSON
242
+ * or is not a JSON object
243
+ */
244
+ static parse = Effect.fn("LenientManifest.parse")((text) => Effect.fromResult(LenientManifest.parseResult(text)));
245
+ /** Whether the manifest is marked private. */
246
+ get isPrivate() {
247
+ return this.private ?? false;
248
+ }
249
+ /** Whether the manifest declares ESM (`"type": "module"`, exact comparison). */
250
+ get isESM() {
251
+ return this.type === "module";
252
+ }
253
+ };
254
+
255
+ //#endregion
256
+ export { LenientManifest };
package/Package.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Dependency } from "./Dependency.js";
2
2
  import { DevEnginesSchema } from "./DevEngines.js";
3
- import { InvalidSpdxLicenseError, SpdxLicense, isValidSpdx } from "./License.js";
4
3
  import { renderJson, resolveFormatOptions } from "./internal/format.js";
5
4
  import { makeWire } from "./internal/wire.js";
5
+ import { InvalidSpdxLicenseError, SpdxLicense, isValidSpdx } from "./License.js";
6
6
  import { PackageManager } from "./PackageManager.js";
7
7
  import { InvalidPackageNameError, PackageName } from "./PackageName.js";
8
8
  import { Person } from "./Person.js";
@@ -23,10 +23,12 @@ import { SemVer } from "@effected/semver";
23
23
  * typed), a present `name` must still satisfy the npm grammar, a present
24
24
  * `packageManager` must still parse — though here the version position may be
25
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.
26
+ * For tolerance of malformed fields, use the shape-lenient `LenientManifest`
27
+ * discovery tier which degrades them to absence, preserved in `rest` and
28
+ * reported on `issues` — or the decode-free {@link PackageJsonFormat} text
29
+ * path (or `@effected/npm`'s shape-blind `Manifest`); here, silently carrying
30
+ * a value the type claims to have validated would be a lie, and silently
31
+ * dropping it would break round-trip fidelity.
30
32
  *
31
33
  * The model is deliberately lean — fields, the `rest` catch-all wire codec,
32
34
  * {@link PackageManifest.decode} and {@link PackageManifest.toJsonString} —
package/README.md CHANGED
@@ -184,6 +184,58 @@ console.log(Effect.runSync(program));
184
184
 
185
185
  The `workspace:` range modifier is honored: `workspace:*` takes the bare version, `workspace:^` and `workspace:~` prefix it, and an explicit modifier is used as-is. The projection is `@effected/npm`'s `DependencySpecifier` statics with full pnpm publish semantics: the alias form `workspace:<name>@<range>` resolves the *target* package's version and becomes the `npm:<name>@<range>` alias pnpm publishes, and a blank catalog name selects the default catalog. A failed catalog assembly surfaces typed as `@effected/npm`'s `CatalogAssemblyError`, alongside the contracts' `DependencyResolutionError`.
186
186
 
187
+ ## Lenient discovery
188
+
189
+ `Package.decode` and `PackageManifest` are strict: a malformed field fails the whole document. That is the right behavior for a manifest you are about to write or publish, and the wrong one for a manifest you are only sniffing — a fetched tarball, a `node_modules` walk, a registry response — where the document is someone else's data and one bad field should not sink the read. `LenientManifest` is that discovery tier: every `Package` field decodes to its plain permissive JSON shape (`name` and `version` accept any string, not the branded npm grammar; `license` accepts any string, no SPDX check; the dependency maps are plain records, not `HashMap`s). A field present but not even that shape degrades to absence instead of failing the document, its raw value is preserved verbatim in `rest` (a malformed known field is treated exactly like an unknown one), and the degradation is reported on `issues`:
190
+
191
+ ```ts
192
+ import { LenientManifest } from "@effected/package-json";
193
+ import { Effect } from "effect";
194
+
195
+ const program = Effect.gen(function* () {
196
+ const sniffed = yield* LenientManifest.decode({ name: "JSONStream", version: "1.0", license: 42 });
197
+ return [sniffed.name, sniffed.version, sniffed.issues, sniffed.rest?.license] as const;
198
+ });
199
+
200
+ console.log(Effect.runSync(program));
201
+ // [
202
+ // "JSONStream",
203
+ // "1.0",
204
+ // [{ field: "license", expected: "a string", value: 42 }],
205
+ // 42,
206
+ // ]
207
+ ```
208
+
209
+ Leniency is per-field, never per-syntax: `decodeResult`/`decode` still fail typed with `PackageDecodeError` when the input is not a JSON object at all (`null`, an array, a scalar), and `parseResult`/`parse` — the pair that also handles the raw `JSON.parse` — fail typed with `PackageJsonSyntaxError` when the text is not valid JSON or does not parse to an object. Each pair follows the package's usual shape: `decodeResult`/`parseResult` are the synchronous `Result` primitives, `decode`/`parse` are their `Effect` forms with a tracing span.
210
+
211
+ An empty `issues` array is not a validity guarantee — the permissive shapes check JSON shape, not npm semantics, so a `LenientManifest` with no issues can still fail `Package.decode`. `LenientManifest` carries no mutation statics and no write path; once you need to validate or edit, re-decode the *original* input through `PackageManifest.decode` (presence-lenient, shape-on-presence strict) or `Package.decode` (strict, publishable).
212
+
213
+ ## Resolving an entry point
214
+
215
+ `resolveEntryPoint` answers one question about a manifest — which file is the package's `"."` entry — and it is pure, IO-free and `Result`-returning, so it works against a plain object with no package on disk:
216
+
217
+ ```ts
218
+ import { resolveEntryPoint } from "@effected/package-json";
219
+
220
+ resolveEntryPoint({ exports: { import: "./esm.js", require: "./cjs.js" } });
221
+ // Result.succeed("./esm.js")
222
+
223
+ resolveEntryPoint({ exports: { require: "./cjs.js" } }, { conditions: ["require"] });
224
+ // Result.succeed("./cjs.js")
225
+
226
+ resolveEntryPoint({ main: "./legacy.js" });
227
+ // Result.succeed("./legacy.js") — no exports field, so main applies
228
+
229
+ resolveEntryPoint({ exports: { require: "./cjs.js" }, main: "./legacy.js" });
230
+ // Result.fail(UnresolvedEntryPointError { reason: "noConditionMatched" })
231
+ ```
232
+
233
+ All three legal `exports` spellings are honored — the string shorthand, a subpath map, and conditions at the root with no `"."` key — and conditions default to `["import", "default"]`, in priority order.
234
+
235
+ The last case above is the semantic worth knowing: **`exports` encapsulates the package**, so a present-but-unmatched `exports` is a typed failure and `main` is *not* consulted. That is Node's rule. The lenient reading — falling through to `main`, then to `index.js` — answers a file the package deliberately does not export, and it loads and behaves plausibly instead of failing. Only an **absent** `exports` reaches `main`, and then the legacy `index.js` default. An `exports` form the resolver does not implement (an array fallback list, or a subpath map with no `"."` entry) fails for its own reason rather than being guessed at.
236
+
237
+ Pair it with `@effected/npm`'s `PackageTarball` to find the entry file inside a tarball extracted before any install has run.
238
+
187
239
  ## Errors
188
240
 
189
241
  Every failure is a `Schema.TaggedError` routed with `Effect.catchTag`. Causes are preserved structurally on a `Schema.Defect` field — a `PackageDecodeError` hands you the `SchemaError` issue tree, not `String(error)`.
@@ -199,6 +251,7 @@ Every failure is a `Schema.TaggedError` routed with `Effect.catchTag`. Causes ar
199
251
  | `InvalidPackageNameError` | A string does not satisfy npm's naming rules. Raised by `Package.setName`. |
200
252
  | `InvalidSpdxLicenseError` | A string is not a valid SPDX license expression. Raised by `Package.setLicense`. |
201
253
  | `InvalidDependencySpecifierError` | A string is not a recognized dependency specifier. Raised by `DependencySpecifier.decode`. |
254
+ | `UnresolvedEntryPointError` | No entry point could be resolved from a manifest. Returned in a `Result` by `resolveEntryPoint`, never raised, with `reason` telling `noConditionMatched`, `noRootExport` and `unsupportedExportsForm` apart. |
202
255
 
203
256
  `Package.setVersion` fails with `InvalidVersionError` from `@effected/semver`, which is where the version grammar lives.
204
257
 
@@ -208,6 +261,8 @@ Every failure is a `Schema.TaggedError` routed with `Effect.catchTag`. Causes ar
208
261
  - `Package.schema` / `Package.wireFor` — the open-JSON ↔ class wire codec, and the factory that builds one for a `.extend()`ed subclass so its custom fields decode as typed members instead of falling into `rest`.
209
262
  - `PackageJsonFile` — the IO surface: `read` and `write` over core `FileSystem` / `Path`, with the platform implementation supplied at the edge.
210
263
  - `PackageValidator` — rule-based validation aggregating every failure, with the default rule set, a parameterized `layerRules` factory, and the publish-gate rules `noUnresolvedDepsRule` and `noLocalDepsRule`.
264
+ - `resolveEntryPoint` — the pure, `Result`-returning entry-point resolver over a manifest's `exports`/`main`, honoring `exports` encapsulation rather than falling through to `main`, with `EntryPointManifest` as its tolerant input shape.
265
+ - `LenientManifest` — the shape-lenient discovery tier below `PackageManifest`: malformed known fields degrade to absence, are preserved verbatim in `rest` and reported on `issues`, rather than failing the document; `decodeResult`/`decode` and `parseResult`/`parse` in the package's usual `Result`/`Effect` pairing.
211
266
  - `Package.resolve` — `catalog:` and `workspace:` expansion over the `@effected/npm` contracts with pnpm's publish-time projection (alias form included), as an explicit step that `write` never performs for you.
212
267
  - `PackageName`, `DependencySpecifier`, `Dependency`, `SpdxLicense`, `PackageManager`, `Person`, `Repository`, `Bugs`, `DevEngine` — the leaf concepts, each owning its own statics, brand and error, usable independently of `Package`. `Repository` and `Bugs` decode the `repository` and `bugs` fields from either their shorthand or object form, the same way `Person` does for `author`, `contributors` and `maintainers`; `Repository` also exposes `browseUrl` and `gitUrl` getters that normalize a shorthand or SSH form to `https://`.
213
268
  - `Package` also types `keywords`, `maintainers` and `homepage` directly, alongside the existing `author` and `contributors`.
package/index.d.ts CHANGED
@@ -106,6 +106,124 @@ declare const DevEnginesSchema: Schema.Struct<{
106
106
  */
107
107
  type DevEngines = typeof DevEnginesSchema.Type;
108
108
  //#endregion
109
+ //#region src/EntryPoint.d.ts
110
+ /**
111
+ * Options for {@link resolveEntryPoint}.
112
+ *
113
+ * @public
114
+ */
115
+ interface ResolveEntryPointOptions {
116
+ /**
117
+ * The export conditions to honour, in priority order.
118
+ *
119
+ * @remarks
120
+ * The first condition present in the manifest wins, so the order is the
121
+ * policy — `["require", "import"]` and `["import", "require"]` resolve the
122
+ * same manifest to different files, on purpose.
123
+ *
124
+ * @defaultValue `["import", "default"]`
125
+ */
126
+ readonly conditions?: ReadonlyArray<string>;
127
+ }
128
+ declare const UnresolvedEntryPointError_base: Schema.Class<UnresolvedEntryPointError, Schema.TaggedStruct<"UnresolvedEntryPointError", {
129
+ /**
130
+ * `noRootExport` — `exports` is a subpath map with no `"."` entry, so the
131
+ * package exports subpaths but no root. `noConditionMatched` — a root
132
+ * entry exists but none of the requested conditions are present, e.g. a
133
+ * `require`-only package read with `["import"]`.
134
+ * `unsupportedExportsForm` — an array fallback list, or another shape this
135
+ * resolver does not implement.
136
+ */
137
+ readonly reason: Schema.Literals<readonly ["noRootExport", "noConditionMatched", "unsupportedExportsForm"]>;
138
+ /** The conditions that were tried, for `noConditionMatched`. */
139
+ readonly conditions: Schema.optionalKey<Schema.$Array<Schema.String>>;
140
+ }>, import("effect/Cause").YieldableError>;
141
+ /**
142
+ * Raised when a manifest resolves no root entry point.
143
+ *
144
+ * @remarks
145
+ * The reason is discriminated rather than a bare "not found" because the three
146
+ * shapes call for different responses, and a caller staring at a consumer's
147
+ * plugin at 3am needs to know which one it hit. Collapsing them into one
148
+ * sentinel is the same class of quiet wrong answer as an untyped error channel.
149
+ *
150
+ * @public
151
+ */
152
+ declare class UnresolvedEntryPointError extends UnresolvedEntryPointError_base {
153
+ get message(): string;
154
+ }
155
+ /**
156
+ * The manifest fields entry resolution reads.
157
+ *
158
+ * @remarks
159
+ * Deliberately structural rather than the full {@link PackageManifest}, so a
160
+ * caller can resolve an entry point from any object carrying these two fields —
161
+ * a manifest parsed straight from a tarball, for instance, with nothing else
162
+ * validated yet.
163
+ *
164
+ * @public
165
+ */
166
+ interface EntryPointManifest {
167
+ readonly exports?: unknown;
168
+ readonly main?: unknown;
169
+ }
170
+ /**
171
+ * Resolve a package's root entry point from its manifest.
172
+ *
173
+ * @remarks
174
+ * The half of "read something out of a published package" that has no home
175
+ * anywhere else: given a manifest, which file is the package's `"."` entry?
176
+ * It is pure and IO-free by design — nothing here touches a filesystem, so it
177
+ * is testable against plain manifest objects with no package on disk, and it
178
+ * composes with a directory that arrived by any route.
179
+ *
180
+ * All three legal `exports` spellings are honoured, because all three appear in
181
+ * real published packages:
182
+ *
183
+ * - **String shorthand** — `"exports": "./index.js"`, sugar for `{ ".": … }`.
184
+ * - **Subpath map** — `{ ".": "./index.js" }`, or `{ ".": { import, … } }`.
185
+ * - **Root conditions** — `{ "import": "./index.js", "default": "./index.cjs" }`,
186
+ * conditions at the root with no `"."` key.
187
+ *
188
+ * **`exports` encapsulates the package.** When it is present but nothing
189
+ * matches, the answer is a typed failure and `main` is **not** consulted — that
190
+ * is Node's rule, and the lenient reading (falling through to `main`, then to
191
+ * `index.js`) is the subtly wrong one: it answers a file the package
192
+ * deliberately does not export, which loads and behaves plausibly instead of
193
+ * failing. Only when `exports` is **absent** does `main`, and then the legacy
194
+ * `index.js` default, apply.
195
+ *
196
+ * A failure is also the answer for an `exports` form this resolver does not
197
+ * implement — an array fallback list, or a subpath map with no `"."` entry.
198
+ * Both are honest "this resolver cannot tell you", never a guess, and each
199
+ * carries its own {@link UnresolvedEntryPointError} reason so a caller can log
200
+ * which shape a package actually had rather than a flat "could not resolve".
201
+ *
202
+ * @example
203
+ * ```ts
204
+ * import { resolveEntryPoint } from "@effected/package-json";
205
+ * import { Result, Schema } from "effect";
206
+ *
207
+ * resolveEntryPoint({ exports: { import: "./esm.js", require: "./cjs.js" } });
208
+ * // Result.succeed("./esm.js")
209
+ *
210
+ * resolveEntryPoint({ exports: { require: "./cjs.js" } }, { conditions: ["require"] });
211
+ * // Result.succeed("./cjs.js")
212
+ *
213
+ * resolveEntryPoint({ exports: { require: "./cjs.js" }, main: "./legacy.js" });
214
+ * // Result.fail(UnresolvedEntryPointError { reason: "noConditionMatched" })
215
+ * ```
216
+ *
217
+ * @param manifest - A package manifest, or any object carrying `exports`/`main`.
218
+ * @param options - Which conditions to honour, in priority order.
219
+ * @returns The entry path as written in the manifest, relative to the package
220
+ * root, or a typed {@link UnresolvedEntryPointError} naming which shape
221
+ * blocked resolution.
222
+ *
223
+ * @public
224
+ */
225
+ declare const resolveEntryPoint: (manifest: EntryPointManifest, options?: ResolveEntryPointOptions) => Result.Result<string, UnresolvedEntryPointError>;
226
+ //#endregion
109
227
  //#region src/License.d.ts
110
228
  declare const InvalidSpdxLicenseError_base: Schema.Class<InvalidSpdxLicenseError, Schema.TaggedStruct<"InvalidSpdxLicenseError", {
111
229
  /** The raw input string that failed validation. */
@@ -960,6 +1078,162 @@ declare class PackageJsonFormat {
960
1078
  static readonly modifyToString: (source: string, path: JsoncPath$1, value: unknown) => Effect.Effect<string, PackageJsonModifyError | PackageJsonSyntaxError, never>;
961
1079
  }
962
1080
  //#endregion
1081
+ //#region src/LenientManifest.d.ts
1082
+ /**
1083
+ * One degraded field from a lenient decode: the top-level `field` that did not
1084
+ * match its permissive shape, a human-readable description of the `expected`
1085
+ * shape, and the raw `value` found there (also preserved verbatim under
1086
+ * `LenientManifest.rest[field]`).
1087
+ *
1088
+ * A value, not an error — the decode still succeeds; issues exist so callers
1089
+ * can report what degraded.
1090
+ *
1091
+ * @public
1092
+ */
1093
+ interface LenientFieldIssue {
1094
+ /** The top-level field name that degraded, e.g. `"name"`. */
1095
+ readonly field: string;
1096
+ /** A human-readable description of the permissive shape the field required. */
1097
+ readonly expected: string;
1098
+ /** The raw value found on the wire, preserved for reporting. */
1099
+ readonly value: unknown;
1100
+ }
1101
+ declare const LenientManifest_base: Schema.Class<LenientManifest, Schema.Struct<{
1102
+ readonly name: Schema.optionalKey<Schema.String>;
1103
+ readonly version: Schema.optionalKey<Schema.String>;
1104
+ readonly description: Schema.optionalKey<Schema.String>;
1105
+ readonly private: Schema.optionalKey<Schema.Boolean>;
1106
+ readonly type: Schema.optionalKey<Schema.String>;
1107
+ readonly main: Schema.optionalKey<Schema.String>;
1108
+ readonly license: Schema.optionalKey<Schema.String>;
1109
+ readonly author: Schema.optionalKey<Schema.Union<readonly [Schema.String, Schema.$Record<Schema.String, Schema.Unknown>]>>;
1110
+ readonly contributors: Schema.optionalKey<Schema.$Array<Schema.Union<readonly [Schema.String, Schema.$Record<Schema.String, Schema.Unknown>]>>>;
1111
+ readonly maintainers: Schema.optionalKey<Schema.$Array<Schema.Union<readonly [Schema.String, Schema.$Record<Schema.String, Schema.Unknown>]>>>;
1112
+ readonly keywords: Schema.optionalKey<Schema.$Array<Schema.String>>;
1113
+ readonly repository: Schema.optionalKey<Schema.Union<readonly [Schema.String, Schema.$Record<Schema.String, Schema.Unknown>]>>;
1114
+ readonly bugs: Schema.optionalKey<Schema.Union<readonly [Schema.String, Schema.$Record<Schema.String, Schema.Unknown>]>>;
1115
+ readonly homepage: Schema.optionalKey<Schema.String>;
1116
+ readonly dependencies: Schema.optionalKey<Schema.$Record<Schema.String, Schema.String>>;
1117
+ readonly devDependencies: Schema.optionalKey<Schema.$Record<Schema.String, Schema.String>>;
1118
+ readonly peerDependencies: Schema.optionalKey<Schema.$Record<Schema.String, Schema.String>>;
1119
+ readonly optionalDependencies: Schema.optionalKey<Schema.$Record<Schema.String, Schema.String>>;
1120
+ readonly peerDependenciesMeta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
1121
+ readonly scripts: Schema.optionalKey<Schema.$Record<Schema.String, Schema.String>>;
1122
+ readonly bin: Schema.optionalKey<Schema.Union<readonly [Schema.String, Schema.$Record<Schema.String, Schema.String>]>>;
1123
+ readonly engines: Schema.optionalKey<Schema.$Record<Schema.String, Schema.String>>;
1124
+ readonly exports: Schema.optionalKey<Schema.Union<readonly [Schema.String, Schema.$Record<Schema.String, Schema.Unknown>]>>;
1125
+ readonly publishConfig: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
1126
+ readonly packageManager: Schema.optionalKey<Schema.String>;
1127
+ readonly devEngines: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
1128
+ /**
1129
+ * Unknown top-level keys, plus every degraded known field's raw value,
1130
+ * verbatim. Always present after a lenient decode (possibly empty).
1131
+ */
1132
+ readonly rest: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
1133
+ /** The degradations collected by the decode — empty when nothing degraded. */
1134
+ readonly issues: Schema.$Array<Schema.Struct<{
1135
+ readonly field: Schema.String;
1136
+ readonly expected: Schema.String;
1137
+ readonly value: Schema.Unknown;
1138
+ }>>;
1139
+ }>, {}>;
1140
+ /**
1141
+ * The shape-lenient view of a package.json document, for discovery and
1142
+ * sniffing — probing a fetched tarball's manifest, walking a `node_modules`
1143
+ * tree, listing candidate packages — where the document is other people's
1144
+ * data and one malformed field must not fail the read.
1145
+ *
1146
+ * @remarks
1147
+ * **This is the discovery tier, not a validation bypass.** Every field shares
1148
+ * its name with the strict `Package` model, but is typed as its plain permissive JSON
1149
+ * shape: `name` and `version` are any string (a legacy uppercase name or a
1150
+ * non-semver `"1.0"` is recovered, not rejected), `license` is any string (no
1151
+ * SPDX check), the dependency maps and `scripts` are plain string→string
1152
+ * records rather than `HashMap`s. A present field that is not even that shape
1153
+ * **degrades to absence** rather than failing the document: the raw value is
1154
+ * preserved verbatim in `rest` and the degradation is reported on `issues`,
1155
+ * so callers can surface what was ignored. Degradation granularity is the
1156
+ * top-level field — one junk entry degrades its whole map, with the raw map
1157
+ * still in `rest`.
1158
+ *
1159
+ * Leniency is per-field, never per-syntax: text that is not valid JSON fails
1160
+ * {@link LenientManifest.parseResult} as a typed
1161
+ * {@link PackageJsonSyntaxError}, and a value that is not a JSON object fails
1162
+ * {@link LenientManifest.decodeResult} as a typed {@link PackageDecodeError}.
1163
+ *
1164
+ * An empty `issues` array does **not** mean the strict tiers would accept the
1165
+ * document — the permissive shapes check JSON shape, not npm semantics. The
1166
+ * upgrade path is to decode the *original* input through
1167
+ * `PackageManifest.decode` (presence-lenient, shape-strict) or
1168
+ * `Package.decode` (strict, publishable) when validation is actually
1169
+ * wanted. This class deliberately carries no mutation statics and no write
1170
+ * path; editing belongs to the strict tiers and to
1171
+ * `PackageJsonFormat.modifyToString` / `PackageJsonFile.modify`.
1172
+ *
1173
+ * @example
1174
+ * ```ts
1175
+ * import { LenientManifest } from "@effected/package-json";
1176
+ * import { Effect } from "effect";
1177
+ *
1178
+ * const program = Effect.gen(function* () {
1179
+ * const sniffed = yield* LenientManifest.decode({ name: "JSONStream", version: "1.0", license: 42 });
1180
+ * console.log(sniffed.name, sniffed.version); // "JSONStream" "1.0"
1181
+ * console.log(sniffed.issues); // [{ field: "license", expected: "a string", value: 42 }]
1182
+ * console.log(sniffed.rest?.license); // 42 — degraded, preserved verbatim
1183
+ * });
1184
+ * ```
1185
+ *
1186
+ * @public
1187
+ */
1188
+ declare class LenientManifest extends LenientManifest_base {
1189
+ /**
1190
+ * Decode an unknown JSON value leniently, degrading malformed fields instead
1191
+ * of failing the document. The sync primitive backing
1192
+ * {@link LenientManifest.decode}.
1193
+ *
1194
+ * @param input - the parsed package.json JSON value (e.g. from `JSON.parse`)
1195
+ * @returns the lenient manifest, or a {@link PackageDecodeError} when
1196
+ * `input` is not a JSON object at all (`null`, an array or a scalar) — the
1197
+ * one failure leniency does not cover
1198
+ */
1199
+ static decodeResult(input: unknown): Result.Result<LenientManifest, PackageDecodeError>;
1200
+ /**
1201
+ * Decode an unknown JSON value leniently, degrading malformed fields instead
1202
+ * of failing the document. The `Effect` form of
1203
+ * {@link LenientManifest.decodeResult}, adding the tracing span.
1204
+ *
1205
+ * @param input - the parsed package.json JSON value (e.g. from `JSON.parse`)
1206
+ * @returns an Effect resolving to the decoded {@link LenientManifest}
1207
+ * @throws (typed) `PackageDecodeError` when `input` is not a JSON object
1208
+ */
1209
+ static readonly decode: (input: unknown) => Effect.Effect<LenientManifest, PackageDecodeError, never>;
1210
+ /**
1211
+ * Parse package.json text and decode it leniently. The sync primitive
1212
+ * backing {@link LenientManifest.parse}.
1213
+ *
1214
+ * @param text - the package.json source text
1215
+ * @returns the lenient manifest, or a {@link PackageJsonSyntaxError} when
1216
+ * the text is not valid JSON (`"invalid-json"`) or parses to something
1217
+ * other than a JSON object (`"not-an-object"`) — leniency is per-field,
1218
+ * never per-syntax
1219
+ */
1220
+ static parseResult(text: string): Result.Result<LenientManifest, PackageJsonSyntaxError>;
1221
+ /**
1222
+ * Parse package.json text and decode it leniently. The `Effect` form of
1223
+ * {@link LenientManifest.parseResult}, adding the tracing span.
1224
+ *
1225
+ * @param text - the package.json source text
1226
+ * @returns an Effect resolving to the decoded {@link LenientManifest}
1227
+ * @throws (typed) `PackageJsonSyntaxError` when the text is not valid JSON
1228
+ * or is not a JSON object
1229
+ */
1230
+ static readonly parse: (text: string) => Effect.Effect<LenientManifest, PackageJsonSyntaxError, never>;
1231
+ /** Whether the manifest is marked private. */
1232
+ get isPrivate(): boolean;
1233
+ /** Whether the manifest declares ESM (`"type": "module"`, exact comparison). */
1234
+ get isESM(): boolean;
1235
+ }
1236
+ //#endregion
963
1237
  //#region src/PackageManagerRange.d.ts
964
1238
  declare const PackageManagerRange_base: Schema.Class<PackageManagerRange, Schema.Struct<{
965
1239
  /** The package-manager name (e.g. `pnpm`). Any lowercase name — the same latitude as {@link PackageManager}, for the same evidence. */
@@ -1115,10 +1389,12 @@ declare const PackageManifest_base: Schema.Class<PackageManifest, Schema.Struct<
1115
1389
  * typed), a present `name` must still satisfy the npm grammar, a present
1116
1390
  * `packageManager` must still parse — though here the version position may be
1117
1391
  * a semver range (`pnpm@^11.20.0`), decoded as {@link PackageManagerRange}.
1118
- * For total tolerance of malformed fields, use the decode-free
1119
- * {@link PackageJsonFormat} text path (or `@effected/npm`'s shape-blind
1120
- * `Manifest`)silently carrying a value the type claims to have validated
1121
- * would be a lie, and silently dropping it would break round-trip fidelity.
1392
+ * For tolerance of malformed fields, use the shape-lenient `LenientManifest`
1393
+ * discovery tier which degrades them to absence, preserved in `rest` and
1394
+ * reported on `issues` — or the decode-free {@link PackageJsonFormat} text
1395
+ * path (or `@effected/npm`'s shape-blind `Manifest`); here, silently carrying
1396
+ * a value the type claims to have validated would be a lie, and silently
1397
+ * dropping it would break round-trip fidelity.
1122
1398
  *
1123
1399
  * The model is deliberately lean — fields, the `rest` catch-all wire codec,
1124
1400
  * {@link PackageManifest.decode} and {@link PackageManifest.toJsonString} —
@@ -1424,5 +1700,5 @@ declare class PackageValidator extends PackageValidator_base {
1424
1700
  }): Layer.Layer<PackageValidator>;
1425
1701
  }
1426
1702
  //#endregion
1427
- 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 };
1703
+ export { BinField, Bugs, Dependency, type DependencyKind, DependencyMapField, type DependencyProtocol, DependencySpecifier, type DependencySpecifierBrand, DevEngine, DevEngineOrArray, type DevEngines, DevEnginesSchema, type EntryPointManifest, ExportsField, InvalidDependencySpecifierError, InvalidPackageNameError, InvalidSpdxLicenseError, JsoncEdit, type JsoncPath, type LenientFieldIssue, LenientManifest, 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 ResolveEntryPointOptions, type RuleFailure, ScopedPackageName, SpdxLicense, StringMapField, type UnresolvedDependency, UnresolvedEntryPointError, UnscopedPackageName, type ValidationRule, defaultRules, isUnresolvedDependency, isValidDependencySpecifier, isValidSpdx, noLocalDepsRule, noUnresolvedDepsRule, resolveEntryPoint };
1428
1704
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Dependency, isUnresolvedDependency } from "./Dependency.js";
2
2
  import { DevEngine, DevEngineOrArray, DevEnginesSchema } from "./DevEngines.js";
3
+ import { UnresolvedEntryPointError, resolveEntryPoint } from "./EntryPoint.js";
3
4
  import { InvalidSpdxLicenseError, SpdxLicense, isValidSpdx } from "./License.js";
4
5
  import { PackageManager } from "./PackageManager.js";
5
6
  import { InvalidPackageNameError, PackageName, ScopedPackageName, UnscopedPackageName } from "./PackageName.js";
@@ -7,6 +8,7 @@ import { Person } from "./Person.js";
7
8
  import { Bugs, Repository } from "./Repository.js";
8
9
  import { BinField, DependencyMapField, ExportsField, Package, PackageDecodeError, PeerDependenciesMetaField, PublishConfigField, RepositoryField, StringMapField } from "./Package.js";
9
10
  import { PackageJsonFormat, PackageJsonModifyError, PackageJsonSyntaxError } from "./PackageJsonFormat.js";
11
+ import { LenientManifest } from "./LenientManifest.js";
10
12
  import { PackageManagerRange } from "./PackageManagerRange.js";
11
13
  import { PackageManifest } from "./PackageManifest.js";
12
14
  import { PackageJsonFile, PackageJsonNotFoundError, PackageJsonParseError, PackageJsonReadError, PackageJsonWriteError } from "./PackageJsonFile.js";
@@ -14,4 +16,4 @@ import { PackageValidationError, PackageValidator, defaultRules, noLocalDepsRule
14
16
  import { JsoncEdit } from "@effected/jsonc";
15
17
  import { DependencySpecifier, InvalidDependencySpecifierError, isValidDependencySpecifier } from "@effected/npm";
16
18
 
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 };
19
+ export { BinField, Bugs, Dependency, DependencyMapField, DependencySpecifier, DevEngine, DevEngineOrArray, DevEnginesSchema, ExportsField, 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, noLocalDepsRule, noUnresolvedDepsRule, resolveEntryPoint };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effected/package-json",
3
- "version": "0.10.2",
3
+ "version": "0.12.0",
4
4
  "private": false,
5
5
  "description": "package.json parsing, editing, validation and file IO as Effect schemas.",
6
6
  "keywords": [
@@ -38,8 +38,8 @@
38
38
  "./package.json": "./package.json"
39
39
  },
40
40
  "dependencies": {
41
- "@effected/jsonc": "^0.7.0",
42
- "@effected/npm": "^0.11.0",
41
+ "@effected/jsonc": "^0.8.0",
42
+ "@effected/npm": "^0.12.0",
43
43
  "@effected/semver": "^0.5.0",
44
44
  "@effected/spdx": "^0.4.0"
45
45
  },
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.58.12"
8
+ "packageVersion": "7.59.0"
9
9
  }
10
10
  ]
11
11
  }