@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 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
- return renderJson(Schema.encodeUnknownSync(Package.schema)(this), resolveFormatOptions(options));
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 { IntegrityHash } from "@effected/npm";
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 PACKAGE_MANAGER_RE = /^([a-z]+)@(\d+\.\d+\.\d+(?:-[a-zA-Z0-9._-]+)?)(?:\+(.+))?$/;
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
- /** The version (e.g. `10.33.0`). */
25
- version: Schema.String,
26
- /** The optional integrity hash (e.g. `sha512.abc`), an `@effected/npm` `IntegrityHash` restricted to the corepack `<algo>.<hex>` form. */
27
- integrity: Schema.Option(CorepackIntegrity)
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 match = input.match(PACKAGE_MANAGER_RE);
36
- if (match === null) return Effect.fail(new SchemaIssue.InvalidValue(Option.some(input), { message: `Invalid packageManager format: "${input}"` }));
37
- const rawIntegrity = match[3];
38
- if (rawIntegrity === void 0) return Effect.succeed(PackageManager.make({
39
- name: match[1],
40
- version: match[2],
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 decoded = Schema.decodeUnknownExit(CorepackIntegrity)(rawIntegrity);
44
- if (Exit.isFailure(decoded)) return Effect.fail(new SchemaIssue.InvalidValue(Option.some(input), { message: `Invalid packageManager integrity: "${rawIntegrity}"` }));
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: match[1],
47
- version: match[2],
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
- /** The version (e.g. `10.33.0`). */
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
- /** The optional integrity hash (e.g. `sha512.abc`), an `@effected/npm` `IntegrityHash` restricted to the corepack `<algo>.<hex>` form. */
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.6.1",
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.6.0",
42
- "@effected/semver": "~0.2.1",
41
+ "@effected/npm": "~0.7.0",
42
+ "@effected/semver": "~0.3.0",
43
43
  "@effected/spdx": "~0.1.1"
44
44
  },
45
45
  "peerDependencies": {