@scopewatch/install-adapters 0.0.1
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/dist/errors.d.ts +28 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +61 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/npm-adapter.d.ts +34 -0
- package/dist/npm-adapter.d.ts.map +1 -0
- package/dist/npm-adapter.js +62 -0
- package/dist/npm-adapter.js.map +1 -0
- package/dist/prerequisites.d.ts +21 -0
- package/dist/prerequisites.d.ts.map +1 -0
- package/dist/prerequisites.js +56 -0
- package/dist/prerequisites.js.map +1 -0
- package/package.json +34 -0
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type InstallErrorCategory = 'node_not_installed' | 'node_version_too_old' | 'npm_not_installed' | 'package_not_found' | 'network_unreachable' | 'permission_denied' | 'entry_point_broken' | 'unrecognized';
|
|
2
|
+
export declare class InstallError extends Error {
|
|
3
|
+
category: InstallErrorCategory;
|
|
4
|
+
/** Raw underlying detail (npm stderr, spawn error, etc.), for diagnostics/logs. */
|
|
5
|
+
detail: string | undefined;
|
|
6
|
+
constructor(category: InstallErrorCategory, message: string, detail?: string);
|
|
7
|
+
}
|
|
8
|
+
export declare const MIN_NODE_VERSION = "22.0.0";
|
|
9
|
+
export declare const RECOMMENDED_NODE_VERSION = "24.x";
|
|
10
|
+
export declare function nodeNotInstalledError(): InstallError;
|
|
11
|
+
export declare function nodeVersionTooOldError(found: string): InstallError;
|
|
12
|
+
export declare function npmNotInstalledError(): InstallError;
|
|
13
|
+
export declare function packageNotFoundError(name: string): InstallError;
|
|
14
|
+
export declare function networkUnreachableError(): InstallError;
|
|
15
|
+
export declare function permissionDeniedError(path: string): InstallError;
|
|
16
|
+
/**
|
|
17
|
+
* Scope note: this category is intentionally minimal for Phase D. It confirms the
|
|
18
|
+
* package's declared entry point exists on disk and, for a plain Node script, that
|
|
19
|
+
* requiring/importing it doesn't throw synchronously. It does NOT attempt an MCP
|
|
20
|
+
* JSON-RPC handshake or any protocol-level connectivity check - that is the job of
|
|
21
|
+
* `scopewatch test <server>` and the diagnostics module (Phase I), which own
|
|
22
|
+
* connection validation. Do not extend this check into protocol testing; a broken
|
|
23
|
+
* or incompletely-published package is the failure this category exists to catch,
|
|
24
|
+
* not "does this server actually speak MCP correctly."
|
|
25
|
+
*/
|
|
26
|
+
export declare function entryPointBrokenError(name: string, detail: string): InstallError;
|
|
27
|
+
export declare function unrecognizedInstallError(code: string | undefined, rawOutput: string): InstallError;
|
|
28
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAC5B,oBAAoB,GACpB,sBAAsB,GACtB,mBAAmB,GACnB,mBAAmB,GACnB,qBAAqB,GACrB,mBAAmB,GACnB,oBAAoB,GACpB,cAAc,CAAC;AAEnB,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,mFAAmF;IACnF,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;gBAEf,QAAQ,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAM7E;AAED,eAAO,MAAM,gBAAgB,WAAW,CAAC;AACzC,eAAO,MAAM,wBAAwB,SAAS,CAAC;AAE/C,wBAAgB,qBAAqB,IAAI,YAAY,CAMpD;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAMlE;AAED,wBAAgB,oBAAoB,IAAI,YAAY,CAMnD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAM/D;AAED,wBAAgB,uBAAuB,IAAI,YAAY,CAMtD;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAMhE;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CAQhF;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,CAOlG"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export class InstallError extends Error {
|
|
2
|
+
category;
|
|
3
|
+
/** Raw underlying detail (npm stderr, spawn error, etc.), for diagnostics/logs. */
|
|
4
|
+
detail;
|
|
5
|
+
constructor(category, message, detail) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'InstallError';
|
|
8
|
+
this.category = category;
|
|
9
|
+
this.detail = detail;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export const MIN_NODE_VERSION = '22.0.0';
|
|
13
|
+
export const RECOMMENDED_NODE_VERSION = '24.x';
|
|
14
|
+
export function nodeNotInstalledError() {
|
|
15
|
+
return new InstallError('node_not_installed', `Node.js is not installed or not on PATH. Install Node.js ${MIN_NODE_VERSION} or later ` +
|
|
16
|
+
`(${RECOMMENDED_NODE_VERSION} recommended) from https://nodejs.org, then run 'scopewatch doctor' again.`);
|
|
17
|
+
}
|
|
18
|
+
export function nodeVersionTooOldError(found) {
|
|
19
|
+
return new InstallError('node_version_too_old', `Node.js ${found} is installed, but Scopewatch requires ${MIN_NODE_VERSION} or later ` +
|
|
20
|
+
`(${RECOMMENDED_NODE_VERSION} recommended). Upgrade Node.js, then run 'scopewatch doctor' again.`);
|
|
21
|
+
}
|
|
22
|
+
export function npmNotInstalledError() {
|
|
23
|
+
return new InstallError('npm_not_installed', `npm is not installed or not on PATH. npm ships with Node.js - reinstalling Node.js ` +
|
|
24
|
+
`from https://nodejs.org should restore it.`);
|
|
25
|
+
}
|
|
26
|
+
export function packageNotFoundError(name) {
|
|
27
|
+
return new InstallError('package_not_found', `Package '${name}' was not found in the npm registry. Check the package name for typos, ` +
|
|
28
|
+
`or confirm it's published.`);
|
|
29
|
+
}
|
|
30
|
+
export function networkUnreachableError() {
|
|
31
|
+
return new InstallError('network_unreachable', `Could not reach the npm registry (network error). Check your internet connection and ` +
|
|
32
|
+
`proxy settings, then retry.`);
|
|
33
|
+
}
|
|
34
|
+
export function permissionDeniedError(path) {
|
|
35
|
+
return new InstallError('permission_denied', `Permission denied writing to ${path}. Scopewatch installs locally and should not require ` +
|
|
36
|
+
`elevated privileges - check ownership of ${path}.`);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Scope note: this category is intentionally minimal for Phase D. It confirms the
|
|
40
|
+
* package's declared entry point exists on disk and, for a plain Node script, that
|
|
41
|
+
* requiring/importing it doesn't throw synchronously. It does NOT attempt an MCP
|
|
42
|
+
* JSON-RPC handshake or any protocol-level connectivity check - that is the job of
|
|
43
|
+
* `scopewatch test <server>` and the diagnostics module (Phase I), which own
|
|
44
|
+
* connection validation. Do not extend this check into protocol testing; a broken
|
|
45
|
+
* or incompletely-published package is the failure this category exists to catch,
|
|
46
|
+
* not "does this server actually speak MCP correctly."
|
|
47
|
+
*/
|
|
48
|
+
export function entryPointBrokenError(name, detail) {
|
|
49
|
+
return new InstallError('entry_point_broken', `'${name}' installed successfully but its entry point failed a basic load check: ` +
|
|
50
|
+
`${truncate(detail, 300)}. This usually means a missing runtime dependency or an ` +
|
|
51
|
+
`incompatible Node version for this package.`, detail);
|
|
52
|
+
}
|
|
53
|
+
export function unrecognizedInstallError(code, rawOutput) {
|
|
54
|
+
return new InstallError('unrecognized', `Install failed with an unrecognized error${code ? ` (${code})` : ''}. ` +
|
|
55
|
+
`Raw npm output: ${truncate(rawOutput, 500)}`, rawOutput);
|
|
56
|
+
}
|
|
57
|
+
function truncate(s, max) {
|
|
58
|
+
const oneLine = s.replace(/\s+/g, ' ').trim();
|
|
59
|
+
return oneLine.length > max ? oneLine.slice(0, max) + '...' : oneLine;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAuB;IAC/B,mFAAmF;IACnF,MAAM,CAAqB;IAE3B,YAAY,QAA8B,EAAE,OAAe,EAAE,MAAe;QAC1E,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,QAAQ,CAAC;AACzC,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC;AAE/C,MAAM,UAAU,qBAAqB;IACnC,OAAO,IAAI,YAAY,CACrB,oBAAoB,EACpB,4DAA4D,gBAAgB,YAAY;QACtF,IAAI,wBAAwB,4EAA4E,CAC3G,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAClD,OAAO,IAAI,YAAY,CACrB,sBAAsB,EACtB,WAAW,KAAK,0CAA0C,gBAAgB,YAAY;QACpF,IAAI,wBAAwB,qEAAqE,CACpG,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,YAAY,CACrB,mBAAmB,EACnB,qFAAqF;QACnF,4CAA4C,CAC/C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,IAAI,YAAY,CACrB,mBAAmB,EACnB,YAAY,IAAI,yEAAyE;QACvF,4BAA4B,CAC/B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,OAAO,IAAI,YAAY,CACrB,qBAAqB,EACrB,uFAAuF;QACrF,6BAA6B,CAChC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,OAAO,IAAI,YAAY,CACrB,mBAAmB,EACnB,gCAAgC,IAAI,uDAAuD;QACzF,4CAA4C,IAAI,GAAG,CACtD,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,MAAc;IAChE,OAAO,IAAI,YAAY,CACrB,oBAAoB,EACpB,IAAI,IAAI,0EAA0E;QAChF,GAAG,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,0DAA0D;QAClF,6CAA6C,EAC/C,MAAM,CACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,IAAwB,EAAE,SAAiB;IAClF,OAAO,IAAI,YAAY,CACrB,cAAc,EACd,4CAA4C,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI;QACtE,mBAAmB,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,EAC/C,SAAS,CACV,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAW;IACtC,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9C,OAAO,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;AACxE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { checkPrerequisites, satisfiesMinVersion } from './prerequisites.js';
|
|
2
|
+
export type { PrerequisiteCheckResult, CommandRunner } from './prerequisites.js';
|
|
3
|
+
export { installPackage, categorizeNpmFailure, checkEntryPoint } from './npm-adapter.js';
|
|
4
|
+
export type { InstallResult, InstallRunner } from './npm-adapter.js';
|
|
5
|
+
export { InstallError, MIN_NODE_VERSION, RECOMMENDED_NODE_VERSION, nodeNotInstalledError, nodeVersionTooOldError, npmNotInstalledError, packageNotFoundError, networkUnreachableError, permissionDeniedError, entryPointBrokenError, unrecognizedInstallError, } from './errors.js';
|
|
6
|
+
export type { InstallErrorCategory } from './errors.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,YAAY,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACzF,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { checkPrerequisites, satisfiesMinVersion } from './prerequisites.js';
|
|
2
|
+
export { installPackage, categorizeNpmFailure, checkEntryPoint } from './npm-adapter.js';
|
|
3
|
+
export { InstallError, MIN_NODE_VERSION, RECOMMENDED_NODE_VERSION, nodeNotInstalledError, nodeVersionTooOldError, npmNotInstalledError, packageNotFoundError, networkUnreachableError, permissionDeniedError, entryPointBrokenError, unrecognizedInstallError, } from './errors.js';
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE7E,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEzF,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { InstallError } from './errors.js';
|
|
2
|
+
import { type CommandRunner } from './prerequisites.js';
|
|
3
|
+
export type InstallResult = {
|
|
4
|
+
ok: true;
|
|
5
|
+
installPath: string;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
error: InstallError;
|
|
9
|
+
};
|
|
10
|
+
/** Injected process runner for testability - avoids spawning real npm in unit tests. */
|
|
11
|
+
export type InstallRunner = (command: string, args: string[]) => {
|
|
12
|
+
code: number;
|
|
13
|
+
stdout: string;
|
|
14
|
+
stderr: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Categorize an npm install failure from its exit output. Matches on npm's own
|
|
18
|
+
* stable error codes (E404, ETARGET, ENOTFOUND, etc.) rather than free-text
|
|
19
|
+
* pattern matching - codes are stable across npm versions, wording is not.
|
|
20
|
+
*/
|
|
21
|
+
export declare function categorizeNpmFailure(packageName: string, stderr: string, installPath: string): InstallError;
|
|
22
|
+
/**
|
|
23
|
+
* Scope note: see errors.ts's entryPointBrokenError doc comment. This is a load
|
|
24
|
+
* smoke-check, not a protocol handshake. It confirms the entry point file exists
|
|
25
|
+
* and, if resolvable as a plain module, that importing it doesn't throw synchronously.
|
|
26
|
+
*/
|
|
27
|
+
export declare function checkEntryPoint(packageName: string, installPath: string, entryFile: string): Promise<InstallError | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Install a package via npm. Prerequisites are checked FIRST - a bad environment
|
|
30
|
+
* (missing Node/npm, wrong version) is reported before any install is attempted,
|
|
31
|
+
* never discovered midway through a partial install.
|
|
32
|
+
*/
|
|
33
|
+
export declare function installPackage(packageName: string, installPath: string, runner: InstallRunner, prereqRunner?: CommandRunner): InstallResult;
|
|
34
|
+
//# sourceMappingURL=npm-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"npm-adapter.d.ts","sourceRoot":"","sources":["../src/npm-adapter.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,YAAY,EAMb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAE5E,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CAAC;AAEvC,wFAAwF;AACxF,MAAM,MAAM,aAAa,GAAG,CAC1B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,KACX;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY,CAoB3G;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAa/H;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,aAAa,EACrB,YAAY,CAAC,EAAE,aAAa,GAC3B,aAAa,CAaf"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { packageNotFoundError, networkUnreachableError, permissionDeniedError, entryPointBrokenError, unrecognizedInstallError, } from './errors.js';
|
|
5
|
+
import { checkPrerequisites } from './prerequisites.js';
|
|
6
|
+
/**
|
|
7
|
+
* Categorize an npm install failure from its exit output. Matches on npm's own
|
|
8
|
+
* stable error codes (E404, ETARGET, ENOTFOUND, etc.) rather than free-text
|
|
9
|
+
* pattern matching - codes are stable across npm versions, wording is not.
|
|
10
|
+
*/
|
|
11
|
+
export function categorizeNpmFailure(packageName, stderr, installPath) {
|
|
12
|
+
const combined = stderr;
|
|
13
|
+
if (/\bE404\b/.test(combined) || /\bETARGET\b/.test(combined)) {
|
|
14
|
+
return packageNotFoundError(packageName);
|
|
15
|
+
}
|
|
16
|
+
if (/\bENOTFOUND\b/.test(combined) ||
|
|
17
|
+
/\bEAI_AGAIN\b/.test(combined) ||
|
|
18
|
+
/\bECONNREFUSED\b/.test(combined) ||
|
|
19
|
+
/\bETIMEDOUT\b/.test(combined)) {
|
|
20
|
+
return networkUnreachableError();
|
|
21
|
+
}
|
|
22
|
+
if (/\bEACCES\b/.test(combined) || /\bEPERM\b/.test(combined)) {
|
|
23
|
+
return permissionDeniedError(installPath);
|
|
24
|
+
}
|
|
25
|
+
const codeMatch = combined.match(/\bE[A-Z0-9]+\b/);
|
|
26
|
+
return unrecognizedInstallError(codeMatch?.[0], combined);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Scope note: see errors.ts's entryPointBrokenError doc comment. This is a load
|
|
30
|
+
* smoke-check, not a protocol handshake. It confirms the entry point file exists
|
|
31
|
+
* and, if resolvable as a plain module, that importing it doesn't throw synchronously.
|
|
32
|
+
*/
|
|
33
|
+
export async function checkEntryPoint(packageName, installPath, entryFile) {
|
|
34
|
+
const fullPath = join(installPath, 'node_modules', packageName, entryFile);
|
|
35
|
+
if (!existsSync(fullPath)) {
|
|
36
|
+
return entryPointBrokenError(packageName, `entry point file not found at ${fullPath}`);
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
await import(pathToFileURL(fullPath).href);
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
return entryPointBrokenError(packageName, err?.message ?? String(err));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Install a package via npm. Prerequisites are checked FIRST - a bad environment
|
|
48
|
+
* (missing Node/npm, wrong version) is reported before any install is attempted,
|
|
49
|
+
* never discovered midway through a partial install.
|
|
50
|
+
*/
|
|
51
|
+
export function installPackage(packageName, installPath, runner, prereqRunner) {
|
|
52
|
+
const prereqs = checkPrerequisites(prereqRunner);
|
|
53
|
+
if (!prereqs.ok) {
|
|
54
|
+
return { ok: false, error: prereqs.error };
|
|
55
|
+
}
|
|
56
|
+
const result = runner('npm', ['install', packageName, '--prefix', installPath]);
|
|
57
|
+
if (result.code !== 0) {
|
|
58
|
+
return { ok: false, error: categorizeNpmFailure(packageName, result.stderr, installPath) };
|
|
59
|
+
}
|
|
60
|
+
return { ok: true, installPath };
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=npm-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"npm-adapter.js","sourceRoot":"","sources":["../src/npm-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { InstallError } from './errors.js';
|
|
2
|
+
export type PrerequisiteCheckResult = {
|
|
3
|
+
ok: true;
|
|
4
|
+
nodeVersion: string;
|
|
5
|
+
npmVersion: string;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
error: InstallError;
|
|
9
|
+
};
|
|
10
|
+
/** Injected for testability - avoids spawning real processes in unit tests. */
|
|
11
|
+
export type CommandRunner = (command: string, args: string[]) => string;
|
|
12
|
+
/**
|
|
13
|
+
* Check that Node.js and npm are present and that Node meets the minimum version.
|
|
14
|
+
* Runs 'node --version' and 'npm --version' as external commands (not process.version)
|
|
15
|
+
* so this reflects what npm/npx will actually invoke on PATH, and so it stays testable
|
|
16
|
+
* via dependency injection without depending on the test runner's own Node version.
|
|
17
|
+
*/
|
|
18
|
+
export declare function checkPrerequisites(runner?: CommandRunner): PrerequisiteCheckResult;
|
|
19
|
+
/** Minimal semver major.minor.patch comparison - sufficient for a floor check. */
|
|
20
|
+
export declare function satisfiesMinVersion(version: string, minVersion: string): boolean;
|
|
21
|
+
//# sourceMappingURL=prerequisites.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prerequisites.d.ts","sourceRoot":"","sources":["../src/prerequisites.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,MAAM,uBAAuB,GAC/B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CAAC;AAEvC,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC;AASxE;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,aAA6B,GAAG,uBAAuB,CAwBjG;AAED,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAQhF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { MIN_NODE_VERSION, nodeNotInstalledError, nodeVersionTooOldError, npmNotInstalledError } from './errors.js';
|
|
3
|
+
// npm/npx ship as .cmd files on Windows, and Node's automatic bare-command
|
|
4
|
+
// PATH resolution only searches for real executables (.exe/.com) without
|
|
5
|
+
// shell: true - found via a real Windows CI run, see apps/cli/src/real-
|
|
6
|
+
// runners.ts's identical fix for the full account of how this was missed.
|
|
7
|
+
const defaultRunner = (command, args) => execFileSync(command, args, { encoding: 'utf-8', windowsHide: true, shell: process.platform === 'win32' });
|
|
8
|
+
/**
|
|
9
|
+
* Check that Node.js and npm are present and that Node meets the minimum version.
|
|
10
|
+
* Runs 'node --version' and 'npm --version' as external commands (not process.version)
|
|
11
|
+
* so this reflects what npm/npx will actually invoke on PATH, and so it stays testable
|
|
12
|
+
* via dependency injection without depending on the test runner's own Node version.
|
|
13
|
+
*/
|
|
14
|
+
export function checkPrerequisites(runner = defaultRunner) {
|
|
15
|
+
let nodeVersionRaw;
|
|
16
|
+
try {
|
|
17
|
+
nodeVersionRaw = runner('node', ['--version']).trim();
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
if (err?.code === 'ENOENT') {
|
|
21
|
+
return { ok: false, error: nodeNotInstalledError() };
|
|
22
|
+
}
|
|
23
|
+
return { ok: false, error: nodeNotInstalledError() };
|
|
24
|
+
}
|
|
25
|
+
const nodeVersion = nodeVersionRaw.replace(/^v/, '');
|
|
26
|
+
if (!satisfiesMinVersion(nodeVersion, MIN_NODE_VERSION)) {
|
|
27
|
+
return { ok: false, error: nodeVersionTooOldError(nodeVersion) };
|
|
28
|
+
}
|
|
29
|
+
let npmVersion;
|
|
30
|
+
try {
|
|
31
|
+
npmVersion = runner('npm', ['--version']).trim();
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
return { ok: false, error: npmNotInstalledError() };
|
|
35
|
+
}
|
|
36
|
+
return { ok: true, nodeVersion, npmVersion };
|
|
37
|
+
}
|
|
38
|
+
/** Minimal semver major.minor.patch comparison - sufficient for a floor check. */
|
|
39
|
+
export function satisfiesMinVersion(version, minVersion) {
|
|
40
|
+
const v = parseSemver(version);
|
|
41
|
+
const min = parseSemver(minVersion);
|
|
42
|
+
if (!v || !min)
|
|
43
|
+
return false;
|
|
44
|
+
if (v.major !== min.major)
|
|
45
|
+
return v.major > min.major;
|
|
46
|
+
if (v.minor !== min.minor)
|
|
47
|
+
return v.minor > min.minor;
|
|
48
|
+
return v.patch >= min.patch;
|
|
49
|
+
}
|
|
50
|
+
function parseSemver(version) {
|
|
51
|
+
const match = version.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
52
|
+
if (!match)
|
|
53
|
+
return null;
|
|
54
|
+
return { major: Number(match[1]), minor: Number(match[2]), patch: Number(match[3]) };
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=prerequisites.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prerequisites.js","sourceRoot":"","sources":["../src/prerequisites.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@scopewatch/install-adapters",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "npm/npx install adapter: prerequisite detection and categorized install diagnostics",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Sathvik Putta <puttasathvik16@gmail.com>",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/PuttaSathvik16/scopewatch.git",
|
|
10
|
+
"directory": "packages/install-adapters"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc",
|
|
26
|
+
"test": "node --import tsx --test 'test/**/*.test.ts'",
|
|
27
|
+
"clean": "rm -rf dist"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^22.0.0",
|
|
31
|
+
"typescript": "^5.7.0",
|
|
32
|
+
"tsx": "^4.19.0"
|
|
33
|
+
}
|
|
34
|
+
}
|