kiriya 0.1.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.
- package/CHANGELOG.md +58 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/dist/src/config/clean-rules.js +39 -0
- package/dist/src/config/config-keys.js +24 -0
- package/dist/src/config/dependency-directories.js +16 -0
- package/dist/src/config/modules.js +38 -0
- package/dist/src/config/protected-paths.js +32 -0
- package/dist/src/config/rebuildable-directories.js +20 -0
- package/dist/src/core/application/command-registry.js +57 -0
- package/dist/src/core/application/config-values.js +33 -0
- package/dist/src/core/application/path-guard.js +69 -0
- package/dist/src/core/application/paths.js +77 -0
- package/dist/src/core/application/plugin-loader.js +100 -0
- package/dist/src/core/application/process-ending.js +28 -0
- package/dist/src/core/application/root-scope.js +123 -0
- package/dist/src/core/application/safety.js +26 -0
- package/dist/src/core/application/text-input.js +44 -0
- package/dist/src/core/application/walk.js +34 -0
- package/dist/src/core/domain/command.js +8 -0
- package/dist/src/core/domain/encodings.js +70 -0
- package/dist/src/core/domain/errors.js +49 -0
- package/dist/src/core/domain/glob.js +57 -0
- package/dist/src/core/domain/global-options.js +10 -0
- package/dist/src/core/domain/input-schema.js +54 -0
- package/dist/src/core/domain/message.js +7 -0
- package/dist/src/core/domain/module.js +2 -0
- package/dist/src/core/domain/names.js +22 -0
- package/dist/src/core/domain/ports/clipboard.js +2 -0
- package/dist/src/core/domain/ports/clock.js +2 -0
- package/dist/src/core/domain/ports/command-catalog.js +2 -0
- package/dist/src/core/domain/ports/compression.js +2 -0
- package/dist/src/core/domain/ports/config-store.js +2 -0
- package/dist/src/core/domain/ports/confirmation.js +2 -0
- package/dist/src/core/domain/ports/environment.js +2 -0
- package/dist/src/core/domain/ports/file-content.js +2 -0
- package/dist/src/core/domain/ports/file-system.js +2 -0
- package/dist/src/core/domain/ports/hasher.js +2 -0
- package/dist/src/core/domain/ports/network.js +2 -0
- package/dist/src/core/domain/ports/opener.js +2 -0
- package/dist/src/core/domain/ports/passthrough.js +2 -0
- package/dist/src/core/domain/ports/plugin-inventory.js +2 -0
- package/dist/src/core/domain/ports/plugin-source.js +2 -0
- package/dist/src/core/domain/ports/port-table.js +2 -0
- package/dist/src/core/domain/ports/process-runner.js +2 -0
- package/dist/src/core/domain/ports/process-table.js +2 -0
- package/dist/src/core/domain/ports/protected-paths.js +2 -0
- package/dist/src/core/domain/ports/random-source.js +2 -0
- package/dist/src/core/domain/ports/standard-input.js +2 -0
- package/dist/src/core/domain/ports/system-info.js +2 -0
- package/dist/src/core/domain/ports/trash.js +2 -0
- package/dist/src/core/domain/secrets.js +59 -0
- package/dist/src/core/domain/targets.js +41 -0
- package/dist/src/core/domain/text-case.js +39 -0
- package/dist/src/core/domain/values/bytes.js +13 -0
- package/dist/src/core/domain/view.js +2 -0
- package/dist/src/core/infrastructure/node/can-listen.js +10 -0
- package/dist/src/core/infrastructure/node/closed-standard-input.adapter.js +12 -0
- package/dist/src/core/infrastructure/node/config-location.js +27 -0
- package/dist/src/core/infrastructure/node/fs-errors.js +22 -0
- package/dist/src/core/infrastructure/node/json-config-store.adapter.js +69 -0
- package/dist/src/core/infrastructure/node/node-compression.adapter.js +82 -0
- package/dist/src/core/infrastructure/node/node-environment.adapter.js +21 -0
- package/dist/src/core/infrastructure/node/node-file-content.adapter.js +99 -0
- package/dist/src/core/infrastructure/node/node-file-system.adapter.js +169 -0
- package/dist/src/core/infrastructure/node/node-hasher.adapter.js +32 -0
- package/dist/src/core/infrastructure/node/node-network.adapter.js +168 -0
- package/dist/src/core/infrastructure/node/node-plugin-source.adapter.js +89 -0
- package/dist/src/core/infrastructure/node/node-process-runner.adapter.js +48 -0
- package/dist/src/core/infrastructure/node/node-random-source.adapter.js +7 -0
- package/dist/src/core/infrastructure/node/node-standard-input.adapter.js +24 -0
- package/dist/src/core/infrastructure/node/node-system-info.adapter.js +63 -0
- package/dist/src/core/infrastructure/node/process-signals.js +51 -0
- package/dist/src/core/infrastructure/node/run-program.js +65 -0
- package/dist/src/core/infrastructure/node/system-clock.adapter.js +22 -0
- package/dist/src/core/infrastructure/node/typed-programs.js +45 -0
- package/dist/src/core/infrastructure/platform/linux/freedesktop-trash.adapter.js +115 -0
- package/dist/src/core/infrastructure/platform/linux/linux-clipboard.adapter.js +70 -0
- package/dist/src/core/infrastructure/platform/linux/linux-opener.adapter.js +21 -0
- package/dist/src/core/infrastructure/platform/linux/linux-port-table.adapter.js +97 -0
- package/dist/src/core/infrastructure/platform/linux/linux-process-table.adapter.js +61 -0
- package/dist/src/core/infrastructure/platform/macos/macos-clipboard.adapter.js +29 -0
- package/dist/src/core/infrastructure/platform/macos/macos-opener.adapter.js +13 -0
- package/dist/src/core/infrastructure/platform/macos/macos-port-table.adapter.js +86 -0
- package/dist/src/core/infrastructure/platform/macos/macos-process-table.adapter.js +62 -0
- package/dist/src/core/infrastructure/platform/macos/macos-trash.adapter.js +64 -0
- package/dist/src/core/infrastructure/platform/windows/system32.js +6 -0
- package/dist/src/core/infrastructure/platform/windows/windows-clipboard.adapter.js +50 -0
- package/dist/src/core/infrastructure/platform/windows/windows-opener.adapter.js +11 -0
- package/dist/src/core/infrastructure/platform/windows/windows-port-table.adapter.js +38 -0
- package/dist/src/core/infrastructure/platform/windows/windows-process-table.adapter.js +134 -0
- package/dist/src/core/infrastructure/platform/windows/windows-trash.adapter.js +86 -0
- package/dist/src/core/presentation/cli/argv.js +83 -0
- package/dist/src/core/presentation/cli/cli-application.js +201 -0
- package/dist/src/core/presentation/cli/cli-view-format.js +46 -0
- package/dist/src/core/presentation/cli/help.js +202 -0
- package/dist/src/core/presentation/cli/json-output.js +28 -0
- package/dist/src/core/presentation/cli/style.js +21 -0
- package/dist/src/core/presentation/cli/suggest.js +25 -0
- package/dist/src/core/presentation/cli/terminal-confirmation.js +63 -0
- package/dist/src/core/presentation/ended-processes.js +18 -0
- package/dist/src/core/presentation/i18n/translator.js +23 -0
- package/dist/src/core/presentation/list-preview.js +10 -0
- package/dist/src/core/presentation/mcp/elicitation.js +55 -0
- package/dist/src/core/presentation/mcp/mcp-application.js +60 -0
- package/dist/src/core/presentation/mcp/mcp-confirmation.js +59 -0
- package/dist/src/core/presentation/mcp/mcp-server.js +355 -0
- package/dist/src/core/presentation/mcp/protocol.js +32 -0
- package/dist/src/core/presentation/mcp/request-state.js +66 -0
- package/dist/src/core/presentation/mcp/tool-definitions.js +149 -0
- package/dist/src/core/presentation/mcp/tool-results.js +56 -0
- package/dist/src/i18n/locales/en.js +827 -0
- package/dist/src/main.js +154 -0
- package/dist/src/modules/archive/application/archive-sources.js +55 -0
- package/dist/src/modules/archive/application/create-tar.use-case.js +123 -0
- package/dist/src/modules/archive/application/create-zip.use-case.js +106 -0
- package/dist/src/modules/archive/application/extract-tar.use-case.js +240 -0
- package/dist/src/modules/archive/application/extract-zip.use-case.js +181 -0
- package/dist/src/modules/archive/application/tar-reader.js +45 -0
- package/dist/src/modules/archive/application/zip-reader.js +57 -0
- package/dist/src/modules/archive/archive.module.js +27 -0
- package/dist/src/modules/archive/domain/crc32.js +18 -0
- package/dist/src/modules/archive/domain/tar-format.js +97 -0
- package/dist/src/modules/archive/domain/tar-stream.js +223 -0
- package/dist/src/modules/archive/domain/zip-format.js +158 -0
- package/dist/src/modules/archive/presentation/tar.view.js +17 -0
- package/dist/src/modules/archive/presentation/untar.view.js +30 -0
- package/dist/src/modules/archive/presentation/unzip.view.js +24 -0
- package/dist/src/modules/archive/presentation/zip.view.js +17 -0
- package/dist/src/modules/clip/application/copy-text.use-case.js +43 -0
- package/dist/src/modules/clip/application/paste-text.use-case.js +25 -0
- package/dist/src/modules/clip/clip.module.js +16 -0
- package/dist/src/modules/clip/presentation/clip.views.js +7 -0
- package/dist/src/modules/completion/application/print-script.use-case.js +38 -0
- package/dist/src/modules/completion/application/suggest-words.use-case.js +37 -0
- package/dist/src/modules/completion/completion.module.js +14 -0
- package/dist/src/modules/completion/domain/scripts.js +91 -0
- package/dist/src/modules/completion/domain/suggest.js +134 -0
- package/dist/src/modules/completion/presentation/completion.views.js +6 -0
- package/dist/src/modules/config/application/config-commands.js +14 -0
- package/dist/src/modules/config/application/get-setting.use-case.js +25 -0
- package/dist/src/modules/config/application/list-config-keys.use-case.js +26 -0
- package/dist/src/modules/config/application/list-settings.use-case.js +28 -0
- package/dist/src/modules/config/application/set-setting.use-case.js +53 -0
- package/dist/src/modules/config/application/show-config-path.use-case.js +21 -0
- package/dist/src/modules/config/application/unset-setting.use-case.js +28 -0
- package/dist/src/modules/config/config.module.js +28 -0
- package/dist/src/modules/config/presentation/config.views.js +33 -0
- package/dist/src/modules/convert/application/convert-case.use-case.js +38 -0
- package/dist/src/modules/convert/application/convert-codecs.use-case.js +92 -0
- package/dist/src/modules/convert/application/convert-input.js +16 -0
- package/dist/src/modules/convert/application/convert-json.use-case.js +51 -0
- package/dist/src/modules/convert/application/convert-time.use-case.js +39 -0
- package/dist/src/modules/convert/application/decode-jwt.use-case.js +52 -0
- package/dist/src/modules/convert/convert.module.js +29 -0
- package/dist/src/modules/convert/domain/json-syntax.js +135 -0
- package/dist/src/modules/convert/domain/jwt.js +29 -0
- package/dist/src/modules/convert/domain/moments.js +20 -0
- package/dist/src/modules/convert/presentation/convert.views.js +44 -0
- package/dist/src/modules/docker/application/clean-engine.use-case.js +77 -0
- package/dist/src/modules/docker/application/compose-project.js +45 -0
- package/dist/src/modules/docker/application/docker-engine.js +40 -0
- package/dist/src/modules/docker/application/list-containers.use-case.js +69 -0
- package/dist/src/modules/docker/application/run-compose.use-case.js +83 -0
- package/dist/src/modules/docker/application/show-logs.use-case.js +51 -0
- package/dist/src/modules/docker/application/stop-project.use-case.js +56 -0
- package/dist/src/modules/docker/docker.module.js +23 -0
- package/dist/src/modules/docker/domain/compose-file.js +105 -0
- package/dist/src/modules/docker/domain/engine-output.js +65 -0
- package/dist/src/modules/docker/presentation/docker.views.js +63 -0
- package/dist/src/modules/doctor/application/run-checks.use-case.js +128 -0
- package/dist/src/modules/doctor/doctor.module.js +13 -0
- package/dist/src/modules/doctor/domain/versions.js +22 -0
- package/dist/src/modules/doctor/presentation/doctor.view.js +30 -0
- package/dist/src/modules/env/application/check-dotenv.use-case.js +116 -0
- package/dist/src/modules/env/application/inspect-path.use-case.js +71 -0
- package/dist/src/modules/env/application/path-entries.js +28 -0
- package/dist/src/modules/env/application/show-variables.use-case.js +47 -0
- package/dist/src/modules/env/domain/dotenv.js +62 -0
- package/dist/src/modules/env/env.module.js +17 -0
- package/dist/src/modules/env/presentation/env.views.js +43 -0
- package/dist/src/modules/files/application/clean-builds.use-case.js +149 -0
- package/dist/src/modules/files/application/compare-paths.use-case.js +127 -0
- package/dist/src/modules/files/application/copy-paths.use-case.js +101 -0
- package/dist/src/modules/files/application/create-paths.use-case.js +85 -0
- package/dist/src/modules/files/application/delete-paths.use-case.js +127 -0
- package/dist/src/modules/files/application/find-duplicates.use-case.js +105 -0
- package/dist/src/modules/files/application/find-files.use-case.js +131 -0
- package/dist/src/modules/files/application/hash-files.use-case.js +73 -0
- package/dist/src/modules/files/application/list-entries.use-case.js +87 -0
- package/dist/src/modules/files/application/measure-folders.use-case.js +102 -0
- package/dist/src/modules/files/application/move-entry.js +19 -0
- package/dist/src/modules/files/application/move-paths.use-case.js +82 -0
- package/dist/src/modules/files/application/read-text.use-case.js +92 -0
- package/dist/src/modules/files/application/rename-paths.use-case.js +184 -0
- package/dist/src/modules/files/application/replace-text.use-case.js +146 -0
- package/dist/src/modules/files/application/search-text.use-case.js +115 -0
- package/dist/src/modules/files/application/show-info.use-case.js +80 -0
- package/dist/src/modules/files/application/show-tree.use-case.js +75 -0
- package/dist/src/modules/files/application/sync-folders.use-case.js +162 -0
- package/dist/src/modules/files/application/text-files.js +46 -0
- package/dist/src/modules/files/application/transfers.js +35 -0
- package/dist/src/modules/files/domain/case-style.js +18 -0
- package/dist/src/modules/files/domain/filters.js +9 -0
- package/dist/src/modules/files/domain/sync-plan.js +35 -0
- package/dist/src/modules/files/domain/text-encoding.js +75 -0
- package/dist/src/modules/files/domain/values.js +28 -0
- package/dist/src/modules/files/files.module.js +70 -0
- package/dist/src/modules/files/presentation/clean.view.js +15 -0
- package/dist/src/modules/files/presentation/compare.view.js +50 -0
- package/dist/src/modules/files/presentation/copy.view.js +17 -0
- package/dist/src/modules/files/presentation/delete.view.js +16 -0
- package/dist/src/modules/files/presentation/dupes.view.js +20 -0
- package/dist/src/modules/files/presentation/find.view.js +16 -0
- package/dist/src/modules/files/presentation/grep.view.js +27 -0
- package/dist/src/modules/files/presentation/hash.view.js +15 -0
- package/dist/src/modules/files/presentation/info.view.js +36 -0
- package/dist/src/modules/files/presentation/list.view.js +17 -0
- package/dist/src/modules/files/presentation/move.view.js +14 -0
- package/dist/src/modules/files/presentation/new.view.js +8 -0
- package/dist/src/modules/files/presentation/read.view.js +6 -0
- package/dist/src/modules/files/presentation/rename.view.js +16 -0
- package/dist/src/modules/files/presentation/replace.view.js +26 -0
- package/dist/src/modules/files/presentation/size.view.js +24 -0
- package/dist/src/modules/files/presentation/sync.view.js +37 -0
- package/dist/src/modules/files/presentation/tree.view.js +22 -0
- package/dist/src/modules/gen/application/gen-options.js +18 -0
- package/dist/src/modules/gen/application/generate-passwords.use-case.js +39 -0
- package/dist/src/modules/gen/application/generate-tokens.use-case.js +38 -0
- package/dist/src/modules/gen/application/generate-ulids.use-case.js +40 -0
- package/dist/src/modules/gen/application/generate-uuids.use-case.js +32 -0
- package/dist/src/modules/gen/domain/identifiers.js +65 -0
- package/dist/src/modules/gen/domain/secrets.js +43 -0
- package/dist/src/modules/gen/gen.module.js +20 -0
- package/dist/src/modules/git/application/fetch-repositories.use-case.js +50 -0
- package/dist/src/modules/git/application/git-client.js +62 -0
- package/dist/src/modules/git/application/pull-repositories.use-case.js +67 -0
- package/dist/src/modules/git/application/repositories.js +67 -0
- package/dist/src/modules/git/application/show-status.use-case.js +49 -0
- package/dist/src/modules/git/application/switch-branch.use-case.js +118 -0
- package/dist/src/modules/git/domain/repository-status.js +37 -0
- package/dist/src/modules/git/git.module.js +25 -0
- package/dist/src/modules/git/presentation/git.views.js +100 -0
- package/dist/src/modules/net/application/check-connection.use-case.js +71 -0
- package/dist/src/modules/net/application/list-addresses.use-case.js +29 -0
- package/dist/src/modules/net/application/lookup-name.use-case.js +55 -0
- package/dist/src/modules/net/net.module.js +17 -0
- package/dist/src/modules/net/presentation/net.views.js +39 -0
- package/dist/src/modules/open/application/open-target.use-case.js +53 -0
- package/dist/src/modules/open/domain/targets.js +45 -0
- package/dist/src/modules/open/open.module.js +12 -0
- package/dist/src/modules/open/presentation/open.view.js +6 -0
- package/dist/src/modules/port/application/end-port-owners.use-case.js +68 -0
- package/dist/src/modules/port/application/find-free-port.use-case.js +40 -0
- package/dist/src/modules/port/application/find-listeners.use-case.js +34 -0
- package/dist/src/modules/port/application/port-owners.js +25 -0
- package/dist/src/modules/port/domain/port-number.js +8 -0
- package/dist/src/modules/port/port.module.js +17 -0
- package/dist/src/modules/port/presentation/port.views.js +27 -0
- package/dist/src/modules/proc/application/end-processes.use-case.js +77 -0
- package/dist/src/modules/proc/application/find-processes.use-case.js +50 -0
- package/dist/src/modules/proc/application/list-processes.use-case.js +41 -0
- package/dist/src/modules/proc/application/show-tree.use-case.js +40 -0
- package/dist/src/modules/proc/domain/process-tree.js +55 -0
- package/dist/src/modules/proc/presentation/proc.views.js +28 -0
- package/dist/src/modules/proc/proc.module.js +19 -0
- package/dist/src/modules/sys/application/create-report.use-case.js +30 -0
- package/dist/src/modules/sys/application/find-tools.use-case.js +20 -0
- package/dist/src/modules/sys/application/machine-facts.js +44 -0
- package/dist/src/modules/sys/application/show-info.use-case.js +29 -0
- package/dist/src/modules/sys/domain/tools.js +17 -0
- package/dist/src/modules/sys/presentation/sys.views.js +67 -0
- package/dist/src/modules/sys/sys.module.js +17 -0
- package/dist/src/modules/wait/application/poll.js +26 -0
- package/dist/src/modules/wait/application/wait-file.use-case.js +55 -0
- package/dist/src/modules/wait/application/wait-port.use-case.js +76 -0
- package/dist/src/modules/wait/application/wait-url.use-case.js +71 -0
- package/dist/src/modules/wait/domain/reasons.js +14 -0
- package/dist/src/modules/wait/domain/wait-input.js +50 -0
- package/dist/src/modules/wait/presentation/wait.views.js +31 -0
- package/dist/src/modules/wait/wait.module.js +21 -0
- package/package.json +57 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { chmod, cp, lstat, mkdir, readdir, readlink, realpath, rename, rm, rmdir, stat, utimes, writeFile, } from "node:fs/promises";
|
|
2
|
+
import { ConflictError } from "../../domain/errors.js";
|
|
3
|
+
import { errorCode, isMissing, translateFsError } from "./fs-errors.js";
|
|
4
|
+
function kindOf(entry) {
|
|
5
|
+
if (entry.isSymbolicLink())
|
|
6
|
+
return "symlink";
|
|
7
|
+
if (entry.isDirectory())
|
|
8
|
+
return "directory";
|
|
9
|
+
if (entry.isFile())
|
|
10
|
+
return "file";
|
|
11
|
+
return "other";
|
|
12
|
+
}
|
|
13
|
+
function toStat(stats) {
|
|
14
|
+
return {
|
|
15
|
+
kind: kindOf(stats),
|
|
16
|
+
size: stats.size,
|
|
17
|
+
modifiedMs: Math.floor(stats.mtimeMs),
|
|
18
|
+
createdMs: Math.floor(stats.birthtimeMs),
|
|
19
|
+
accessedMs: Math.floor(stats.atimeMs),
|
|
20
|
+
mode: stats.mode,
|
|
21
|
+
device: stats.dev,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export class NodeFileSystemAdapter {
|
|
25
|
+
async lstat(path) {
|
|
26
|
+
try {
|
|
27
|
+
return toStat(await lstat(path));
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (isMissing(error))
|
|
31
|
+
return null;
|
|
32
|
+
return translateFsError(error, path);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async stat(path) {
|
|
36
|
+
try {
|
|
37
|
+
return toStat(await stat(path));
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (isMissing(error))
|
|
41
|
+
return null;
|
|
42
|
+
return translateFsError(error, path);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async readDirectory(path) {
|
|
46
|
+
try {
|
|
47
|
+
const entries = await readdir(path, { withFileTypes: true });
|
|
48
|
+
return entries.map((entry) => ({ name: entry.name, kind: kindOf(entry) }));
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return translateFsError(error, path);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async readLink(path) {
|
|
55
|
+
try {
|
|
56
|
+
return await readlink(path);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
return translateFsError(error, path);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async realPath(path) {
|
|
63
|
+
try {
|
|
64
|
+
return await realpath(path);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
if (isMissing(error))
|
|
68
|
+
return null;
|
|
69
|
+
return translateFsError(error, path);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async createDirectory(path) {
|
|
73
|
+
try {
|
|
74
|
+
await mkdir(path, { recursive: true });
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
translateFsError(error, path);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async createFile(path, content) {
|
|
81
|
+
try {
|
|
82
|
+
await writeFile(path, content, { encoding: "utf8", flag: "wx" });
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
translateFsError(error, path);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async remove(path) {
|
|
89
|
+
try {
|
|
90
|
+
await rm(path, { recursive: true, force: false, maxRetries: 3 });
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
translateFsError(error, path);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async copy(source, destination, overwrite) {
|
|
97
|
+
try {
|
|
98
|
+
await cp(source, destination, {
|
|
99
|
+
recursive: true,
|
|
100
|
+
preserveTimestamps: true,
|
|
101
|
+
force: overwrite,
|
|
102
|
+
errorOnExist: !overwrite,
|
|
103
|
+
verbatimSymlinks: true,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
translateFsError(error, destination);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async move(source, destination) {
|
|
111
|
+
// rename replaces an existing file on Linux and macOS, so the check comes first.
|
|
112
|
+
if ((await this.lstat(destination)) !== null)
|
|
113
|
+
throw new ConflictError("core.fs.exists", { path: destination });
|
|
114
|
+
try {
|
|
115
|
+
await rename(source, destination);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
if (errorCode(error) !== "EXDEV")
|
|
120
|
+
translateFsError(error, source);
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
await this.copy(source, destination, false);
|
|
124
|
+
await rm(source, { recursive: true, force: true, maxRetries: 3 });
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
translateFsError(error, source);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async setTimes(path, accessedMs, modifiedMs) {
|
|
131
|
+
try {
|
|
132
|
+
await utimes(path, new Date(accessedMs), new Date(modifiedMs));
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
translateFsError(error, path);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async setMode(path, mode) {
|
|
139
|
+
try {
|
|
140
|
+
await chmod(path, mode);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
translateFsError(error, path);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async sameEntry(first, second) {
|
|
147
|
+
try {
|
|
148
|
+
const [a, b] = await Promise.all([lstat(first, { bigint: true }), lstat(second, { bigint: true })]);
|
|
149
|
+
// Some file systems, such as FAT, report no file ids at all.
|
|
150
|
+
return a.ino !== 0n && a.ino === b.ino && a.dev === b.dev;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async removeEmptyDirectory(path) {
|
|
157
|
+
try {
|
|
158
|
+
await rmdir(path);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
const code = errorCode(error);
|
|
163
|
+
if (code === "ENOTEMPTY" || code === "EEXIST")
|
|
164
|
+
return false;
|
|
165
|
+
return translateFsError(error, path);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=node-file-system.adapter.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { open } from "node:fs/promises";
|
|
3
|
+
import { translateFsError } from "./fs-errors.js";
|
|
4
|
+
const CHUNK_BYTES = 1024 * 1024;
|
|
5
|
+
export class NodeHasherAdapter {
|
|
6
|
+
async hashFile(path, algorithm, limitBytes = Number.POSITIVE_INFINITY) {
|
|
7
|
+
const hash = createHash(algorithm);
|
|
8
|
+
const handle = await open(path, "r").catch((error) => translateFsError(error, path));
|
|
9
|
+
try {
|
|
10
|
+
const buffer = new Uint8Array(CHUNK_BYTES);
|
|
11
|
+
let total = 0;
|
|
12
|
+
for (;;) {
|
|
13
|
+
const wanted = Math.min(buffer.length, limitBytes - total);
|
|
14
|
+
if (wanted <= 0)
|
|
15
|
+
break;
|
|
16
|
+
const { bytesRead } = await handle.read(buffer, 0, wanted, null);
|
|
17
|
+
if (bytesRead === 0)
|
|
18
|
+
break;
|
|
19
|
+
hash.update(buffer.subarray(0, bytesRead));
|
|
20
|
+
total += bytesRead;
|
|
21
|
+
}
|
|
22
|
+
return hash.digest("hex");
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return translateFsError(error, path);
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
await handle.close();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=node-hasher.adapter.js.map
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { Resolver, lookup as systemLookup } from "node:dns/promises";
|
|
2
|
+
import { request as httpRequest } from "node:http";
|
|
3
|
+
import { request as httpsRequest } from "node:https";
|
|
4
|
+
import { createConnection } from "node:net";
|
|
5
|
+
import { networkInterfaces } from "node:os";
|
|
6
|
+
import { InterruptedError } from "../../domain/errors.js";
|
|
7
|
+
const CONNECT_FAILURES = {
|
|
8
|
+
ECONNREFUSED: "refused",
|
|
9
|
+
ETIMEDOUT: "timeout",
|
|
10
|
+
ENOTFOUND: "not-found",
|
|
11
|
+
EAI_AGAIN: "not-found",
|
|
12
|
+
EAI_NONAME: "not-found",
|
|
13
|
+
EHOSTUNREACH: "unreachable",
|
|
14
|
+
ENETUNREACH: "unreachable",
|
|
15
|
+
EADDRNOTAVAIL: "unreachable",
|
|
16
|
+
};
|
|
17
|
+
const LOOKUP_FAILURES = {
|
|
18
|
+
ENOTFOUND: "not-found",
|
|
19
|
+
ENODATA: "not-found",
|
|
20
|
+
EAI_NONAME: "not-found",
|
|
21
|
+
ETIMEOUT: "timeout",
|
|
22
|
+
EAI_AGAIN: "timeout",
|
|
23
|
+
};
|
|
24
|
+
/** The code Node.js gives an error, looking inside the AggregateError that trying several addresses produces. */
|
|
25
|
+
function errorCode(error) {
|
|
26
|
+
if (typeof error !== "object" || error === null)
|
|
27
|
+
return null;
|
|
28
|
+
if ("code" in error && typeof error.code === "string")
|
|
29
|
+
return error.code;
|
|
30
|
+
if (error instanceof AggregateError)
|
|
31
|
+
return errorCode(error.errors[0]);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
/** A certificate or TLS problem is told apart, since trying again does not fix it by itself. */
|
|
35
|
+
function httpFailure(code) {
|
|
36
|
+
if (code === null)
|
|
37
|
+
return "failed";
|
|
38
|
+
if (code.startsWith("ERR_TLS_") || code.startsWith("ERR_SSL_") || /CERT|SELF_SIGNED/.test(code))
|
|
39
|
+
return "tls";
|
|
40
|
+
return CONNECT_FAILURES[code] ?? "failed";
|
|
41
|
+
}
|
|
42
|
+
function lookupFailure(error) {
|
|
43
|
+
const code = errorCode(error);
|
|
44
|
+
return { ok: false, failure: (code === null ? undefined : LOOKUP_FAILURES[code]) ?? "failed", code };
|
|
45
|
+
}
|
|
46
|
+
async function records(resolver, name, type) {
|
|
47
|
+
const plain = (recordType) => (value) => ({ type: recordType, value, priority: null });
|
|
48
|
+
switch (type) {
|
|
49
|
+
case "a":
|
|
50
|
+
return (await resolver.resolve4(name)).map(plain("A"));
|
|
51
|
+
case "aaaa":
|
|
52
|
+
return (await resolver.resolve6(name)).map(plain("AAAA"));
|
|
53
|
+
case "cname":
|
|
54
|
+
return (await resolver.resolveCname(name)).map(plain("CNAME"));
|
|
55
|
+
case "ns":
|
|
56
|
+
return (await resolver.resolveNs(name)).map(plain("NS"));
|
|
57
|
+
case "txt":
|
|
58
|
+
// A TXT record arrives in chunks of at most 255 characters.
|
|
59
|
+
return (await resolver.resolveTxt(name)).map((chunks) => plain("TXT")(chunks.join("")));
|
|
60
|
+
case "mx":
|
|
61
|
+
return (await resolver.resolveMx(name))
|
|
62
|
+
.sort((first, second) => first.priority - second.priority)
|
|
63
|
+
.map((entry) => ({ type: "MX", value: entry.exchange, priority: entry.priority }));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export class NodeNetworkAdapter {
|
|
67
|
+
addresses() {
|
|
68
|
+
return Object.entries(networkInterfaces()).flatMap(([interfaceName, entries]) => (entries ?? []).map((entry) => ({
|
|
69
|
+
interfaceName,
|
|
70
|
+
family: entry.family,
|
|
71
|
+
address: entry.address,
|
|
72
|
+
cidr: entry.cidr,
|
|
73
|
+
mac: entry.mac,
|
|
74
|
+
internal: entry.internal,
|
|
75
|
+
})));
|
|
76
|
+
}
|
|
77
|
+
connect(host, port, timeoutMs, signal) {
|
|
78
|
+
if (signal.aborted)
|
|
79
|
+
return Promise.reject(new InterruptedError("core.error.interrupted"));
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
const started = performance.now();
|
|
82
|
+
const elapsed = () => Math.round(performance.now() - started);
|
|
83
|
+
const socket = createConnection({ host, port });
|
|
84
|
+
// null means the run was interrupted.
|
|
85
|
+
const settle = (outcome) => {
|
|
86
|
+
clearTimeout(timer);
|
|
87
|
+
signal.removeEventListener("abort", onAbort);
|
|
88
|
+
socket.destroy();
|
|
89
|
+
if (outcome === null)
|
|
90
|
+
reject(new InterruptedError("core.error.interrupted"));
|
|
91
|
+
else
|
|
92
|
+
resolve(outcome);
|
|
93
|
+
};
|
|
94
|
+
const onAbort = () => settle(null);
|
|
95
|
+
const timer = setTimeout(() => settle({ ok: false, failure: "timeout", code: null, ms: elapsed() }), timeoutMs);
|
|
96
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
97
|
+
socket.once("connect", () => settle({ ok: true, address: socket.remoteAddress ?? host, ms: elapsed() }));
|
|
98
|
+
socket.once("error", (error) => {
|
|
99
|
+
const code = errorCode(error);
|
|
100
|
+
const failure = (code === null ? undefined : CONNECT_FAILURES[code]) ?? "failed";
|
|
101
|
+
settle({ ok: false, failure, code, ms: elapsed() });
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
request(url, timeoutMs, signal) {
|
|
106
|
+
if (signal.aborted)
|
|
107
|
+
return Promise.reject(new InterruptedError("core.error.interrupted"));
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
const started = performance.now();
|
|
110
|
+
const elapsed = () => Math.round(performance.now() - started);
|
|
111
|
+
const target = new URL(url);
|
|
112
|
+
const send = target.protocol === "https:" ? httpsRequest : httpRequest;
|
|
113
|
+
const outgoing = send(target, { method: "GET", headers: { "user-agent": "kiriya" } });
|
|
114
|
+
let settled = false;
|
|
115
|
+
// null means the run was interrupted.
|
|
116
|
+
const settle = (outcome) => {
|
|
117
|
+
if (settled)
|
|
118
|
+
return;
|
|
119
|
+
settled = true;
|
|
120
|
+
clearTimeout(timer);
|
|
121
|
+
signal.removeEventListener("abort", onAbort);
|
|
122
|
+
outgoing.destroy();
|
|
123
|
+
if (outcome === null)
|
|
124
|
+
reject(new InterruptedError("core.error.interrupted"));
|
|
125
|
+
else
|
|
126
|
+
resolve(outcome);
|
|
127
|
+
};
|
|
128
|
+
const onAbort = () => settle(null);
|
|
129
|
+
const timer = setTimeout(() => settle({ ok: false, failure: "timeout", code: null, ms: elapsed() }), timeoutMs);
|
|
130
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
131
|
+
outgoing.once("response", (response) => {
|
|
132
|
+
// The status is all a wait needs, so the body is never read.
|
|
133
|
+
response.destroy();
|
|
134
|
+
settle({ ok: true, status: response.statusCode ?? 0, ms: elapsed() });
|
|
135
|
+
});
|
|
136
|
+
// Kept after settling, because destroying the request can still emit an error.
|
|
137
|
+
outgoing.on("error", (error) => {
|
|
138
|
+
const code = errorCode(error);
|
|
139
|
+
settle({ ok: false, failure: httpFailure(code), code, ms: elapsed() });
|
|
140
|
+
});
|
|
141
|
+
outgoing.end();
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
async lookup(name) {
|
|
145
|
+
try {
|
|
146
|
+
const found = await systemLookup(name, { all: true });
|
|
147
|
+
const addresses = found.map((entry) => ({
|
|
148
|
+
type: entry.family === 6 ? "AAAA" : "A",
|
|
149
|
+
value: entry.address,
|
|
150
|
+
priority: null,
|
|
151
|
+
}));
|
|
152
|
+
return { ok: true, records: addresses };
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
return lookupFailure(error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async resolve(name, type, timeoutMs) {
|
|
159
|
+
const resolver = new Resolver({ timeout: timeoutMs, tries: 2 });
|
|
160
|
+
try {
|
|
161
|
+
return { ok: true, records: await records(resolver, name, type) };
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
return lookupFailure(error);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=node-network.adapter.js.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
|
+
import { NotFoundError, OperationFailedError } from "../../domain/errors.js";
|
|
5
|
+
const PACKAGE_NAME = /^(@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*$/i;
|
|
6
|
+
async function kindOf(target) {
|
|
7
|
+
try {
|
|
8
|
+
const found = await stat(target);
|
|
9
|
+
return found.isDirectory() ? "directory" : found.isFile() ? "file" : null;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
async function manifestOf(folder) {
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(await readFile(path.join(folder, "package.json"), "utf8"));
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const field = (value, name) => typeof value === "object" && value !== null ? value[name] : undefined;
|
|
24
|
+
/** A package folder's ES module entry: its "." export, then "main", then index.js. */
|
|
25
|
+
function entryFile(folder, manifest) {
|
|
26
|
+
const root = typeof manifest?.exports === "string" ? manifest.exports : field(manifest?.exports, ".");
|
|
27
|
+
const conditional = typeof root === "string" ? root : (field(root, "import") ?? field(root, "default"));
|
|
28
|
+
if (typeof conditional === "string")
|
|
29
|
+
return path.resolve(folder, conditional);
|
|
30
|
+
if (typeof manifest?.main === "string")
|
|
31
|
+
return path.resolve(folder, manifest.main);
|
|
32
|
+
return path.join(folder, "index.js");
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Where a package name may be installed: node_modules above kiriya itself, which is
|
|
36
|
+
* where global installs sit side by side, then node_modules above the configuration file.
|
|
37
|
+
*/
|
|
38
|
+
function packageFolders(name, baseDirectory) {
|
|
39
|
+
const folders = [];
|
|
40
|
+
for (const start of [path.dirname(fileURLToPath(import.meta.url)), baseDirectory]) {
|
|
41
|
+
for (let current = start;; current = path.dirname(current)) {
|
|
42
|
+
folders.push(path.join(current, "node_modules", name));
|
|
43
|
+
if (path.dirname(current) === current)
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return folders;
|
|
48
|
+
}
|
|
49
|
+
export class NodePluginSource {
|
|
50
|
+
async load(entry, baseDirectory) {
|
|
51
|
+
let folder = null;
|
|
52
|
+
let file = null;
|
|
53
|
+
if (entry.startsWith(".") || path.isAbsolute(entry)) {
|
|
54
|
+
const target = path.resolve(baseDirectory, entry);
|
|
55
|
+
const kind = await kindOf(target);
|
|
56
|
+
if (kind === "file")
|
|
57
|
+
file = target;
|
|
58
|
+
if (kind === "directory")
|
|
59
|
+
folder = target;
|
|
60
|
+
}
|
|
61
|
+
else if (PACKAGE_NAME.test(entry)) {
|
|
62
|
+
for (const candidate of packageFolders(entry, baseDirectory)) {
|
|
63
|
+
if ((await kindOf(candidate)) === "directory") {
|
|
64
|
+
folder = candidate;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (folder === null && file === null)
|
|
70
|
+
throw new NotFoundError("core.plugin.not-found", { entry });
|
|
71
|
+
const manifest = folder === null ? null : await manifestOf(folder);
|
|
72
|
+
const location = file ?? entryFile(folder ?? "", manifest);
|
|
73
|
+
let exports;
|
|
74
|
+
try {
|
|
75
|
+
exports = await import(pathToFileURL(location).href);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
79
|
+
throw new OperationFailedError("core.plugin.import-failed", { entry, detail }, { cause: error });
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
location,
|
|
83
|
+
name: typeof manifest?.name === "string" ? manifest.name : null,
|
|
84
|
+
version: typeof manifest?.version === "string" ? manifest.version : null,
|
|
85
|
+
exports,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=node-plugin-source.adapter.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { access, stat } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { CapabilityUnavailableError, InterruptedError } from "../../domain/errors.js";
|
|
5
|
+
import { runProgram } from "./run-program.js";
|
|
6
|
+
/**
|
|
7
|
+
* Without a shell, Windows starts only real executables, so PATH is searched for
|
|
8
|
+
* .exe and .com files there; batch files would need cmd.exe and are not found.
|
|
9
|
+
*/
|
|
10
|
+
const WINDOWS_EXTENSIONS = [".exe", ".com"];
|
|
11
|
+
export class NodeProcessRunnerAdapter {
|
|
12
|
+
async find(program) {
|
|
13
|
+
const windows = process.platform === "win32";
|
|
14
|
+
const extensions = !windows || path.extname(program) !== "" ? [""] : WINDOWS_EXTENSIONS;
|
|
15
|
+
const directories = (process.env["PATH"] ?? "").split(path.delimiter).filter((directory) => directory !== "");
|
|
16
|
+
for (const directory of directories) {
|
|
17
|
+
for (const extension of extensions) {
|
|
18
|
+
const candidate = path.join(directory, `${program}${extension}`);
|
|
19
|
+
try {
|
|
20
|
+
if (!(await stat(candidate)).isFile())
|
|
21
|
+
continue;
|
|
22
|
+
if (!windows)
|
|
23
|
+
await access(candidate, constants.X_OK);
|
|
24
|
+
return candidate;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Not in this folder, or not executable: keep looking.
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
async run(program, args, options = {}) {
|
|
34
|
+
try {
|
|
35
|
+
const result = await runProgram(program, args, options);
|
|
36
|
+
return { code: result.code, stdout: result.stdout, stderr: result.stderr };
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
// Aborting the signal kills the program, which Node.js reports as an error.
|
|
40
|
+
if (options.signal?.aborted === true) {
|
|
41
|
+
throw new InterruptedError("core.error.interrupted", {}, { cause: error });
|
|
42
|
+
}
|
|
43
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
44
|
+
throw new CapabilityUnavailableError("core.process.cannot-start", { program, detail }, { cause: error });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=node-process-runner.adapter.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { OperationFailedError } from "../../domain/errors.js";
|
|
2
|
+
import { formatBytes } from "../../domain/values/bytes.js";
|
|
3
|
+
export class NodeStandardInput {
|
|
4
|
+
stream;
|
|
5
|
+
constructor(stream = process.stdin) {
|
|
6
|
+
this.stream = stream;
|
|
7
|
+
}
|
|
8
|
+
get isTerminal() {
|
|
9
|
+
return this.stream.isTTY === true;
|
|
10
|
+
}
|
|
11
|
+
async read(maxBytes) {
|
|
12
|
+
const chunks = [];
|
|
13
|
+
let total = 0;
|
|
14
|
+
for await (const chunk of this.stream) {
|
|
15
|
+
const buffer = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
16
|
+
total += buffer.length;
|
|
17
|
+
if (total > maxBytes)
|
|
18
|
+
throw new OperationFailedError("core.stdin.too-large", { limit: formatBytes(maxBytes) });
|
|
19
|
+
chunks.push(buffer);
|
|
20
|
+
}
|
|
21
|
+
return Buffer.concat(chunks);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=node-standard-input.adapter.js.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
/** PRETTY_NAME from the text of an os-release file, or null. */
|
|
4
|
+
export function prettyNameFromOsRelease(text) {
|
|
5
|
+
for (const line of text.split(/\r?\n/)) {
|
|
6
|
+
const match = /^PRETTY_NAME=(.*)$/.exec(line.trim());
|
|
7
|
+
if (match === null)
|
|
8
|
+
continue;
|
|
9
|
+
const value = (match[1] ?? "").trim().replace(/^(["'])(.*)\1$/, "$2");
|
|
10
|
+
return value === "" ? null : value;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
/** Windows 11 still names itself Windows 10 where Node.js reads the name; its builds start at 22000. */
|
|
15
|
+
export function windowsName(version, release) {
|
|
16
|
+
const build = Number(release.split(".")[2] ?? "0");
|
|
17
|
+
return build >= 22000 ? version.replace(/^Windows 10\b/, "Windows 11") : version;
|
|
18
|
+
}
|
|
19
|
+
export class NodeSystemInfoAdapter {
|
|
20
|
+
environment;
|
|
21
|
+
processRunner;
|
|
22
|
+
constructor(environment, processRunner) {
|
|
23
|
+
this.environment = environment;
|
|
24
|
+
this.processRunner = processRunner;
|
|
25
|
+
}
|
|
26
|
+
async read() {
|
|
27
|
+
const { locale, timeZone } = Intl.DateTimeFormat().resolvedOptions();
|
|
28
|
+
return {
|
|
29
|
+
os: this.environment.os,
|
|
30
|
+
osName: await this.osName(),
|
|
31
|
+
kernel: os.release(),
|
|
32
|
+
arch: os.arch(),
|
|
33
|
+
cpuModel: os.cpus()[0]?.model.trim() ?? "",
|
|
34
|
+
cpuCount: os.availableParallelism(),
|
|
35
|
+
memoryTotalBytes: os.totalmem(),
|
|
36
|
+
memoryFreeBytes: os.freemem(),
|
|
37
|
+
uptimeSeconds: Math.floor(os.uptime()),
|
|
38
|
+
hostname: os.hostname(),
|
|
39
|
+
locale,
|
|
40
|
+
timeZone,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async osName() {
|
|
44
|
+
switch (this.environment.os) {
|
|
45
|
+
case "windows":
|
|
46
|
+
return windowsName(os.version(), os.release());
|
|
47
|
+
case "linux":
|
|
48
|
+
for (const file of ["/etc/os-release", "/usr/lib/os-release"]) {
|
|
49
|
+
const name = await readFile(file, "utf8").then(prettyNameFromOsRelease, () => null);
|
|
50
|
+
if (name !== null)
|
|
51
|
+
return name;
|
|
52
|
+
}
|
|
53
|
+
return "Linux";
|
|
54
|
+
case "macos": {
|
|
55
|
+
const version = await this.processRunner
|
|
56
|
+
.run("/usr/bin/sw_vers", ["-productVersion"], { timeoutMs: 10_000 })
|
|
57
|
+
.then((result) => (result.code === 0 ? result.stdout.trim() : ""), () => "");
|
|
58
|
+
return version === "" ? "macOS" : `macOS ${version}`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=node-system-info.adapter.js.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
2
|
+
const WAIT_MS = 3_000;
|
|
3
|
+
const POLL_MS = 100;
|
|
4
|
+
function errorCode(error) {
|
|
5
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string"
|
|
6
|
+
? error.code
|
|
7
|
+
: null;
|
|
8
|
+
}
|
|
9
|
+
/** Signal 0 checks that a process exists without touching it; EPERM means it exists but is not ours. */
|
|
10
|
+
function isRunning(pid) {
|
|
11
|
+
try {
|
|
12
|
+
process.kill(pid, 0);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
return errorCode(error) === "EPERM";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function refusal(code) {
|
|
20
|
+
if (code === "ESRCH")
|
|
21
|
+
return "not-found";
|
|
22
|
+
return code === "EPERM" ? "denied" : "failed";
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* SIGTERM, or SIGKILL with `force`, then polling until each process is gone or a few
|
|
26
|
+
* seconds pass. Node.js ends a Windows process at once for either signal.
|
|
27
|
+
*/
|
|
28
|
+
export async function endProcesses(pids, force) {
|
|
29
|
+
const results = new Map();
|
|
30
|
+
for (const pid of pids) {
|
|
31
|
+
try {
|
|
32
|
+
process.kill(pid, force ? "SIGKILL" : "SIGTERM");
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
const code = errorCode(error);
|
|
36
|
+
results.set(pid, { pid, outcome: refusal(code), code });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const signalled = pids.filter((pid) => !results.has(pid));
|
|
40
|
+
const deadline = Date.now() + WAIT_MS;
|
|
41
|
+
let running = signalled.filter(isRunning);
|
|
42
|
+
while (running.length > 0 && Date.now() < deadline) {
|
|
43
|
+
await sleep(POLL_MS);
|
|
44
|
+
running = running.filter(isRunning);
|
|
45
|
+
}
|
|
46
|
+
for (const pid of signalled) {
|
|
47
|
+
results.set(pid, { pid, outcome: running.includes(pid) ? "still-running" : "ended", code: null });
|
|
48
|
+
}
|
|
49
|
+
return pids.map((pid) => results.get(pid) ?? { pid, outcome: "failed", code: null });
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=process-signals.js.map
|