@effected/lockfiles 0.6.2 → 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/Lockfile.js +28 -0
- package/README.md +8 -3
- package/index.d.ts +19 -0
- package/package.json +3 -3
- package/tsdoc-metadata.json +1 -1
package/Lockfile.js
CHANGED
|
@@ -128,6 +128,8 @@ var Lockfile = class Lockfile extends Schema.Class("Lockfile")({
|
|
|
128
128
|
#nameIndex;
|
|
129
129
|
/** Lazily built importer-path → importer index; deliberately outside the schema, never encodes. */
|
|
130
130
|
#importerIndex;
|
|
131
|
+
/** Lazily built instance-id → package index; deliberately outside the schema, never encodes. */
|
|
132
|
+
#instanceIndex;
|
|
131
133
|
/**
|
|
132
134
|
* Parse lockfile content of a known format into the unified model — the
|
|
133
135
|
* package's only fallible boundary.
|
|
@@ -253,6 +255,32 @@ var Lockfile = class Lockfile extends Schema.Class("Lockfile")({
|
|
|
253
255
|
}
|
|
254
256
|
return Option.fromUndefinedOr(this.#importerIndex.get(path));
|
|
255
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
* The resolved package with the given instance id, or `Option.none()` when
|
|
260
|
+
* the lockfile records none. Backed by a lazily built index, so repeated
|
|
261
|
+
* lookups are O(1) and a consumer that never walks edges pays nothing.
|
|
262
|
+
*
|
|
263
|
+
* @remarks
|
|
264
|
+
* `ResolvedPackage.instanceId` is what a resolved edge points at, so peer and
|
|
265
|
+
* dependency resolution is a lookup through this index rather than a scan.
|
|
266
|
+
* Without it every consumer rebuilds the same map — `new Map(lockfile.packages.map((p) => [p.instanceId, p]))` —
|
|
267
|
+
* which is O(n) per walk and one more place for the key choice to drift.
|
|
268
|
+
*
|
|
269
|
+
* The index is a `Map`, so an instance id that collides with an `Object`
|
|
270
|
+
* member name (`__proto__`, `constructor`) neither pollutes nor
|
|
271
|
+
* false-matches, exactly as for {@link Lockfile.importer}.
|
|
272
|
+
*
|
|
273
|
+
* @param instanceId - The instance id to look up.
|
|
274
|
+
* @returns The matching {@link ResolvedPackage}, or `Option.none()`.
|
|
275
|
+
*/
|
|
276
|
+
packageByInstanceId(instanceId) {
|
|
277
|
+
if (this.#instanceIndex === void 0) {
|
|
278
|
+
const index = /* @__PURE__ */ new Map();
|
|
279
|
+
for (const pkg of this.packages) if (!index.has(pkg.instanceId)) index.set(pkg.instanceId, pkg);
|
|
280
|
+
this.#instanceIndex = index;
|
|
281
|
+
}
|
|
282
|
+
return Option.fromUndefinedOr(this.#instanceIndex.get(instanceId));
|
|
283
|
+
}
|
|
256
284
|
/** The workspace-local packages. */
|
|
257
285
|
get workspacePackages() {
|
|
258
286
|
return this.packages.filter((pkg) => pkg.isWorkspace);
|
package/README.md
CHANGED
|
@@ -135,19 +135,24 @@ A row in `lockfile.packages` is one package **instance**, not one package. A dep
|
|
|
135
135
|
`resolved` maps each dependency name — and each peer name the lockfile records a resolution for — to the `instanceId` it actually resolved to, which is what lets a consumer walk the real graph rather than re-resolving ranges itself. `peerDependencies` and `peerDependenciesMeta` carry the declarations as written, defaulting to `{}`.
|
|
136
136
|
|
|
137
137
|
```ts
|
|
138
|
+
import { Option } from "effect";
|
|
139
|
+
|
|
138
140
|
const [instance] = lockfile.packagesNamed("react-dom");
|
|
139
141
|
const reactId = instance?.resolved["react"];
|
|
140
|
-
const react =
|
|
141
|
-
//
|
|
142
|
+
const react = reactId === undefined ? Option.none() : lockfile.packageByInstanceId(reactId);
|
|
143
|
+
// Option.some(ResolvedPackage) — the version this particular react-dom instance resolved to
|
|
144
|
+
// Option.none() when the lockfile records no row under that id
|
|
142
145
|
```
|
|
143
146
|
|
|
147
|
+
`packageByInstanceId` is the lookup to reach for rather than a scan over `lockfile.packages`: it builds its index lazily on first call and reuses it, so walking a graph edge by edge stays linear instead of quadratic. A repeated id resolves to the first row, so a malformed lockfile gives a stable answer rather than an iteration-order one.
|
|
148
|
+
|
|
144
149
|
`unresolvedEdges` names the edges the lockfile **records** but the model could not name. A name listed there is not absent — it is unanswered, so a missing `resolved` key must not be read as "nothing is there". A gate that cares about completeness checks that array before trusting an empty result.
|
|
145
150
|
|
|
146
151
|
## Features
|
|
147
152
|
|
|
148
153
|
- `Lockfile.parse(content, { format })` — the package's only fallible boundary; fails with `LockfileParseError` or `LockfileFramingError`.
|
|
149
154
|
- `Lockfile#withImporterNames(names)` — the pure second stage for pnpm: rewrites importer-path names and both ends of each dependency edge once the consumer has read the manifests.
|
|
150
|
-
- `Lockfile#packagesNamed(name)` and `Lockfile#workspacePackages` — total lookups over the model.
|
|
155
|
+
- `Lockfile#packagesNamed(name)`, `Lockfile#packageByInstanceId(id)` (`Option`-answering, over a lazily built index) and `Lockfile#workspacePackages` — total lookups over the model.
|
|
151
156
|
- `LockfileFormat` with `filenameFor` / `fromFilename` — the format literal and its mapping to lockfile filenames.
|
|
152
157
|
- `LockfileIntegrity.compare(lockfile, manifests)` — total, pure integrity checking with no error channel; the report carries `valid`, `missingWorkspaces`, `extraWorkspaces` and `unsatisfiedConstraints`. Constraint checking is best-effort by design: `workspace:` / `link:` / `file:` specifiers and rows whose range does not parse as SemVer are skipped.
|
|
153
158
|
- `WorkspaceManifest` — the manifest input shape for integrity checking: a package name plus four optional dependency records. Deliberately a plain value rather than a strict manifest model, so consumers can derive it from anything.
|
package/index.d.ts
CHANGED
|
@@ -468,6 +468,25 @@ declare class Lockfile extends Lockfile_base {
|
|
|
468
468
|
* @returns The matching {@link LockfileImporter}, or `Option.none()`.
|
|
469
469
|
*/
|
|
470
470
|
importer(path: string): Option.Option<LockfileImporter>;
|
|
471
|
+
/**
|
|
472
|
+
* The resolved package with the given instance id, or `Option.none()` when
|
|
473
|
+
* the lockfile records none. Backed by a lazily built index, so repeated
|
|
474
|
+
* lookups are O(1) and a consumer that never walks edges pays nothing.
|
|
475
|
+
*
|
|
476
|
+
* @remarks
|
|
477
|
+
* `ResolvedPackage.instanceId` is what a resolved edge points at, so peer and
|
|
478
|
+
* dependency resolution is a lookup through this index rather than a scan.
|
|
479
|
+
* Without it every consumer rebuilds the same map — `new Map(lockfile.packages.map((p) => [p.instanceId, p]))` —
|
|
480
|
+
* which is O(n) per walk and one more place for the key choice to drift.
|
|
481
|
+
*
|
|
482
|
+
* The index is a `Map`, so an instance id that collides with an `Object`
|
|
483
|
+
* member name (`__proto__`, `constructor`) neither pollutes nor
|
|
484
|
+
* false-matches, exactly as for {@link Lockfile.importer}.
|
|
485
|
+
*
|
|
486
|
+
* @param instanceId - The instance id to look up.
|
|
487
|
+
* @returns The matching {@link ResolvedPackage}, or `Option.none()`.
|
|
488
|
+
*/
|
|
489
|
+
packageByInstanceId(instanceId: string): Option.Option<ResolvedPackage>;
|
|
471
490
|
/** The workspace-local packages. */
|
|
472
491
|
get workspacePackages(): ReadonlyArray<ResolvedPackage>;
|
|
473
492
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/lockfiles",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Pure lockfile parsing for bun, npm, pnpm and yarn Berry into one unified Effect schema model, with pure integrity checking against workspace manifests.",
|
|
6
6
|
"keywords": [
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@effected/jsonc": "^0.7.0",
|
|
44
|
-
"@effected/npm": "^0.
|
|
44
|
+
"@effected/npm": "^0.12.0",
|
|
45
45
|
"@effected/semver": "^0.5.0",
|
|
46
|
-
"@effected/yaml": "^0.
|
|
46
|
+
"@effected/yaml": "^0.11.0",
|
|
47
47
|
"effect": "4.0.0-rc.109"
|
|
48
48
|
},
|
|
49
49
|
"engines": {
|