Skip to content

Dependency resolution

A package install touches two distinct dependency surfaces:

  1. Runtime npm dependenciespackage.json:dependencies and optionalDependencies. Resolved via npm install from the configured registry.
  2. Install lifecycle hooks — Matrix-specific scripts the package declares in matrix.json:install.<phase>. Run after the copy completes.

Runtime npm dependencies

Source: installRuntimeNpmDependencies in packages/mx-cli/src/commands/install.ts:217-291.

For tarball-source installs only (not directory-source), the CLI:

  1. Reads package.json:dependencies and optionalDependencies.
  2. If both are absent or empty, skips the step entirely.
  3. Otherwise rewrites the staged package.json into a private wrapper:
    json
    {
      "private": true,
      "name": "mx-runtime-deps-<sanitized-package-name>",
      "version": "0.0.0",
      "dependencies": { ... },
      "optionalDependencies": { ... }
    }
  4. Writes a temporary .npmrc at <tempDir>/.npmrc carrying:
    • The configured registry as default and as the @open-matrix: scope override.
    • The _authToken if a credential is found.
  5. Runs:
    npm install \
      --omit=dev \
      --omit=peer \
      --legacy-peer-deps \
      --no-audit \
      --fund=false \
      --package-lock=false \
      --userconfig <tempDir>/.npmrc
  6. Restores the original package.json after npm install completes.
  7. Records a dependencies entry in the install's lifecyclePhases list.

If npm install fails, the entire install fails and the package is not promoted out of staging.

Why not run this on directory-source installs? Because directory installs typically come from a workspace where the dependencies are already resolved by the workspace package manager (pnpm in this repo). Running npm install again would be redundant and slow.

Install lifecycle hooks

matrix.json:install.<phase>.script declares a JS file the installer runs after the package is copied into place. Validator: packages/mx-cli/src/utils/manifestValidator.ts:44-67.

Phase ordering (install.ts:302-339):

validate → migrate → seed → verify

Each phase is run separately. A non-zero exit code aborts the install and triggers backup restoration.

Each hook receives an environment with these variables set:

Env varMeaning
MATRIX_PACKAGES_DIRThe package store root (<MATRIX_HOME>/packages/...)
MATRIX_PACKAGE_DIRThe directory the script's package was just installed into
MATRIX_PACKAGE_NAMEThe package name
MATRIX_PACKAGE_VERSIONThe package version
MATRIX_INSTALL_SOURCEThe original source string passed to mx install
MATRIX_INSTALL_PHASEThe phase being run (validate/migrate/seed/verify)

Hooks are invoked with process.execPath <scriptPath>. They run in the package's own working directory.

Phase semantics

PhaseConvention
validateSanity-check prerequisites the install needs. Refuse to proceed if not met.
migrateMigrate previous-version state. Idempotent.
seedLay down initial files, schema, sample data. Skip if already done.
verifyAfter everything, confirm the install is healthy. Fail loudly if not.

These are conventions, not enforced. The CLI runs each script identically; what they do is up to the package.

Workspace-internal dependencies

Workspace dependencies (workspace:* in package.json:dependencies) are resolved by pnpm at install time inside the monorepo. They do not get rewritten into version specifiers when the package is packed; npm pack carries workspace:* through. When the resulting tarball is installed at a remote site, those workspace specifiers become real npm specifiers — the publishing pipeline must handle the substitution before tagging a release. The repo currently does this in packages/hivecast/scripts/publish-bootstrap-packages.js for the HiveCast bootstrap set.

See also

Source: projects/matrix-3/packages/mx-cli/src/commands/install.ts:217-339 implements both the npm dependency install and the lifecycle hook runner.