fix: accept decentraland-crypto-fetch 3 as a peer - #52
Conversation
decentraland-bot
left a comment
There was a problem hiding this comment.
Requesting changes for one package-contract issue.
Findings:
- P1 —
package.json:22now advertisesdecentraland-crypto-fetch@^3.0.0as an acceptable required peer, but v3 declaresnode >=22.0.0(package-lock.json:3350). This package does not publish a matchingenginesrequirement and its docs say the peer is only needed foruseNotifications. With npm peer auto-installation, consumers that do not pindecentraland-crypto-fetchcan resolve v3 and fail installs under Node 18/20 +engine-strict, including consumers that do not use notifications. Please either make this peer optional viapeerDependenciesMetaif it is notifications-only, or explicitly raise/document the package’s Node support as part of the contract. - P2 — consumer-facing docs are stale/inconsistent with the new peer range:
README.md:16says>= 2.0.1,docs/useNotifications.md:18andAGENTS.md:5still say^2.0.1. Please update them to the exact supported range and mention the Node 22 requirement for v3. - P2 — the published peer range claims both v2 and v3 compatibility, but CI now only installs/tests v3. A small package-contract check for
decentraland-crypto-fetch@2.0.1would keep the dual-range guarantee from regressing. - Security: no security issues found.
- CI: passing (
audit,build,test).
Reviewed by Jarvis 🤖 · Requested by Lautaro Petaccio (<@U025WCHLMN3>) via Slack
decentraland-bot
left a comment
There was a problem hiding this comment.
Thanks for the updates. The original peer-range conflict is addressed, and CI is green, but the new optional-peer contract still has one blocking package-contract issue.
Findings:
- P1 —
package.jsonnow marksdecentraland-crypto-fetchoptional, but the root entrypoint still statically reaches it throughsrc/index.ts -> src/clients/index.ts -> src/clients/notifications/createNotificationsClient.ts. In ESM, static re-exports are resolved during module linking, so a consumer that imports unrelated hooks from@dcl/hookswithout installing the optional peer can still fail to bundle/start even if it never calls notifications. That makes the new optional-peer contract misleading and can break consumers that rely on the package metadata. Please either keep the peer required, or truly isolate notifications behind a notification-only subpath/dynamic import, and ideally add a smoke test that imports a non-notification hook from a packed install withoutdecentraland-crypto-fetch. - P2 —
package-lock.jsonis out of sync withpackage.json: the root lockfile entry haspeerDependenciesbut not the newpeerDependenciesMetaoptional metadata. Please regenerate/update the lockfile so the published package contract and lockfile match. - P2 —
test/package-contract.test.tsimportssemver, butsemveris not declared as a direct dev dependency. Please add it explicitly (and types if needed), or simplify the test to assert the exact intended range string. - P2 —
docs/useNotifications.mdsays the optional peer “is only installed if you use this hook.” Package managers do not install optional peers based on hook usage; consumers must install the signer themselves when using notifications. Please reword this to “Install this optional peer only in apps that useuseNotifications/createNotificationsClient.” - Security: no secrets, auth bypass, or signed-fetch security issue found in the changed code/dependency contract.
- CI: passing (
audit,build,test).
Reviewed by Jarvis 🤖 · Requested by Lautaro Petaccio (<@U025WCHLMN3>) via Slack
|
All four addressed. P1 — the optional contract is now realYou're right, and the diagnosis was exact: Fixed by deferring the value import, not by adding a subpath — that keeps
Guarded, and the guard has a guard
Two companion assertions keep it honest:
That third one earned its place immediately: it caught my walker resolving relative specifiers against the wrong directory, silently truncating the graph to one hop. Without it I'd have shipped a guard that passed because it inspected almost nothing. Counterfactual: restoring the static import fails the first assertion and names the offending module. On the packed-install smoke testI ran it, and it surfaced something worth knowing. With the tarball installed and no So a raw-Node smoke test would assert the wrong thing. I verified the artifact property directly instead: in the packed, peer-less install there is no static edge to P2 — lockfile out of syncRegenerated. The root entry now records P2 — undeclared
|
decentraland-bot
left a comment
There was a problem hiding this comment.
Re-review complete. The previous blocking optional-peer issue is addressed: decentraland-crypto-fetch is now an optional peer, the runtime value import is deferred until notification methods are called, and the new tests guard both the package contract and the root static import graph.
Findings:
- P2 —
README.md:16still says the optional peer is only needed foruseNotifications, but the publiccreateNotificationsClientexport also needs it when used directly. Please includecreateNotificationsClientin that sentence for consistency withdocs/useNotifications.md. - P2 — The source graph test is useful but intentionally regex-based and source-only, so it could miss less common static dependency forms (
require(...), TS import-equals, aliased internal imports). Not blocking for this change, but a packed-consumer/bundler smoke test would be stronger if this contract regresses again.
Consumer impact: public API shape is unchanged and the peer range is backward-compatible (^2.0.1 || ^3.0.0), with the missing optional peer now failing only when the notifications client is actually used.
Security: no new security issues found in the changed code or dependency contract.
CI: GitHub checks are passing (audit, build, test). I also verified locally with npm test -- --runInBand (174 passing) and npm run build.
Reviewed by Jarvis 🤖 · Requested by Lautaro Petaccio (<@U025WCHLMN3>) via Slack
5657d54 to
5d63a7f
Compare
The peer was pinned to `^2.0.1`, so a consumer moving to the 3.x signer could satisfy neither range and every install failed with ERESOLVE. That is the whole problem: this package uses two things from it, `signedFetchFactory` and the `AuthIdentity` type, and neither changed across the major. Widened to `>=2.0.0`. A peer range states what this package can work with, not what a consumer should choose -- the consumer picks the version, and pinning a major on their behalf is what caused the failure. Deliberately left REQUIRED rather than optional. Marking it optional would spare consumers who never call `useNotifications` an install they do not need, but it would also be a claim this package cannot honour as written: `src/index.ts` re-exports the notifications client through `./clients`, and ESM resolves static re-exports at link time, so importing any hook resolves the signer whether or not it is used. Making that claim true means deferring the import at runtime, and that is more machinery than the problem justifies. Required is the honest description of what the code does today. The consequence, worth stating because it is not free: npm resolves a peer to the highest satisfying version, so consumers get 3.x, which declares `node >= 22`. Most consumers of this package target Node 18 or 20, and will see EBADENGINE on install -- a warning, or a failure under `engine-strict`. These are browser apps, so nothing breaks at runtime. An app that must stay on 2.x should depend on it directly. The dev dependency moves to 3.x so CI tests against the version npm will actually resolve for a consumer, rather than the one it will not.
5d63a7f to
a7bedae
Compare
The problem
The peer was pinned to
^2.0.1, so a consumer moving to the 3.x signer could satisfy neither range and every install failed withERESOLVE. That's the whole problem this PR exists for.This package uses exactly two things from that dependency —
signedFetchFactoryand theAuthIdentitytype — and neither changed across the major.The change
The entire
package.jsondiff:A peer range states what this package can work with, not what a consumer should choose. The consumer picks the version; pinning a major on their behalf is what caused the failure.
The dev dependency moves to 3.x so CI tests against the version npm will actually resolve for a consumer, rather than the one it won't.
No source changes. The rest of the diff is the lockfile and three doc lines.
Why it stays required, not optional
An earlier revision marked the peer optional and added a runtime deferral to make that claim true. Rolled back — more machinery than the problem justifies.
Worth recording, since "optional" is superficially attractive here:
src/index.tsre-exports the notifications client through./clients, and ESM resolves static re-exports at link time. So importing any hook resolves the signer whether or not it's used. Declaring it optional without deferring that import would advertise a contract the code doesn't honour — and deferring it means a dynamicimport(), a memoised loader, and a module-graph test to keep it honest.Required is the honest description of what the code does today.
The consequence, stated plainly
npm resolves a peer to the highest satisfying version, so consumers get 3.x, which declares
node >= 22. Most consumers of this package target Node 18 or 20:They'll see
EBADENGINEon install — a warning, or a hard failure underengine-strict. These are browser apps, so nothing breaks at runtime; the engines field affects install and CI only.An app that must stay on 2.x should depend on
decentraland-crypto-fetchdirectly rather than relying on this range to hold it back.Checks
14 suites / 190 tests pass.
tsc --noEmitandnpm run buildclean.