@effected/package-json 0.6.1 → 0.7.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/Package.js +2 -1
- package/PackageManager.js +80 -26
- package/index.d.ts +58 -3
- package/package.json +3 -3
package/Package.js
CHANGED
|
@@ -331,7 +331,8 @@ var Package = class Package extends Schema.Class("Package")({
|
|
|
331
331
|
* sorting and empty-map stripping unless the options opt out. Pure.
|
|
332
332
|
*/
|
|
333
333
|
toJsonString(options) {
|
|
334
|
-
|
|
334
|
+
const raw = Schema.encodeUnknownSync(Package.schema)(this);
|
|
335
|
+
return renderJson(raw, resolveFormatOptions(options));
|
|
335
336
|
}
|
|
336
337
|
};
|
|
337
338
|
|
package/PackageManager.js
CHANGED
|
@@ -1,50 +1,104 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CorepackIntegrityHash } from "@effected/npm";
|
|
2
2
|
import { Effect, Exit, Option, Schema, SchemaIssue, SchemaTransformation } from "effect";
|
|
3
|
+
import { SemVer } from "@effected/semver";
|
|
3
4
|
|
|
4
5
|
//#region src/PackageManager.ts
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
* The `packageManager` field only ever carries corepack's `<algo>.<hex>`
|
|
8
|
-
* integrity form (the `name@version+sha512.<hex>` tail). Restrict the
|
|
9
|
-
* `@effected/npm` `IntegrityHash` brand — which also admits the SRI and yarn
|
|
10
|
-
* forms — to just the corepack shape, so an SRI or yarn integrity here fails
|
|
11
|
-
* typed rather than being accepted into a field that can never legitimately
|
|
12
|
-
* hold it.
|
|
13
|
-
*/
|
|
14
|
-
const CorepackIntegrity = IntegrityHash.pipe(Schema.check(Schema.makeFilter((value) => IntegrityHash.isCorepack(value) ? void 0 : "Expected a corepack (<algo>.<hex>) integrity hash")));
|
|
6
|
+
const PACKAGE_MANAGER_NAME_RE = /^[a-z]+$/;
|
|
7
|
+
const invalid = (input, message) => Effect.fail(new SchemaIssue.InvalidValue(Option.some(input), { message }));
|
|
15
8
|
/**
|
|
16
9
|
* A structured `packageManager` value with `name`, `version` and an optional
|
|
17
10
|
* `integrity` hash.
|
|
18
11
|
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* The same `<name>@<version>[+<integrity>]` triple `@effected/npm`'s
|
|
14
|
+
* `PackageManagerPin` models, in its `package.json` field form. Both share the
|
|
15
|
+
* strict pieces — the version is `@effected/semver`'s
|
|
16
|
+
* `SemVer.PinnableVersionString` (decode rules through `SemVer.isPinnable`),
|
|
17
|
+
* the integrity is npm's `CorepackIntegrityHash` — and
|
|
18
|
+
* both apply the first-`+`-is-integrity rule. Reach for the pin when
|
|
19
|
+
* provisioning a package manager; reach for this class when reading or writing
|
|
20
|
+
* the manifest field.
|
|
21
|
+
*
|
|
22
|
+
* **The one deliberate divergence is the name grammar**, and it points this
|
|
23
|
+
* way: the pin closes the set to the four managers the kit can provision
|
|
24
|
+
* (`npm | pnpm | yarn | bun`), while this field model accepts any lowercase
|
|
25
|
+
* name. The evidence:
|
|
26
|
+
*
|
|
27
|
+
* - Corepack 0.34.0 (`specUtils.ts`, `parseSpec`) recognises **three** names —
|
|
28
|
+
* `npm`, `pnpm`, `yarn` — and throws an "unsupported package manager
|
|
29
|
+
* specification" usage error for any other. Adopting that set here would reject
|
|
30
|
+
* `bun@1.2.20`, which is real: six published packages in this repo's own
|
|
31
|
+
* `node_modules` carry exactly that value, and a manifest model that cannot
|
|
32
|
+
* read them is useless for the job it has.
|
|
33
|
+
* - Corepack does not treat the set as closed either. `parseSpec` skips the
|
|
34
|
+
* name check entirely when the spec is a URL, so a custom name is reachable
|
|
35
|
+
* in corepack's own grammar (behind `COREPACK_ENABLE_UNSAFE_CUSTOM_URLS`).
|
|
36
|
+
* - npm documents no constraint on this field at all. Its `package.json`
|
|
37
|
+
* reference constrains only `devEngines.packageManager.name` — a different
|
|
38
|
+
* field, modeled here by `DevEngine` and out of scope for this class.
|
|
39
|
+
*
|
|
40
|
+
* So: field model = manifests as they exist in the wild; pin = the kit's
|
|
41
|
+
* provisioning vocabulary. A name outside the pin's four is representable here
|
|
42
|
+
* and simply will not be installable through the pin — which is the honest
|
|
43
|
+
* relationship between a document model and a provisioning contract.
|
|
44
|
+
*
|
|
19
45
|
* @public
|
|
20
46
|
*/
|
|
21
47
|
var PackageManager = class PackageManager extends Schema.Class("PackageManager")({
|
|
22
|
-
/** The package-manager name (e.g. `pnpm`). */
|
|
48
|
+
/** The package-manager name (e.g. `pnpm`). Any lowercase name — see the class remarks. */
|
|
23
49
|
name: Schema.String,
|
|
24
|
-
/**
|
|
25
|
-
version:
|
|
26
|
-
|
|
27
|
-
|
|
50
|
+
/**
|
|
51
|
+
* The version (e.g. `10.33.0`): `@effected/semver`'s
|
|
52
|
+
* `SemVer.PinnableVersionString` — an exact SemVer 2.0.0 version with no
|
|
53
|
+
* build metadata and no surrounding whitespace. Prerelease versions are
|
|
54
|
+
* allowed (`10.0.0-rc.1`); ranges, partial versions, dist-tags,
|
|
55
|
+
* leading-zero components and padded values are not, and a version
|
|
56
|
+
* carrying build metadata is rejected at construction because the grammar
|
|
57
|
+
* cannot express it. The shared schema is consumed by identity, not
|
|
58
|
+
* copied — the suite asserts `fields.version === SemVer.PinnableVersionString`.
|
|
59
|
+
*/
|
|
60
|
+
version: SemVer.PinnableVersionString,
|
|
61
|
+
/**
|
|
62
|
+
* The optional integrity hash (e.g. `sha512.abc`): `@effected/npm`'s
|
|
63
|
+
* `CorepackIntegrityHash`, the shared restriction of the `IntegrityHash`
|
|
64
|
+
* brand to the corepack `<algo>.<hex>` form.
|
|
65
|
+
*/
|
|
66
|
+
integrity: Schema.Option(CorepackIntegrityHash)
|
|
28
67
|
}) {
|
|
29
68
|
/**
|
|
30
69
|
* Schema transformation between the `"name@version+integrity"` string and a
|
|
31
70
|
* {@link PackageManager}.
|
|
71
|
+
*
|
|
72
|
+
* @remarks
|
|
73
|
+
* Decoding splits on the first `@`, then on the first `+` — which always
|
|
74
|
+
* begins the integrity, never semver build metadata — and validates each
|
|
75
|
+
* component: the name against the lowercase grammar, the version through
|
|
76
|
+
* `@effected/semver`'s strict parse, the integrity through
|
|
77
|
+
* `CorepackIntegrityHash`. Every failure is a typed decode failure naming
|
|
78
|
+
* the component that failed. Encoding prints the canonical string, which is
|
|
79
|
+
* byte-identical to any input this codec accepts.
|
|
32
80
|
*/
|
|
33
81
|
static FromString = Schema.String.pipe(Schema.decodeTo(Schema.instanceOf(PackageManager), SchemaTransformation.transformOrFail({
|
|
34
82
|
decode: (input) => {
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
83
|
+
const at = input.indexOf("@");
|
|
84
|
+
if (at === -1) return invalid(input, `Invalid packageManager format: "${input}"`);
|
|
85
|
+
const name = input.slice(0, at);
|
|
86
|
+
if (!PACKAGE_MANAGER_NAME_RE.test(name)) return invalid(input, `Invalid packageManager name: "${name}"`);
|
|
87
|
+
const rest = input.slice(at + 1);
|
|
88
|
+
const plus = rest.indexOf("+");
|
|
89
|
+
const version = plus === -1 ? rest : rest.slice(0, plus);
|
|
90
|
+
if (!SemVer.isPinnable(version)) return invalid(input, `Invalid packageManager version: "${version}"`);
|
|
91
|
+
if (plus === -1) return Effect.succeed(PackageManager.make({
|
|
92
|
+
name,
|
|
93
|
+
version,
|
|
41
94
|
integrity: Option.none()
|
|
42
95
|
}));
|
|
43
|
-
const
|
|
44
|
-
|
|
96
|
+
const rawIntegrity = rest.slice(plus + 1);
|
|
97
|
+
const decoded = Schema.decodeUnknownExit(CorepackIntegrityHash)(rawIntegrity);
|
|
98
|
+
if (Exit.isFailure(decoded)) return invalid(input, `Invalid packageManager integrity: "${rawIntegrity}"`);
|
|
45
99
|
return Effect.succeed(PackageManager.make({
|
|
46
|
-
name
|
|
47
|
-
version
|
|
100
|
+
name,
|
|
101
|
+
version,
|
|
48
102
|
integrity: Option.some(decoded.value)
|
|
49
103
|
}));
|
|
50
104
|
},
|
package/index.d.ts
CHANGED
|
@@ -144,23 +144,78 @@ type SpdxLicense = string & Brand.Brand<"SpdxLicense">;
|
|
|
144
144
|
//#endregion
|
|
145
145
|
//#region src/PackageManager.d.ts
|
|
146
146
|
declare const PackageManager_base: Schema.Class<PackageManager, Schema.Struct<{
|
|
147
|
-
/** The package-manager name (e.g. `pnpm`). */
|
|
147
|
+
/** The package-manager name (e.g. `pnpm`). Any lowercase name — see the class remarks. */
|
|
148
148
|
readonly name: Schema.String;
|
|
149
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* The version (e.g. `10.33.0`): `@effected/semver`'s
|
|
151
|
+
* `SemVer.PinnableVersionString` — an exact SemVer 2.0.0 version with no
|
|
152
|
+
* build metadata and no surrounding whitespace. Prerelease versions are
|
|
153
|
+
* allowed (`10.0.0-rc.1`); ranges, partial versions, dist-tags,
|
|
154
|
+
* leading-zero components and padded values are not, and a version
|
|
155
|
+
* carrying build metadata is rejected at construction because the grammar
|
|
156
|
+
* cannot express it. The shared schema is consumed by identity, not
|
|
157
|
+
* copied — the suite asserts `fields.version === SemVer.PinnableVersionString`.
|
|
158
|
+
*/
|
|
150
159
|
readonly version: Schema.String;
|
|
151
|
-
/**
|
|
160
|
+
/**
|
|
161
|
+
* The optional integrity hash (e.g. `sha512.abc`): `@effected/npm`'s
|
|
162
|
+
* `CorepackIntegrityHash`, the shared restriction of the `IntegrityHash`
|
|
163
|
+
* brand to the corepack `<algo>.<hex>` form.
|
|
164
|
+
*/
|
|
152
165
|
readonly integrity: Schema.Option<Schema.brand<Schema.String, "IntegrityHash">>;
|
|
153
166
|
}>, {}>;
|
|
154
167
|
/**
|
|
155
168
|
* A structured `packageManager` value with `name`, `version` and an optional
|
|
156
169
|
* `integrity` hash.
|
|
157
170
|
*
|
|
171
|
+
* @remarks
|
|
172
|
+
* The same `<name>@<version>[+<integrity>]` triple `@effected/npm`'s
|
|
173
|
+
* `PackageManagerPin` models, in its `package.json` field form. Both share the
|
|
174
|
+
* strict pieces — the version is `@effected/semver`'s
|
|
175
|
+
* `SemVer.PinnableVersionString` (decode rules through `SemVer.isPinnable`),
|
|
176
|
+
* the integrity is npm's `CorepackIntegrityHash` — and
|
|
177
|
+
* both apply the first-`+`-is-integrity rule. Reach for the pin when
|
|
178
|
+
* provisioning a package manager; reach for this class when reading or writing
|
|
179
|
+
* the manifest field.
|
|
180
|
+
*
|
|
181
|
+
* **The one deliberate divergence is the name grammar**, and it points this
|
|
182
|
+
* way: the pin closes the set to the four managers the kit can provision
|
|
183
|
+
* (`npm | pnpm | yarn | bun`), while this field model accepts any lowercase
|
|
184
|
+
* name. The evidence:
|
|
185
|
+
*
|
|
186
|
+
* - Corepack 0.34.0 (`specUtils.ts`, `parseSpec`) recognises **three** names —
|
|
187
|
+
* `npm`, `pnpm`, `yarn` — and throws an "unsupported package manager
|
|
188
|
+
* specification" usage error for any other. Adopting that set here would reject
|
|
189
|
+
* `bun@1.2.20`, which is real: six published packages in this repo's own
|
|
190
|
+
* `node_modules` carry exactly that value, and a manifest model that cannot
|
|
191
|
+
* read them is useless for the job it has.
|
|
192
|
+
* - Corepack does not treat the set as closed either. `parseSpec` skips the
|
|
193
|
+
* name check entirely when the spec is a URL, so a custom name is reachable
|
|
194
|
+
* in corepack's own grammar (behind `COREPACK_ENABLE_UNSAFE_CUSTOM_URLS`).
|
|
195
|
+
* - npm documents no constraint on this field at all. Its `package.json`
|
|
196
|
+
* reference constrains only `devEngines.packageManager.name` — a different
|
|
197
|
+
* field, modeled here by `DevEngine` and out of scope for this class.
|
|
198
|
+
*
|
|
199
|
+
* So: field model = manifests as they exist in the wild; pin = the kit's
|
|
200
|
+
* provisioning vocabulary. A name outside the pin's four is representable here
|
|
201
|
+
* and simply will not be installable through the pin — which is the honest
|
|
202
|
+
* relationship between a document model and a provisioning contract.
|
|
203
|
+
*
|
|
158
204
|
* @public
|
|
159
205
|
*/
|
|
160
206
|
declare class PackageManager extends PackageManager_base {
|
|
161
207
|
/**
|
|
162
208
|
* Schema transformation between the `"name@version+integrity"` string and a
|
|
163
209
|
* {@link PackageManager}.
|
|
210
|
+
*
|
|
211
|
+
* @remarks
|
|
212
|
+
* Decoding splits on the first `@`, then on the first `+` — which always
|
|
213
|
+
* begins the integrity, never semver build metadata — and validates each
|
|
214
|
+
* component: the name against the lowercase grammar, the version through
|
|
215
|
+
* `@effected/semver`'s strict parse, the integrity through
|
|
216
|
+
* `CorepackIntegrityHash`. Every failure is a typed decode failure naming
|
|
217
|
+
* the component that failed. Encoding prints the canonical string, which is
|
|
218
|
+
* byte-identical to any input this codec accepts.
|
|
164
219
|
*/
|
|
165
220
|
static readonly FromString: Schema.Codec<PackageManager, string>;
|
|
166
221
|
/** Whether an integrity hash is present. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/package-json",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.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/npm": "~0.
|
|
42
|
-
"@effected/semver": "~0.
|
|
41
|
+
"@effected/npm": "~0.7.0",
|
|
42
|
+
"@effected/semver": "~0.3.0",
|
|
43
43
|
"@effected/spdx": "~0.1.1"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|