Skip to content

npm dedup drops a required dependency edge in a retained peer-only package #37022

Open

Description

@capy-ai

Version: Deno 2.9.5 (stable, release, x86_64-unknown-linux-gnu)
V8: 15.0.245.2-rusty
TypeScript: 6.0.3

Incremental lockfile-only installation can retain a peer-bound npm package while removing one of its required ordinary dependency mappings during version deduplication. This report concerns the generated snapshot's completeness, not a demonstrated runtime execution failure.

Independent public-package reproduction

This fixture was created independently from three public npm packages. No application manifest, application lockfile, application source, private registry, or synthetic package/import is used. Both locks below were generated by Deno, never hand-edited.

Save as package.before.json:

{
  "dependencies": {
    "@tiptap/core": "3.27.4",
    "@tiptap/extension-code-block": "3.27.4",
    "@tiptap/pm": "3.27.4"
  },
  "overrides": {
    "prosemirror-view": "1.42.1"
  }
}

Save as package.after.json:

{
  "dependencies": {
    "@tiptap/core": "3.30.5",
    "@tiptap/extension-code-block": "3.27.4",
    "@tiptap/pm": "3.30.5"
  },
  "overrides": {
    "prosemirror-view": "1.42.3"
  }
}

Save as required-edge.test.ts:

Deno.test("every retained TipTap model records its required view dependency", () => {
  const lock = JSON.parse(Deno.readTextFileSync("deno.lock")) as {
    npm: Record<string, { dependencies?: string[] }>;
  };
  const models = Object.entries(lock.npm).filter(([name]) => name.startsWith("@tiptap/pm@"));
  if (models.length === 0) throw new Error("No TipTap model records found");
  for (const [name, record] of models) {
    if (!record.dependencies?.some((dependency) => /^prosemirror-view(?:@|$)/.test(dependency))) {
      throw new Error(`${name} omits its required prosemirror-view dependency`);
    }
  }
});

In a directory containing those files:

cp package.before.json package.json
deno install --lockfile-only --cached-only --no-remote --node-modules-dir=none --frozen=false --minimum-dependency-age=P7D
cp deno.lock before.lock
deno test --no-config --no-lock --no-npm --no-remote --allow-read=deno.lock required-edge.test.ts

cp package.after.json package.json
deno install --lockfile-only --cached-only --no-remote --node-modules-dir=none --frozen=false --minimum-dependency-age=P7D
cp deno.lock after.lock
deno test --no-config --no-lock --no-npm --no-remote --allow-read=deno.lock required-edge.test.ts

Verified on October 14, 2026 with already-cached public npm metadata. --cached-only requires that cache; a cold-cache run was not verified. The exact generated initial lock is included below so that evolving transitive ranges do not obscure the observed case. No package code or lifecycle script was executed: installation was lockfile-only, and the test only parses JSON.

Expected and actual

Before generation/test: install exits 0; test reports 1 passed | 0 failed.

After generation/test: install exits 0, with peer-version warnings for the retained code-block extension; test reports 0 passed | 1 failed:

Error: @tiptap/pm@3.27.4 omits its required prosemirror-view dependency

The warning is not suppressed. It says the old extension's peers resolve to 3.30.5, but the generated lock still records its older peer context:

@tiptap/extension-code-block@3.27.4_<old peer context>
  -> @tiptap/core@3.27.4_@tiptap+pm@3.27.4
  -> @tiptap/pm@3.27.4

The retained pm 3.27.4 record loses prosemirror-view from dependencies; pm 3.30.5 records it, and the only view record is 1.42.3. Official npm metadata for pm 3.27.4 declares prosemirror-view: ^1.41.9 as a required dependency, not an optional or peer dependency. Its package integrity is unchanged between the two locks.

I expected the required dependency mapping to remain on every retained pm package, or the obsolete peer context to be consistently rebound/removed, rather than retaining a referenced package with that mapping absent.

Replaying the update twice from the same untouched generated before.lock, identical after manifest and the same metadata cache produced identical output:

  • Before lock SHA-256: 8065b9f346fcc445b400339277a9a9a91248e12aefd64ebd886bf12d9ab59ef7
  • Both after outputs SHA-256: 83586dc1efdde1629dfd379692b7df971bc12e4c333b903becfa7d20d8e60ea0

Versioned source trace and runtime limitation

Source references are at Deno v2.9.5 commit 17fadf33a8df3af9488b9f42efd1f2290d6dc7a3:

Exact initial generated lock

This lock belongs only to the independent public fixture above. It contains public npm identities/integrities and no remote/JSR section or application data.

before.lock (generated, unedited)
{
  "version": "5",
  "specifiers": {
    "npm:@tiptap/core@3.27.4": "3.27.4_@tiptap+pm@3.27.4",
    "npm:@tiptap/extension-code-block@3.27.4": "3.27.4_@tiptap+core@3.27.4__@tiptap+pm@3.27.4_@tiptap+pm@3.27.4",
    "npm:@tiptap/pm@3.27.4": "3.27.4"
  },
  "npm": {
    "@tiptap/core@3.27.4_@tiptap+pm@3.27.4": {
      "integrity": "sha512-8W/GwlEn0JwNdpyVfTWcXwHYUpj9BWwO++YxtizmgjJzlwigSh7/xLVJMwVykuQHQ2fCq5rkUvmBRtpHOMLUQA==",
      "dependencies": [
        "@tiptap/pm"
      ]
    },
    "@tiptap/extension-code-block@3.27.4_@tiptap+core@3.27.4__@tiptap+pm@3.27.4_@tiptap+pm@3.27.4": {
      "integrity": "sha512-a5caWfWN6Z6usy48vzJDDOhWoA6+rFFCHGpQM7jXn/7rRzYPcvBzTZUGptjEbltj4YqtrQ2tVwTJcCtbb+mknA==",
      "dependencies": [
        "@tiptap/core",
        "@tiptap/pm"
      ]
    },
    "@tiptap/pm@3.27.4": {
      "integrity": "sha512-UB8lcyomfWk7YGI2PZKNqcYXfyRA+PFj+QntlsUXyrsiA5JJIaE8SHKYjxKlGG/xtW3EtPm1b0p38T9Mk4xiFw==",
      "dependencies": [
        "prosemirror-changeset",
        "prosemirror-commands",
        "prosemirror-dropcursor",
        "prosemirror-gapcursor",
        "prosemirror-history",
        "prosemirror-inputrules",
        "prosemirror-keymap",
        "prosemirror-model",
        "prosemirror-schema-list",
        "prosemirror-state",
        "prosemirror-tables",
        "prosemirror-transform",
        "prosemirror-view"
      ]
    },
    "orderedmap@2.1.1": {
      "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="
    },
    "prosemirror-changeset@2.4.4": {
      "integrity": "sha512-Gyuz9jYdrAeA9I7AholyOes9KUmv/lEwsC5O9s5xF5VB3EgTPsXMGUxz4+H1B+wKVPdZbvRcg1Cp3x8qX+8ofA==",
      "dependencies": [
        "prosemirror-transform"
      ]
    },
    "prosemirror-commands@1.7.2": {
      "integrity": "sha512-q6Q6szxqdu9Xd6EcdKsqXghu5nQdZTpB4Q9yd04WRc7/jt763e/rT60Owh0L1GYY+T46o5rD+9lEN36dZS43tw==",
      "dependencies": [
        "prosemirror-model",
        "prosemirror-state",
        "prosemirror-transform"
      ]
    },
    "prosemirror-dropcursor@1.8.4": {
      "integrity": "sha512-VmKxhcA6a+h5oPSYmfF1ZNeQqPbyYaIxqZsKqeYCyTlPtrjUgNaHL3P8vVaO19oqAV0zuaM/axkowZHrB1P3Tg==",
      "dependencies": [
        "prosemirror-state",
        "prosemirror-transform",
        "prosemirror-view"
      ]
    },
    "prosemirror-gapcursor@1.4.1": {
      "integrity": "sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==",
      "dependencies": [
        "prosemirror-keymap",
        "prosemirror-model",
        "prosemirror-state",
        "prosemirror-view"
      ]
    },
    "prosemirror-history@1.5.1": {
      "integrity": "sha512-onlwnqKvgtFg6srZOmDOAhlAy1FIHQpOExORybX0S+DvJepE5tPALrB9iGet/2sqqqrelSnB10OoiOJqAvu2DA==",
      "dependencies": [
        "prosemirror-state",
        "prosemirror-transform",
        "prosemirror-view",
        "rope-sequence"
      ]
    },
    "prosemirror-inputrules@1.5.1": {
      "integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==",
      "dependencies": [
        "prosemirror-state",
        "prosemirror-transform"
      ]
    },
    "prosemirror-keymap@1.2.3": {
      "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==",
      "dependencies": [
        "prosemirror-state",
        "w3c-keyname"
      ]
    },
    "prosemirror-model@1.25.12": {
      "integrity": "sha512-Ue2gTmXMa7EhpLNhC7J+h4+ykD8ha12K6rrZFFKKJHBForfIStw5gJ6Zrf1mqqAa9NmpmEB4wp9bKmA4eUVjWg==",
      "dependencies": [
        "orderedmap"
      ]
    },
    "prosemirror-schema-list@1.5.1": {
      "integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==",
      "dependencies": [
        "prosemirror-model",
        "prosemirror-state",
        "prosemirror-transform"
      ]
    },
    "prosemirror-state@1.4.4": {
      "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
      "dependencies": [
        "prosemirror-model",
        "prosemirror-transform",
        "prosemirror-view"
      ]
    },
    "prosemirror-tables@1.8.5": {
      "integrity": "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==",
      "dependencies": [
        "prosemirror-keymap",
        "prosemirror-model",
        "prosemirror-state",
        "prosemirror-transform",
        "prosemirror-view"
      ]
    },
    "prosemirror-transform@1.12.2": {
      "integrity": "sha512-PE/aY0HEY4zczvmqrilgkUK/WautF0chvMqkmM/iN5/aPHvwrCWaQZjS6CIZp+K84YrvPFqrL+YArrfJX8xX7g==",
      "dependencies": [
        "prosemirror-model"
      ]
    },
    "prosemirror-view@1.42.1": {
      "integrity": "sha512-rRqzZnRgkyh69XoOMrfFJHwauHscLBmHbq772kwbic1ymQAM8gXjzEbJse5j1ep2UO2HRIAQL0bY3kZ/RoqjVw==",
      "dependencies": [
        "prosemirror-model",
        "prosemirror-state",
        "prosemirror-transform"
      ]
    },
    "rope-sequence@1.3.4": {
      "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="
    },
    "w3c-keyname@2.2.8": {
      "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
    }
  },
  "workspace": {
    "packageJson": {
      "dependencies": [
        "npm:@tiptap/core@3.27.4",
        "npm:@tiptap/extension-code-block@3.27.4",
        "npm:@tiptap/pm@3.27.4"
      ],
      "overrides": {
        "prosemirror-view": "1.42.1"
      }
    }
  }
}

Activity

  1. CloudCorpRecords commented on Oct 17, 2026

    @CloudCorpRecords

    Hi, I'm CloudCorpRecords. I'd like to take this on: npm dedup drops a required dependency edge in a retained peer-only package.

    I'm commenting first per the repo's contribution guidelines, and I'll follow the contribution process. I'll open a PR against the issue once the work is ready.

    eta-days: 4
    Claimed under the Greenlight Claim Protocol: this claim renews or expires
    automatically, and releases the moment a PR is linked or I say so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions