inup 1.5.5 → 1.6.0

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.
Files changed (75) hide show
  1. package/README.md +53 -10
  2. package/dist/cli.js +53 -31
  3. package/dist/config/constants.js +4 -6
  4. package/dist/config/package-meta.js +10 -0
  5. package/dist/config/project-config.js +11 -1
  6. package/dist/core/package-detector.js +37 -5
  7. package/dist/core/upgrade-runner.js +8 -10
  8. package/dist/core/upgrader.js +15 -11
  9. package/dist/features/changelog/clients/github-client.js +5 -6
  10. package/dist/features/changelog/services/changelog-service.js +2 -4
  11. package/dist/features/changelog/services/package-metadata-service.js +7 -23
  12. package/dist/features/changelog/services/release-notes-service.js +4 -29
  13. package/dist/features/debug/services/performance-tracker.js +6 -8
  14. package/dist/features/headless/headless-runner.js +53 -0
  15. package/dist/features/headless/index.js +27 -0
  16. package/dist/features/headless/report.js +64 -0
  17. package/dist/features/headless/types.js +6 -0
  18. package/dist/features/headless/vulnerability-audit.js +106 -0
  19. package/dist/index.js +1 -2
  20. package/dist/interactive-ui.js +15 -606
  21. package/dist/services/background-audit.js +3 -5
  22. package/dist/services/cache-manager.js +5 -7
  23. package/dist/services/http/inflight.js +22 -0
  24. package/dist/services/http/retry.js +30 -0
  25. package/dist/services/index.js +0 -1
  26. package/dist/services/npm-registry.js +20 -97
  27. package/dist/services/package-manager-detector.js +6 -3
  28. package/dist/services/persistent-cache.js +8 -13
  29. package/dist/types/domain.js +3 -0
  30. package/dist/types/streaming.js +3 -0
  31. package/dist/types/ui.js +3 -0
  32. package/dist/types.js +17 -0
  33. package/dist/ui/controllers/package-info-modal-controller.js +22 -31
  34. package/dist/ui/controllers/vulnerability-audit-controller.js +17 -8
  35. package/dist/ui/index.js +3 -0
  36. package/dist/ui/input-handler.js +58 -75
  37. package/dist/ui/keymap.js +208 -0
  38. package/dist/ui/modal/package-info-sections/release-notes.js +161 -0
  39. package/dist/ui/modal/package-info-sections/sections.js +174 -0
  40. package/dist/ui/modal/package-info-sections/text.js +75 -0
  41. package/dist/ui/modal/package-info-sections.js +15 -369
  42. package/dist/ui/modal/package-info.js +1 -1
  43. package/dist/ui/presenters/health.js +24 -0
  44. package/dist/ui/renderer/help-modal.js +75 -0
  45. package/dist/ui/renderer/index.js +2 -5
  46. package/dist/ui/renderer/package-list/interface.js +184 -0
  47. package/dist/ui/renderer/package-list/rows.js +177 -0
  48. package/dist/ui/renderer/package-list.js +15 -455
  49. package/dist/ui/session/action-dispatcher.js +233 -0
  50. package/dist/ui/session/index.js +13 -0
  51. package/dist/ui/session/interactive-session.js +280 -0
  52. package/dist/ui/session/selection-state-builder.js +149 -0
  53. package/dist/ui/state/filter-manager.js +17 -6
  54. package/dist/ui/state/modal-manager.js +35 -0
  55. package/dist/ui/state/navigation-manager.js +33 -4
  56. package/dist/ui/state/state-manager.js +68 -3
  57. package/dist/ui/state/theme-manager.js +1 -0
  58. package/dist/ui/themes-colors.js +16 -4
  59. package/dist/ui/themes.js +11 -19
  60. package/dist/ui/utils/cursor.js +12 -7
  61. package/dist/ui/utils/index.js +6 -1
  62. package/dist/ui/utils/text.js +3 -2
  63. package/dist/ui/utils/version.js +60 -86
  64. package/dist/utils/color.js +38 -0
  65. package/dist/utils/config.js +13 -1
  66. package/dist/utils/engines.js +63 -0
  67. package/dist/utils/filesystem/io.js +103 -0
  68. package/dist/utils/filesystem/paths.js +19 -0
  69. package/dist/utils/filesystem/scan.js +280 -0
  70. package/dist/utils/filesystem.js +17 -335
  71. package/dist/utils/index.js +3 -0
  72. package/dist/utils/manifest.js +35 -0
  73. package/dist/utils/version.js +38 -1
  74. package/package.json +8 -7
  75. package/dist/services/jsdelivr-registry.js +0 -395
@@ -7,11 +7,14 @@ exports.configManager = void 0;
7
7
  const fs_1 = require("fs");
8
8
  const path_1 = require("path");
9
9
  const env_paths_1 = __importDefault(require("env-paths"));
10
+ const config_1 = require("../config");
10
11
  class ConfigManager {
12
+ configDir;
13
+ configPath;
11
14
  constructor() {
12
15
  // Use env-paths for cross-platform config directory support
13
16
  // Mac/Linux: ~/.config/inup, Windows: %APPDATA%/inup
14
- const paths = (0, env_paths_1.default)('inup');
17
+ const paths = (0, env_paths_1.default)(config_1.PACKAGE_NAME);
15
18
  this.configDir = paths.config;
16
19
  this.configPath = (0, path_1.join)(this.configDir, 'config.json');
17
20
  }
@@ -51,6 +54,15 @@ class ConfigManager {
51
54
  config.theme = theme;
52
55
  this.writeConfig(config);
53
56
  }
57
+ getFilters() {
58
+ const config = this.readConfig();
59
+ return config.filters ?? null;
60
+ }
61
+ setFilters(filters) {
62
+ const config = this.readConfig();
63
+ config.filters = filters;
64
+ this.writeConfig(config);
65
+ }
54
66
  }
55
67
  // Export singleton instance
56
68
  exports.configManager = new ConfigManager();
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.checkNodeEngineCompatibility = checkNodeEngineCompatibility;
37
+ const semver = __importStar(require("semver"));
38
+ /**
39
+ * Check whether the running Node version satisfies a package's declared
40
+ * `engines.node` range. Returns a short human-readable warning when it does
41
+ * not, or `null` when compatible (or when the inputs are unusable).
42
+ *
43
+ * Best-effort: an unparseable range is treated as "no opinion" (null) rather
44
+ * than a false warning.
45
+ */
46
+ function checkNodeEngineCompatibility(requiredRange, currentNodeVersion = process.versions.node) {
47
+ if (!requiredRange) {
48
+ return null;
49
+ }
50
+ const range = semver.validRange(requiredRange, { loose: true });
51
+ if (!range) {
52
+ return null;
53
+ }
54
+ const current = semver.coerce(currentNodeVersion);
55
+ if (!current) {
56
+ return null;
57
+ }
58
+ if (semver.satisfies(current, range, { includePrerelease: true })) {
59
+ return null;
60
+ }
61
+ return `requires Node ${requiredRange}, you're on ${current.version}`;
62
+ }
63
+ //# sourceMappingURL=engines.js.map
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readPackageJson = readPackageJson;
4
+ exports.detectJsonFormat = detectJsonFormat;
5
+ exports.readPackageJsonAsync = readPackageJsonAsync;
6
+ exports.collectAllDependencies = collectAllDependencies;
7
+ exports.collectAllDependenciesAsync = collectAllDependenciesAsync;
8
+ const fs_1 = require("fs");
9
+ const fs_2 = require("fs");
10
+ function readPackageJson(path) {
11
+ try {
12
+ const content = (0, fs_1.readFileSync)(path, 'utf-8');
13
+ return JSON.parse(content);
14
+ }
15
+ catch (error) {
16
+ throw new Error(`Failed to read package.json: ${error}`);
17
+ }
18
+ }
19
+ /**
20
+ * Detect the indentation and trailing-newline style of a raw JSON document so a
21
+ * re-serialized version can preserve the original formatting instead of normalizing it.
22
+ *
23
+ * The first indented line's leading whitespace is exactly one indent unit; using it verbatim
24
+ * as the JSON.stringify indent round-trips tabs, 2-space, and 4-space without branching on type.
25
+ * Minified/single-line files (no indented line) fall back to 2 spaces, matching prior behavior.
26
+ */
27
+ function detectJsonFormat(raw) {
28
+ const match = raw.match(/\n([ \t]+)\S/);
29
+ return {
30
+ indent: match ? match[1] : 2,
31
+ trailingNewline: raw.endsWith('\n'),
32
+ };
33
+ }
34
+ async function readPackageJsonAsync(path) {
35
+ try {
36
+ const content = await fs_2.promises.readFile(path, 'utf-8');
37
+ return JSON.parse(content);
38
+ }
39
+ catch (error) {
40
+ throw new Error(`Failed to read package.json: ${error}`);
41
+ }
42
+ }
43
+ function collectAllDependencies(packageJsonFiles, _options = {}) {
44
+ const allDeps = [];
45
+ for (const packageJsonPath of packageJsonFiles) {
46
+ try {
47
+ const packageJson = readPackageJson(packageJsonPath);
48
+ const depTypes = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
49
+ for (const depType of depTypes) {
50
+ const deps = packageJson[depType];
51
+ if (deps && typeof deps === 'object') {
52
+ for (const [name, version] of Object.entries(deps)) {
53
+ allDeps.push({
54
+ name,
55
+ version: version,
56
+ type: depType,
57
+ packageJsonPath,
58
+ });
59
+ }
60
+ }
61
+ }
62
+ }
63
+ catch {
64
+ // Skip malformed package.json files
65
+ }
66
+ }
67
+ return allDeps;
68
+ }
69
+ async function collectAllDependenciesAsync(packageJsonFiles, _options = {}) {
70
+ const packageJsonPromises = packageJsonFiles.map(async (packageJsonPath) => {
71
+ try {
72
+ const packageJson = await readPackageJsonAsync(packageJsonPath);
73
+ return { packageJson, packageJsonPath };
74
+ }
75
+ catch {
76
+ // Skip malformed package.json files
77
+ return null;
78
+ }
79
+ });
80
+ const results = await Promise.all(packageJsonPromises);
81
+ const allDeps = [];
82
+ for (const result of results) {
83
+ if (!result)
84
+ continue;
85
+ const { packageJson, packageJsonPath } = result;
86
+ const depTypes = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
87
+ for (const depType of depTypes) {
88
+ const deps = packageJson[depType];
89
+ if (deps && typeof deps === 'object') {
90
+ for (const [name, version] of Object.entries(deps)) {
91
+ allDeps.push({
92
+ name,
93
+ version: version,
94
+ type: depType,
95
+ packageJsonPath,
96
+ });
97
+ }
98
+ }
99
+ }
100
+ }
101
+ return allDeps;
102
+ }
103
+ //# sourceMappingURL=io.js.map
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findPackageJson = findPackageJson;
4
+ exports.findWorkspaceRoot = findWorkspaceRoot;
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const package_manager_detector_1 = require("../../services/package-manager-detector");
8
+ function findPackageJson(cwd = process.cwd()) {
9
+ const packageJsonPath = (0, path_1.join)(cwd, 'package.json');
10
+ return (0, fs_1.existsSync)(packageJsonPath) ? packageJsonPath : null;
11
+ }
12
+ function findWorkspaceRoot(cwd = process.cwd(), packageManager) {
13
+ if (!packageManager) {
14
+ const detected = package_manager_detector_1.PackageManagerDetector.detect(cwd);
15
+ packageManager = detected.name;
16
+ }
17
+ return package_manager_detector_1.PackageManagerDetector.findWorkspaceRoot(cwd, packageManager);
18
+ }
19
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findAllPackageJsonFiles = findAllPackageJsonFiles;
4
+ exports.findAllPackageJsonFilesAsync = findAllPackageJsonFilesAsync;
5
+ const fs_1 = require("fs");
6
+ const fs_2 = require("fs");
7
+ const path_1 = require("path");
8
+ const SKIP_DIRS = new Set([
9
+ 'node_modules',
10
+ 'dist',
11
+ 'build',
12
+ 'coverage',
13
+ 'out',
14
+ 'lib',
15
+ 'es',
16
+ 'esm',
17
+ 'cjs',
18
+ ]);
19
+ /**
20
+ * Skip dirs that are ambiguous source-vs-build directories where a real package may legitimately
21
+ * live. Only these trigger the "silently skipped a package" warning — node_modules and build-output
22
+ * dirs (dist/build/coverage/out) routinely contain package.json files and would be pure noise.
23
+ */
24
+ const WARN_SKIP_DIRS = new Set(['lib', 'es', 'esm', 'cjs']);
25
+ /** Effective skip set: the defaults minus any directory the caller opted back into via `scanDirs`. */
26
+ function buildSkipSet(scanDirs) {
27
+ if (!scanDirs || scanDirs.length === 0) {
28
+ return SKIP_DIRS;
29
+ }
30
+ const skip = new Set(SKIP_DIRS);
31
+ for (const dir of scanDirs) {
32
+ skip.delete(dir);
33
+ }
34
+ return skip;
35
+ }
36
+ function classifyDirectory(name, skipSet) {
37
+ if (name.startsWith('.'))
38
+ return 'hidden';
39
+ if (skipSet.has(name))
40
+ return 'skip-dir';
41
+ return null;
42
+ }
43
+ /**
44
+ * Cheaply decide whether a pruned directory looks like it holds a real package — a package.json
45
+ * directly inside it, or inside any immediate child (the common `lib/<pkg>/package.json` monorepo
46
+ * layout). Stays shallow (depth 1) so detecting a skip doesn't re-walk the subtree we just pruned.
47
+ */
48
+ function prunedDirHoldsPackage(dir) {
49
+ if ((0, fs_1.existsSync)((0, path_1.join)(dir, 'package.json'))) {
50
+ return true;
51
+ }
52
+ let entries;
53
+ try {
54
+ entries = (0, fs_1.readdirSync)(dir);
55
+ }
56
+ catch {
57
+ return false;
58
+ }
59
+ for (const entry of entries) {
60
+ if (entry.startsWith('.'))
61
+ continue;
62
+ const child = (0, path_1.join)(dir, entry);
63
+ try {
64
+ if ((0, fs_1.statSync)(child).isDirectory() && (0, fs_1.existsSync)((0, path_1.join)(child, 'package.json'))) {
65
+ return true;
66
+ }
67
+ }
68
+ catch {
69
+ // Skip children we can't stat
70
+ }
71
+ }
72
+ return false;
73
+ }
74
+ /**
75
+ * Decide whether to descend into a directory, and notify when one is pruned by the default skip
76
+ * list despite containing a package.json (so the caller can surface a "silently skipped" warning).
77
+ */
78
+ function shouldTraverse(name, fullPath, relativePath, skipSet, onSkippedPackageDir) {
79
+ const reason = classifyDirectory(name, skipSet);
80
+ if (reason === null) {
81
+ return true;
82
+ }
83
+ if (reason === 'skip-dir' &&
84
+ WARN_SKIP_DIRS.has(name) &&
85
+ onSkippedPackageDir &&
86
+ prunedDirHoldsPackage(fullPath)) {
87
+ onSkippedPackageDir(relativePath);
88
+ }
89
+ return false;
90
+ }
91
+ function findAllPackageJsonFiles(rootDir = process.cwd(), excludePatterns = [], maxDepth = 10, onProgress, options = {}) {
92
+ const packageJsonFiles = [];
93
+ const visitedPaths = new Set();
94
+ let directoriesScanned = 0;
95
+ let lastProgressAt = 0;
96
+ const progressIntervalMs = 250;
97
+ const skipSet = buildSkipSet(options.scanDirs);
98
+ const excludeRegexes = excludePatterns.map((pattern) => new RegExp(pattern, 'i'));
99
+ function shouldExcludePath(relativePath) {
100
+ return excludeRegexes.some((regex) => regex.test(relativePath));
101
+ }
102
+ function reportProgress(currentDir, force = false) {
103
+ if (!onProgress)
104
+ return;
105
+ const now = Date.now();
106
+ if (!force && now - lastProgressAt < progressIntervalMs) {
107
+ return;
108
+ }
109
+ lastProgressAt = now;
110
+ const relativePath = (0, path_1.relative)(rootDir, currentDir) || '.';
111
+ onProgress(relativePath, packageJsonFiles.length);
112
+ }
113
+ function traverseDirectory(dir, depth = 0) {
114
+ if (depth > maxDepth) {
115
+ return;
116
+ }
117
+ try {
118
+ // Prevent symlink cycles by tracking visited real paths
119
+ const realPath = (0, fs_1.realpathSync)(dir);
120
+ if (visitedPaths.has(realPath)) {
121
+ return;
122
+ }
123
+ visitedPaths.add(realPath);
124
+ directoriesScanned++;
125
+ // Report progress every 10 directories or on first scan
126
+ if (onProgress && (directoriesScanned % 10 === 0 || directoriesScanned === 1)) {
127
+ reportProgress(dir, true);
128
+ }
129
+ const files = (0, fs_1.readdirSync)(dir);
130
+ for (const file of files) {
131
+ reportProgress(dir);
132
+ const fullPath = (0, path_1.join)(dir, file);
133
+ const relativePath = (0, path_1.relative)(rootDir, fullPath);
134
+ if (shouldExcludePath(relativePath)) {
135
+ continue;
136
+ }
137
+ let stat;
138
+ try {
139
+ stat = (0, fs_1.statSync)(fullPath);
140
+ }
141
+ catch {
142
+ // Skip files/dirs we can't stat (broken symlinks, permission issues)
143
+ continue;
144
+ }
145
+ if (stat.isDirectory()) {
146
+ if (shouldTraverse(file, fullPath, relativePath, skipSet, options.onSkippedPackageDir)) {
147
+ traverseDirectory(fullPath, depth + 1);
148
+ }
149
+ }
150
+ else if (file === 'package.json' && stat.isFile()) {
151
+ packageJsonFiles.push(fullPath);
152
+ }
153
+ }
154
+ }
155
+ catch {
156
+ // Skip directories that can't be read (permission issues, etc.)
157
+ }
158
+ }
159
+ traverseDirectory(rootDir);
160
+ return packageJsonFiles;
161
+ }
162
+ async function findAllPackageJsonFilesAsync(rootDir = process.cwd(), excludePatterns = [], maxDepth = 10, onProgress, options = {}) {
163
+ const packageJsonFiles = [];
164
+ const visitedPaths = new Set();
165
+ let directoriesScanned = 0;
166
+ let lastProgressAt = 0;
167
+ const progressIntervalMs = 250;
168
+ const concurrency = Math.max(1, Math.min(options.concurrency ?? 16, 64));
169
+ const skipSet = buildSkipSet(options.scanDirs);
170
+ const excludeRegexes = excludePatterns.map((pattern) => new RegExp(pattern, 'i'));
171
+ function shouldExcludePath(relativePath) {
172
+ return excludeRegexes.some((regex) => regex.test(relativePath));
173
+ }
174
+ function reportProgress(currentDir, force = false) {
175
+ if (!onProgress)
176
+ return;
177
+ const now = Date.now();
178
+ if (!force && now - lastProgressAt < progressIntervalMs) {
179
+ return;
180
+ }
181
+ lastProgressAt = now;
182
+ const relativePath = (0, path_1.relative)(rootDir, currentDir) || '.';
183
+ onProgress(relativePath, packageJsonFiles.length);
184
+ }
185
+ const pending = [];
186
+ let activeTasks = 0;
187
+ let failedError = null;
188
+ let resolveDone = null;
189
+ let rejectDone = null;
190
+ const done = new Promise((resolve, reject) => {
191
+ resolveDone = resolve;
192
+ rejectDone = reject;
193
+ });
194
+ function finishIfIdle() {
195
+ if (pending.length === 0 && activeTasks === 0) {
196
+ resolveDone?.();
197
+ }
198
+ }
199
+ function schedule(dir, depth) {
200
+ pending.push({ dir, depth });
201
+ pump();
202
+ }
203
+ async function processDirectory(dir, depth) {
204
+ if (depth > maxDepth) {
205
+ return;
206
+ }
207
+ let realPath;
208
+ try {
209
+ realPath = await fs_2.promises.realpath(dir);
210
+ }
211
+ catch {
212
+ return;
213
+ }
214
+ if (visitedPaths.has(realPath)) {
215
+ return;
216
+ }
217
+ visitedPaths.add(realPath);
218
+ directoriesScanned++;
219
+ if (directoriesScanned % 10 === 0 || directoriesScanned === 1) {
220
+ reportProgress(dir, true);
221
+ }
222
+ let files;
223
+ try {
224
+ files = await fs_2.promises.readdir(dir);
225
+ }
226
+ catch {
227
+ return;
228
+ }
229
+ for (const file of files) {
230
+ reportProgress(dir);
231
+ const fullPath = (0, path_1.join)(dir, file);
232
+ const relativePath = (0, path_1.relative)(rootDir, fullPath);
233
+ if (shouldExcludePath(relativePath)) {
234
+ continue;
235
+ }
236
+ let stat;
237
+ try {
238
+ stat = await fs_2.promises.stat(fullPath);
239
+ }
240
+ catch {
241
+ continue;
242
+ }
243
+ if (stat.isDirectory()) {
244
+ if (shouldTraverse(file, fullPath, relativePath, skipSet, options.onSkippedPackageDir)) {
245
+ schedule(fullPath, depth + 1);
246
+ }
247
+ }
248
+ else if (file === 'package.json' && stat.isFile()) {
249
+ packageJsonFiles.push(fullPath);
250
+ }
251
+ }
252
+ }
253
+ function pump() {
254
+ while (activeTasks < concurrency && pending.length > 0 && !failedError) {
255
+ const next = pending.shift();
256
+ if (!next)
257
+ break;
258
+ activeTasks++;
259
+ void processDirectory(next.dir, next.depth)
260
+ .catch((error) => {
261
+ if (!failedError) {
262
+ failedError = error;
263
+ rejectDone?.(error);
264
+ }
265
+ })
266
+ .finally(() => {
267
+ activeTasks--;
268
+ if (failedError) {
269
+ return;
270
+ }
271
+ pump();
272
+ finishIfIdle();
273
+ });
274
+ }
275
+ }
276
+ schedule(rootDir, 0);
277
+ await done;
278
+ return packageJsonFiles;
279
+ }
280
+ //# sourceMappingURL=scan.js.map