MemberJunction / MemberJunction/MJ
mj app install writes app packages into workspace package.json before verifying they resolve, wedging npm install for the whole monorepo
- Dominant language
- TSQL
- Stars
- 29
- Forks
- 6
- Avg merge
- 1d 8h
- Merged PRs (30d)
- 308
Description
## Summary
`mj app install` writes an app's package names into the workspace `package.json` files **before** anything verifies those packages actually resolve. When an app's packages are unpublished (or misnamed), the entries land anyway and wedge `npm install` / `pnpm install` at the workspace root — for the *whole monorepo*, not just the app — until someone removes them by hand.
This is the last untracked finding from the #3337 review; the other two are #3302 and #3338.
## Mechanism
`AddAppPackages` distributes manifest packages to the server/client workspaces and writes them straight through:
- [`packages/OpenApp/Engine/src/install/package-manager.ts#L110-L132`](https://github.com/MemberJunction/MJ/blob/next/packages/OpenApp/Engine/src/install/package-manager.ts#L110-L132) — `AddAppPackages`
- [`packages/OpenApp/Engine/src/install/package-manager.ts#L361-L373`](https://github.com/MemberJunction/MJ/blob/next/packages/OpenApp/Engine/src/install/package-manager.ts#L361-L373) — `AddDependenciesToPackageJson`
```ts
for (const pkg of packages) {
pkgJson.dependencies[pkg.name] = versionStr; // no existence / resolvability check
}
writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + '\n', 'utf-8');
```
The name comes from the app manifest and is trusted verbatim. There is no registry probe (`npm view`, or a dry-run resolve) before the write, and the `try/catch` only catches filesystem/JSON errors — an unresolvable-but-well-formed name is a complete success as far as this code is concerned.
## Impact
- **Blast radius is the workspace, not the app.** A single unpublished app package makes root `npm install` fail, so unrelated work in the monorepo is blocked too.
- **Manual recovery.** The user has to know to open `packages/MJExplorer/package.json` and `packages/MJAPI/package.json` and delete the entries. Nothing in the failure output points there.
- **Partial writes stick.** `AddAppPackages` writes per target in a loop and returns `Success: false` mid-way on error, but does not roll back the `package.json` files it already wrote.
- **It is the expected path for a private/in-development app**, not an exotic edge case — which is why it was hit during ordinary manual testing.
## Reproduction
Reported by @MS-BC during the live verification for #3337 ([PR description](https://github.com/MemberJunction/MJ/pull/3337)), installing `https://github.com/MemberJunction/mj-sample-open-app` against a live database:
1. `mj app install ` for an app whose `-ng` / entities / actions packages are not published to npm.
2. Install completes far enough to write `@mj-sample-app/ng`, `@mj-sample-app/entities`, `@mj-sample-app/actions` into the MJExplorer/MJAPI `package.json` files.
3. `npm install` at the workspace root now fails (404 for the unpublished names).
4. The only fix is to hand-edit both `package.json` files.
## Suggested direction
Options, roughly in increasing order of effort:
1. **Verify before writing** — resolve each package (registry probe or `npm view`) up front; fail the install with a clear message naming the unresolvable packages, before touching any `package.json`.
2. **Roll back on failure** — make the `package.json` writes transactional across targets so a failed install leaves the workspace as it found it.
3. **Tell the user how to recover** — at minimum, when install fails after the dependency write, print the exact files and entries to remove.
(1) + (3) probably covers the real pain. Related: #3273 (`mj app link`) would give unpublished/local apps a supported path, but it does not remove the need for this guard on the published path.
## Notes
Filed from a code review of #3337 rather than a fresh reproduction — the mechanism above is verified by reading the code on `next`; the end-to-end symptom is as reported in the PR description.
Contributor guide
Research direction
Start in packages/OpenApp/Engine/src/install/package-manager.ts with AddAppPackages and AddDependenciesToPackageJson, then trace the mj app install path. Reproduce with an app whose packages are unpublished and inspect the MJExplorer and MJAPI package.json files. Done means unresolved packages are reported before writes, or failed installs leave no partial dependency changes and explain recovery.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100