@augment-vir/node 31.0.0 → 31.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/dist/augments/fs/dir-contents.js +1 -1
- package/dist/augments/terminal/relevant-args.d.ts +1 -7
- package/dist/augments/terminal/relevant-args.js +1 -7
- package/dist/augments/terminal/run-cli-script.js +1 -5
- package/dist/augments/terminal/shell.js +1 -4
- package/dist/docker/containers/docker-command-inputs.js +1 -1
- package/dist/docker/containers/run-container.mock.js +1 -4
- package/dist/docker/docker-image.js +1 -6
- package/dist/prisma/model-data.js +2 -5
- package/dist/prisma/prisma-migrations.js +2 -7
- package/dist/prisma/run-prisma-command.js +3 -15
- package/dist/scripts/fix-ts-bin.script.d.ts +1 -0
- package/dist/scripts/fix-ts-bin.script.js +48 -0
- package/package.json +4 -4
- package/src/augments/docker.ts +118 -0
- package/src/augments/fs/dir-contents.ts +116 -0
- package/src/augments/fs/download.ts +31 -0
- package/src/augments/fs/json.ts +87 -0
- package/src/augments/fs/read-dir.ts +60 -0
- package/src/augments/fs/read-file.ts +18 -0
- package/src/augments/fs/symlink.ts +37 -0
- package/src/augments/fs/write.ts +18 -0
- package/src/augments/npm/query-workspace.ts +47 -0
- package/src/augments/npm/read-package-json.ts +28 -0
- package/src/augments/os/operating-system.ts +55 -0
- package/src/augments/path/ancestor.ts +78 -0
- package/src/augments/path/os-path.ts +45 -0
- package/src/augments/path/root.ts +14 -0
- package/src/augments/prisma.ts +174 -0
- package/src/augments/terminal/question.ts +142 -0
- package/src/augments/terminal/relevant-args.ts +81 -0
- package/src/augments/terminal/run-cli-script.ts +60 -0
- package/src/augments/terminal/shell.ts +312 -0
- package/src/docker/containers/container-info.ts +83 -0
- package/src/docker/containers/container-status.ts +110 -0
- package/src/docker/containers/copy-to-container.ts +34 -0
- package/src/docker/containers/docker-command-inputs.ts +119 -0
- package/src/docker/containers/kill-container.ts +25 -0
- package/src/docker/containers/run-command.ts +51 -0
- package/src/docker/containers/run-container.mock.ts +17 -0
- package/src/docker/containers/run-container.ts +92 -0
- package/src/docker/containers/try-or-kill-container.ts +18 -0
- package/src/docker/docker-image.ts +56 -0
- package/src/docker/docker-startup.ts +49 -0
- package/src/docker/run-docker-test.mock.ts +26 -0
- package/src/file-paths.mock.ts +29 -0
- package/src/index.ts +19 -0
- package/src/prisma/disable-ci-env.mock.ts +88 -0
- package/src/prisma/model-data.ts +213 -0
- package/src/prisma/prisma-client.ts +43 -0
- package/src/prisma/prisma-database.mock.ts +31 -0
- package/src/prisma/prisma-database.ts +35 -0
- package/src/prisma/prisma-errors.ts +45 -0
- package/src/prisma/prisma-migrations.ts +149 -0
- package/src/prisma/run-prisma-command.ts +59 -0
- package/src/scripts/fix-ts-bin.script.ts +60 -0
|
@@ -65,7 +65,7 @@ export async function resetDirContents(rootDir, contents) {
|
|
|
65
65
|
* @package [`@augment-vir/node`](https://www.npmjs.com/package/@augment-vir/node)
|
|
66
66
|
*/
|
|
67
67
|
export async function writeDirContents(rootDir, contents) {
|
|
68
|
-
await Promise.all(getObjectTypedEntries(contents).map(async ([relativePath, content
|
|
68
|
+
await Promise.all(getObjectTypedEntries(contents).map(async ([relativePath, content]) => {
|
|
69
69
|
const fullPath = join(rootDir, relativePath);
|
|
70
70
|
if (check.isString(content)) {
|
|
71
71
|
await writeFileAndDir(fullPath, content);
|
|
@@ -37,13 +37,7 @@ export type RelevantArgsInput = {
|
|
|
37
37
|
*
|
|
38
38
|
* ```ts
|
|
39
39
|
* extractRelevantArgs({
|
|
40
|
-
* rawArgs: [
|
|
41
|
-
* 'npx',
|
|
42
|
-
* 'ts-node',
|
|
43
|
-
* './my-script.ts',
|
|
44
|
-
* 'arg1',
|
|
45
|
-
* '--arg2',
|
|
46
|
-
* ], // typically will be process.argv
|
|
40
|
+
* rawArgs: ['npx', 'ts-node', './my-script.ts', 'arg1', '--arg2'], // typically will be process.argv
|
|
47
41
|
* binName: 'my-script', // should be your package.json "bin" property name, can be undefined
|
|
48
42
|
* fileName: 'my-script.ts', // should be __filename from the script that will be executed
|
|
49
43
|
* });
|
|
@@ -9,13 +9,7 @@ import { basename } from 'node:path';
|
|
|
9
9
|
*
|
|
10
10
|
* ```ts
|
|
11
11
|
* extractRelevantArgs({
|
|
12
|
-
* rawArgs: [
|
|
13
|
-
* 'npx',
|
|
14
|
-
* 'ts-node',
|
|
15
|
-
* './my-script.ts',
|
|
16
|
-
* 'arg1',
|
|
17
|
-
* '--arg2',
|
|
18
|
-
* ], // typically will be process.argv
|
|
12
|
+
* rawArgs: ['npx', 'ts-node', './my-script.ts', 'arg1', '--arg2'], // typically will be process.argv
|
|
19
13
|
* binName: 'my-script', // should be your package.json "bin" property name, can be undefined
|
|
20
14
|
* fileName: 'my-script.ts', // should be __filename from the script that will be executed
|
|
21
15
|
* });
|
|
@@ -41,11 +41,7 @@ binName) {
|
|
|
41
41
|
if (!runner) {
|
|
42
42
|
throw new Error("No runner configured for file extension '${extension}' in '${path}'");
|
|
43
43
|
}
|
|
44
|
-
const results = await runShellCommand(interpolationSafeWindowsPath([
|
|
45
|
-
runner,
|
|
46
|
-
path,
|
|
47
|
-
...args,
|
|
48
|
-
].join(' ')), {
|
|
44
|
+
const results = await runShellCommand(interpolationSafeWindowsPath([runner, path, ...args].join(' ')), {
|
|
49
45
|
hookUpToConsole: true,
|
|
50
46
|
});
|
|
51
47
|
process.exit(results.exitCode || 0);
|
|
@@ -169,10 +169,7 @@ export async function runShellCommand(command, options = {}) {
|
|
|
169
169
|
}
|
|
170
170
|
/* node:coverage enable */
|
|
171
171
|
shellTarget.destroy();
|
|
172
|
-
const rejectionErrorMessage = combineErrors([
|
|
173
|
-
new Error(stderr),
|
|
174
|
-
...errors,
|
|
175
|
-
]);
|
|
172
|
+
const rejectionErrorMessage = combineErrors([new Error(stderr), ...errors]);
|
|
176
173
|
/** Reject now because the "done" listener won't get fired after killing the process. */
|
|
177
174
|
reject(rejectionErrorMessage);
|
|
178
175
|
});
|
|
@@ -37,7 +37,7 @@ export function makeEnvFlags(envMapping) {
|
|
|
37
37
|
if (!envMapping) {
|
|
38
38
|
return '';
|
|
39
39
|
}
|
|
40
|
-
const flags = Object.entries(envMapping).map(([key, { value, allowInterpolation }
|
|
40
|
+
const flags = Object.entries(envMapping).map(([key, { value, allowInterpolation }]) => {
|
|
41
41
|
const quote = allowInterpolation ? '"' : "'";
|
|
42
42
|
return `-e ${key}=${wrapString({ value, wrapper: quote })}`;
|
|
43
43
|
});
|
|
@@ -4,10 +4,7 @@ export async function runMockLongLivingContainer(containerName, args = {}) {
|
|
|
4
4
|
containerName: containerName,
|
|
5
5
|
detach: true,
|
|
6
6
|
imageName: 'alpine:3.20.2',
|
|
7
|
-
dockerFlags: [
|
|
8
|
-
'-i',
|
|
9
|
-
'-t',
|
|
10
|
-
],
|
|
7
|
+
dockerFlags: ['-i', '-t'],
|
|
11
8
|
command: 'sh',
|
|
12
9
|
platform: 'amd64',
|
|
13
10
|
...args,
|
|
@@ -11,12 +11,7 @@ imageName, platform) {
|
|
|
11
11
|
const command = [
|
|
12
12
|
'docker',
|
|
13
13
|
'pull',
|
|
14
|
-
...(platform
|
|
15
|
-
? [
|
|
16
|
-
'--platform',
|
|
17
|
-
platform,
|
|
18
|
-
]
|
|
19
|
-
: []),
|
|
14
|
+
...(platform ? ['--platform', platform] : []),
|
|
20
15
|
wrapString({ value: imageName, wrapper: "'" }),
|
|
21
16
|
].join(' ');
|
|
22
17
|
await runShellCommand(command, {
|
|
@@ -8,7 +8,7 @@ export async function addData(prismaClient, data) {
|
|
|
8
8
|
}
|
|
9
9
|
async function addModelDataObject(prismaClient, data) {
|
|
10
10
|
/** Add the mock data to the mock prisma client. */
|
|
11
|
-
await awaitedForEach(getObjectTypedEntries(data), async ([modelName, mockData
|
|
11
|
+
await awaitedForEach(getObjectTypedEntries(data), async ([modelName, mockData]) => {
|
|
12
12
|
/**
|
|
13
13
|
* This type is dumbed down to just `AnyObject[]` because the union of all possible
|
|
14
14
|
* model data is just way too big (and not helpful as the inputs to this function are
|
|
@@ -37,10 +37,7 @@ async function addModelDataObject(prismaClient, data) {
|
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
|
-
const prismockKeys = [
|
|
41
|
-
'getData',
|
|
42
|
-
'setData',
|
|
43
|
-
];
|
|
40
|
+
const prismockKeys = ['getData', 'setData'];
|
|
44
41
|
export function getAllPrismaModelNames(prismaClient) {
|
|
45
42
|
return Object.keys(prismaClient)
|
|
46
43
|
.filter((key) => !key.startsWith('$') && !key.startsWith('_') && !prismockKeys.includes(key))
|
|
@@ -13,12 +13,7 @@ var DbChangeRequired;
|
|
|
13
13
|
DbChangeRequired["ResetNeeded"] = "reset-needed";
|
|
14
14
|
})(DbChangeRequired || (DbChangeRequired = {}));
|
|
15
15
|
export async function applyPrismaMigrationsToDev(schemaFilePath, env = {}) {
|
|
16
|
-
const command = [
|
|
17
|
-
'prisma',
|
|
18
|
-
'migrate',
|
|
19
|
-
'dev',
|
|
20
|
-
`--schema='${schemaFilePath}'`,
|
|
21
|
-
].join(' ');
|
|
16
|
+
const command = ['prisma', 'migrate', 'dev', `--schema='${schemaFilePath}'`].join(' ');
|
|
22
17
|
log.faint(`> ${command}`);
|
|
23
18
|
let dbRequirement = undefined;
|
|
24
19
|
const result = await runShellCommand(command, {
|
|
@@ -76,7 +71,7 @@ export async function getMigrationStatus(schemaFilePath, env = {}) {
|
|
|
76
71
|
foundNotAppliedMigrations = true;
|
|
77
72
|
}
|
|
78
73
|
else {
|
|
79
|
-
const [, countMatch
|
|
74
|
+
const [, countMatch] = safeMatch(line, /^([\d,]+) migrations? found in/);
|
|
80
75
|
if (countMatch) {
|
|
81
76
|
listedMigrations.totalMigrations = toEnsuredNumber(countMatch);
|
|
82
77
|
}
|
|
@@ -2,28 +2,16 @@ import { log } from '@augment-vir/common';
|
|
|
2
2
|
import { interpolationSafeWindowsPath } from '../augments/path/os-path.js';
|
|
3
3
|
import { runShellCommand } from '../augments/terminal/shell.js';
|
|
4
4
|
import { PrismaSchemaError } from './prisma-errors.js';
|
|
5
|
-
const prismaCommandsThatSupportNoHints = [
|
|
6
|
-
'generate',
|
|
7
|
-
];
|
|
5
|
+
const prismaCommandsThatSupportNoHints = ['generate'];
|
|
8
6
|
export async function runPrismaCommand({ command, ignoreExitCode = false, },
|
|
9
7
|
/** Set to `undefined` to omit the `--schema` flag. */
|
|
10
8
|
schemaFilePath, env = {}) {
|
|
11
|
-
const schemaFileArgs = schemaFilePath
|
|
12
|
-
? [
|
|
13
|
-
'--schema',
|
|
14
|
-
schemaFilePath,
|
|
15
|
-
]
|
|
16
|
-
: [];
|
|
9
|
+
const schemaFileArgs = schemaFilePath ? ['--schema', schemaFilePath] : [];
|
|
17
10
|
/** Disable Prisma's in-CLI ads. */
|
|
18
11
|
const noHintsArg = prismaCommandsThatSupportNoHints.some((commandName) => command.startsWith(commandName))
|
|
19
12
|
? '--no-hints'
|
|
20
13
|
: '';
|
|
21
|
-
const fullCommand = [
|
|
22
|
-
'prisma',
|
|
23
|
-
command,
|
|
24
|
-
...schemaFileArgs,
|
|
25
|
-
noHintsArg,
|
|
26
|
-
].join(' ');
|
|
14
|
+
const fullCommand = ['prisma', command, ...schemaFileArgs, noHintsArg].join(' ');
|
|
27
15
|
log.faint(`> ${fullCommand}`);
|
|
28
16
|
const result = await runShellCommand(interpolationSafeWindowsPath(fullCommand), {
|
|
29
17
|
env: {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/* node:coverage disable */
|
|
2
|
+
/**
|
|
3
|
+
* To use this script properly, call it directly in your postinstall script. Like this:
|
|
4
|
+
*
|
|
5
|
+
* ```json
|
|
6
|
+
* {
|
|
7
|
+
* "scripts": {
|
|
8
|
+
* "postinstall": "tsx node_modules/@augment-vir/node/src/scripts/fix-ts-bin.script.ts"
|
|
9
|
+
* }
|
|
10
|
+
* }
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
import { log } from '@augment-vir/common';
|
|
14
|
+
import { interpolationSafeWindowsPath, runShellCommand } from '@augment-vir/node';
|
|
15
|
+
import { rm, writeFile } from 'node:fs/promises';
|
|
16
|
+
import { join, sep } from 'node:path/posix';
|
|
17
|
+
const packagesToFix = [
|
|
18
|
+
{
|
|
19
|
+
packageName: 'mono-vir',
|
|
20
|
+
binName: 'mono-vir',
|
|
21
|
+
scriptPath: join('mono-vir', 'src', 'cli', 'cli.script.ts'),
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
packageName: 'virmator',
|
|
25
|
+
binName: 'virmator',
|
|
26
|
+
scriptPath: join('virmator', 'src', 'cli.script.ts'),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
packageName: 'prettier',
|
|
30
|
+
binName: 'prettier',
|
|
31
|
+
scriptPath: join('prettier', 'bin', 'prettier.cjs'),
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
function createBinFileContents({ scriptPath }) {
|
|
35
|
+
return [
|
|
36
|
+
'#!/bin/bash',
|
|
37
|
+
'',
|
|
38
|
+
`npx tsx "$(dirname "$(dirname "$(readlink -f "$0")")")${sep}${scriptPath}" "$@"`,
|
|
39
|
+
].join('\n');
|
|
40
|
+
}
|
|
41
|
+
async function fixTsBin(packageToFix) {
|
|
42
|
+
const binFilePath = join(process.cwd(), 'node_modules', '.bin', packageToFix.binName);
|
|
43
|
+
await rm(binFilePath, { force: true });
|
|
44
|
+
await writeFile(binFilePath, createBinFileContents(packageToFix));
|
|
45
|
+
await runShellCommand(`chmod +x ${interpolationSafeWindowsPath(binFilePath)}`);
|
|
46
|
+
log.success(`Fixed ${packageToFix.packageName} bin.`);
|
|
47
|
+
}
|
|
48
|
+
await Promise.all(packagesToFix.map(async (packageToFix) => await fixTsBin(packageToFix)));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@augment-vir/node",
|
|
3
|
-
"version": "31.
|
|
3
|
+
"version": "31.1.0",
|
|
4
4
|
"description": "A collection of augments, helpers types, functions, and classes only for Node.js (backend) JavaScript environments.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"augment",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"test:update": "npm test"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@augment-vir/assert": "^31.
|
|
41
|
-
"@augment-vir/common": "^31.
|
|
40
|
+
"@augment-vir/assert": "^31.1.0",
|
|
41
|
+
"@augment-vir/common": "^31.1.0",
|
|
42
42
|
"@date-vir/duration": "^7.0.1",
|
|
43
43
|
"ansi-styles": "^6.2.1",
|
|
44
44
|
"terminate": "^2.8.0",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"typed-event-target": "^4.0.2"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@augment-vir/test": "^31.
|
|
49
|
+
"@augment-vir/test": "^31.1.0",
|
|
50
50
|
"@prisma/client": "^5.22.0",
|
|
51
51
|
"@types/node": "^22.10.0",
|
|
52
52
|
"@web/dev-server-esbuild": "^1.0.3",
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {getContainerInfo} from '../docker/containers/container-info.js';
|
|
2
|
+
import {
|
|
3
|
+
getContainerLogs,
|
|
4
|
+
getContainerStatus,
|
|
5
|
+
waitUntilContainerExited,
|
|
6
|
+
waitUntilContainerRemoved,
|
|
7
|
+
waitUntilContainerRunning,
|
|
8
|
+
} from '../docker/containers/container-status.js';
|
|
9
|
+
import {copyToContainer} from '../docker/containers/copy-to-container.js';
|
|
10
|
+
import {
|
|
11
|
+
makeEnvFlags,
|
|
12
|
+
makePortMapFlags,
|
|
13
|
+
makeVolumeFlags,
|
|
14
|
+
} from '../docker/containers/docker-command-inputs.js';
|
|
15
|
+
import {killContainer} from '../docker/containers/kill-container.js';
|
|
16
|
+
import {runContainerCommand} from '../docker/containers/run-command.js';
|
|
17
|
+
import {runContainer} from '../docker/containers/run-container.js';
|
|
18
|
+
import {tryOrKillContainer} from '../docker/containers/try-or-kill-container.js';
|
|
19
|
+
import {
|
|
20
|
+
isImageInLocalRegistry,
|
|
21
|
+
removeImageFromLocalRegistry,
|
|
22
|
+
updateImage,
|
|
23
|
+
} from '../docker/docker-image.js';
|
|
24
|
+
import {isDockerRunning, startDocker} from '../docker/docker-startup.js';
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
type DockerContainerInfo,
|
|
28
|
+
type DockerContainerInfoState,
|
|
29
|
+
} from '../docker/containers/container-info.js';
|
|
30
|
+
export {
|
|
31
|
+
DockerContainerStatus,
|
|
32
|
+
exitedDockerContainerStatuses,
|
|
33
|
+
} from '../docker/containers/container-status.js';
|
|
34
|
+
export {type CopyToDockerContainerParams} from '../docker/containers/copy-to-container.js';
|
|
35
|
+
export {
|
|
36
|
+
type DockerEnvMap,
|
|
37
|
+
type DockerPortMap,
|
|
38
|
+
type DockerVolumeMap,
|
|
39
|
+
type DockerVolumeMappingType,
|
|
40
|
+
} from '../docker/containers/docker-command-inputs.js';
|
|
41
|
+
export {type RunDockerContainerCommandParams} from '../docker/containers/run-command.js';
|
|
42
|
+
export {type RunDockerContainerParams} from '../docker/containers/run-container.js';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Centralized Docker API from `@augment-vir/node`.
|
|
46
|
+
*
|
|
47
|
+
* @category Node : Docker
|
|
48
|
+
* @category Package : @augment-vir/node
|
|
49
|
+
* @package [`@augment-vir/node`](https://www.npmjs.com/package/@augment-vir/node)
|
|
50
|
+
*/
|
|
51
|
+
export const docker = {
|
|
52
|
+
/** Detects if the Docker service is running. */
|
|
53
|
+
isRunning: isDockerRunning,
|
|
54
|
+
/**
|
|
55
|
+
* Tries to start Docker based ont he current operating system's supported commands. The success
|
|
56
|
+
* of this operation is heavily dependent on how you have Docker setup on your system.
|
|
57
|
+
*/
|
|
58
|
+
start: startDocker,
|
|
59
|
+
image: {
|
|
60
|
+
/** Downloads an image if it is missing from the local registry. */
|
|
61
|
+
update: updateImage,
|
|
62
|
+
/** Removes an image from the local registry. */
|
|
63
|
+
remove: removeImageFromLocalRegistry,
|
|
64
|
+
/** Detects if an image exists in the local registry. */
|
|
65
|
+
exists: isImageInLocalRegistry,
|
|
66
|
+
},
|
|
67
|
+
container: {
|
|
68
|
+
/**
|
|
69
|
+
* Get the current status of a container. If the container does not exist at all, the status
|
|
70
|
+
* will be {@link DockerContainerStatus.Removed}.
|
|
71
|
+
*/
|
|
72
|
+
getStatus: getContainerStatus,
|
|
73
|
+
/** Wait until a container is running and responsive. */
|
|
74
|
+
waitUntilRunning: waitUntilContainerRunning,
|
|
75
|
+
/**
|
|
76
|
+
* Wait until a container has a status that can be classified as "exited".
|
|
77
|
+
*
|
|
78
|
+
* @see {@link exitedDockerContainerStatuses}
|
|
79
|
+
*/
|
|
80
|
+
waitUntilExited: waitUntilContainerExited,
|
|
81
|
+
/** Wait until a container is completely removed. */
|
|
82
|
+
waitUntilRemoved: waitUntilContainerRemoved,
|
|
83
|
+
/**
|
|
84
|
+
* Runs a callback (which presumably will run a command within the given `containerName`)
|
|
85
|
+
* and kills the container if the callback fails.
|
|
86
|
+
*/
|
|
87
|
+
tryOrKill: tryOrKillContainer,
|
|
88
|
+
/** Run a container that isn't already running. */
|
|
89
|
+
run: runContainer,
|
|
90
|
+
/** Kill a container. */
|
|
91
|
+
kill: killContainer,
|
|
92
|
+
/** Copy a file or directory to a container. */
|
|
93
|
+
copyTo: copyToContainer,
|
|
94
|
+
/** Run a command on a container that is already running. */
|
|
95
|
+
runCommand: runContainerCommand,
|
|
96
|
+
/** Run `docker inspect` on a container and return its output. */
|
|
97
|
+
getInfo: getContainerInfo,
|
|
98
|
+
/** Get a container's logs. */
|
|
99
|
+
getLogs: getContainerLogs,
|
|
100
|
+
},
|
|
101
|
+
util: {
|
|
102
|
+
/**
|
|
103
|
+
* Manually create a string of volume mapping flags. This is automatically done already
|
|
104
|
+
* inside the run container methods.
|
|
105
|
+
*/
|
|
106
|
+
makeVolumeFlags,
|
|
107
|
+
/**
|
|
108
|
+
* Manually create a string of port mapping flags. This is automatically done already inside
|
|
109
|
+
* the run container methods.
|
|
110
|
+
*/
|
|
111
|
+
makePortMapFlags,
|
|
112
|
+
/**
|
|
113
|
+
* Manually create a string of env mapping flags. This is automatically done already inside
|
|
114
|
+
* the run container methods.
|
|
115
|
+
*/
|
|
116
|
+
makeEnvFlags,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import {check} from '@augment-vir/assert';
|
|
2
|
+
import {getObjectTypedEntries} from '@augment-vir/common';
|
|
3
|
+
import {readdir, readFile, rm, stat} from 'node:fs/promises';
|
|
4
|
+
import {join} from 'node:path';
|
|
5
|
+
import {writeFileAndDir} from './write.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Nested contents read from a file system directory.
|
|
9
|
+
*
|
|
10
|
+
* @category Node : File
|
|
11
|
+
* @category Package : @augment-vir/node
|
|
12
|
+
* @package [`@augment-vir/node`](https://www.npmjs.com/package/@augment-vir/node)
|
|
13
|
+
*/
|
|
14
|
+
export type DirContents = {
|
|
15
|
+
[Path in string]: string | DirContents;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Read all contents within a directory and store them in an object. Optionally recursive.
|
|
20
|
+
*
|
|
21
|
+
* @category Node : File
|
|
22
|
+
* @category Package : @augment-vir/node
|
|
23
|
+
* @package [`@augment-vir/node`](https://www.npmjs.com/package/@augment-vir/node)
|
|
24
|
+
*/
|
|
25
|
+
export async function readAllDirContents(
|
|
26
|
+
dir: string,
|
|
27
|
+
{
|
|
28
|
+
recursive = false,
|
|
29
|
+
excludeList,
|
|
30
|
+
}: {
|
|
31
|
+
recursive?: boolean;
|
|
32
|
+
excludeList?: ReadonlyArray<string | RegExp> | undefined;
|
|
33
|
+
},
|
|
34
|
+
): Promise<DirContents> {
|
|
35
|
+
const fileNames = (await readdir(dir)).sort();
|
|
36
|
+
|
|
37
|
+
const allFileContents = await Promise.all(
|
|
38
|
+
fileNames.map(async (fileName) => {
|
|
39
|
+
const filePath = join(dir, fileName);
|
|
40
|
+
|
|
41
|
+
if (
|
|
42
|
+
excludeList?.some((excludeItem) => {
|
|
43
|
+
if (check.isString(excludeItem)) {
|
|
44
|
+
return filePath.includes(excludeItem);
|
|
45
|
+
} else {
|
|
46
|
+
return filePath.match(excludeItem);
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const isFile = (await stat(filePath)).isFile();
|
|
54
|
+
const contents = isFile
|
|
55
|
+
? (await readFile(filePath)).toString()
|
|
56
|
+
: recursive
|
|
57
|
+
? await readAllDirContents(filePath, {recursive, excludeList})
|
|
58
|
+
: undefined;
|
|
59
|
+
|
|
60
|
+
if (check.isObject(contents) && !Object.keys(contents).length) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return contents;
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
return fileNames.reduce((accum: DirContents, fileName, index) => {
|
|
69
|
+
if (allFileContents[index] != undefined) {
|
|
70
|
+
const fileContents = allFileContents[index];
|
|
71
|
+
accum[fileName] = fileContents;
|
|
72
|
+
}
|
|
73
|
+
return accum;
|
|
74
|
+
}, {});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Deletes and entire directory and resets it to the given contents.
|
|
79
|
+
*
|
|
80
|
+
* @category Node : File
|
|
81
|
+
* @category Package : @augment-vir/node
|
|
82
|
+
* @package [`@augment-vir/node`](https://www.npmjs.com/package/@augment-vir/node)
|
|
83
|
+
*/
|
|
84
|
+
export async function resetDirContents(
|
|
85
|
+
rootDir: string,
|
|
86
|
+
contents: Readonly<DirContents>,
|
|
87
|
+
): Promise<void> {
|
|
88
|
+
await rm(rootDir, {
|
|
89
|
+
force: true,
|
|
90
|
+
recursive: true,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
await writeDirContents(rootDir, contents);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Write {@link DirContents} to a directory.
|
|
97
|
+
*
|
|
98
|
+
* @category Node : File
|
|
99
|
+
* @category Package : @augment-vir/node
|
|
100
|
+
* @package [`@augment-vir/node`](https://www.npmjs.com/package/@augment-vir/node)
|
|
101
|
+
*/
|
|
102
|
+
export async function writeDirContents(
|
|
103
|
+
rootDir: string,
|
|
104
|
+
contents: Readonly<DirContents>,
|
|
105
|
+
): Promise<void> {
|
|
106
|
+
await Promise.all(
|
|
107
|
+
getObjectTypedEntries(contents).map(async ([relativePath, content]) => {
|
|
108
|
+
const fullPath = join(rootDir, relativePath);
|
|
109
|
+
if (check.isString(content)) {
|
|
110
|
+
await writeFileAndDir(fullPath, content);
|
|
111
|
+
} else {
|
|
112
|
+
await writeDirContents(fullPath, content);
|
|
113
|
+
}
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
116
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {createWriteStream} from 'node:fs';
|
|
2
|
+
import {mkdir} from 'node:fs/promises';
|
|
3
|
+
import {dirname} from 'node:path';
|
|
4
|
+
import {Readable} from 'node:stream';
|
|
5
|
+
import {finished} from 'node:stream/promises';
|
|
6
|
+
import type {ReadableStream} from 'node:stream/web';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Download a file.
|
|
10
|
+
*
|
|
11
|
+
* @category Node : File
|
|
12
|
+
* @category Package : @augment-vir/node
|
|
13
|
+
* @package [`@augment-vir/node`](https://www.npmjs.com/package/@augment-vir/node)
|
|
14
|
+
*/
|
|
15
|
+
export async function downloadFile({url, writePath}: {url: string; writePath: string}) {
|
|
16
|
+
const response = await fetch(url);
|
|
17
|
+
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
throw new Error(`${response.status}: ${response.statusText}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Idk how to actually trigger a response with no body. */
|
|
23
|
+
/* node:coverage ignore next 3 */
|
|
24
|
+
if (!response.body) {
|
|
25
|
+
throw new Error(`Response body is missing from '${url}'.`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
await mkdir(dirname(writePath), {recursive: true});
|
|
29
|
+
const fileStream = createWriteStream(writePath);
|
|
30
|
+
await finished(Readable.fromWeb(response.body as ReadableStream).pipe(fileStream));
|
|
31
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
appendJson,
|
|
3
|
+
JsonCompatibleArray,
|
|
4
|
+
JsonCompatibleObject,
|
|
5
|
+
type JsonCompatibleValue,
|
|
6
|
+
} from '@augment-vir/common';
|
|
7
|
+
import type {PartialWithUndefined} from '@augment-vir/core';
|
|
8
|
+
import {mkdir, readFile} from 'node:fs/promises';
|
|
9
|
+
import {dirname} from 'node:path';
|
|
10
|
+
import {writeFileAndDir} from './write.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Read a file and also parse its contents as JSON.
|
|
14
|
+
*
|
|
15
|
+
* @category Node : File
|
|
16
|
+
* @category JSON : Node
|
|
17
|
+
* @category Package : @augment-vir/node
|
|
18
|
+
* @package [`@augment-vir/node`](https://www.npmjs.com/package/@augment-vir/node)
|
|
19
|
+
* @see
|
|
20
|
+
* - {@link writeJsonFile}
|
|
21
|
+
* - {@link appendJsonFile}
|
|
22
|
+
*/
|
|
23
|
+
export async function readJsonFile(path: string): Promise<JsonCompatibleValue | undefined> {
|
|
24
|
+
try {
|
|
25
|
+
const contents = (await readFile(path)).toString();
|
|
26
|
+
return JSON.parse(contents);
|
|
27
|
+
} catch {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Options for {@link writeJsonFile}.
|
|
34
|
+
*
|
|
35
|
+
* @category Node : File
|
|
36
|
+
* @category JSON : Node
|
|
37
|
+
* @category Package : @augment-vir/node
|
|
38
|
+
* @package [`@augment-vir/node`](https://www.npmjs.com/package/@augment-vir/node)
|
|
39
|
+
*/
|
|
40
|
+
export type WriteJsonOptions = PartialWithUndefined<{
|
|
41
|
+
includeTrailingNewLine: boolean;
|
|
42
|
+
}>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Write to a file and stringify `data` as JSON before doing so.
|
|
46
|
+
*
|
|
47
|
+
* @category Node : File
|
|
48
|
+
* @category JSON : Node
|
|
49
|
+
* @category Package : @augment-vir/node
|
|
50
|
+
* @package [`@augment-vir/node`](https://www.npmjs.com/package/@augment-vir/node)
|
|
51
|
+
* @see
|
|
52
|
+
* - {@link readJsonFile}
|
|
53
|
+
* - {@link appendJsonFile}
|
|
54
|
+
*/
|
|
55
|
+
export async function writeJsonFile(
|
|
56
|
+
path: string,
|
|
57
|
+
data: JsonCompatibleValue,
|
|
58
|
+
options: WriteJsonOptions = {},
|
|
59
|
+
): Promise<void> {
|
|
60
|
+
await mkdir(dirname(path), {recursive: true});
|
|
61
|
+
|
|
62
|
+
const trailingNewLine = options.includeTrailingNewLine ? '\n' : '';
|
|
63
|
+
|
|
64
|
+
await writeFileAndDir(path, JSON.stringify(data, null, 4) + trailingNewLine);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Append the given `newData` to the contents of the existing JSON file. If the file does not yet
|
|
69
|
+
* exist, `newData` is written as its only JSON contents.
|
|
70
|
+
*
|
|
71
|
+
* @category Node : File
|
|
72
|
+
* @category JSON : Node
|
|
73
|
+
* @category Package : @augment-vir/node
|
|
74
|
+
* @package [`@augment-vir/node`](https://www.npmjs.com/package/@augment-vir/node)
|
|
75
|
+
* @see
|
|
76
|
+
* - {@link readJsonFile}
|
|
77
|
+
* - {@link writeJsonFile}
|
|
78
|
+
*/
|
|
79
|
+
export async function appendJsonFile(
|
|
80
|
+
path: string,
|
|
81
|
+
newData: JsonCompatibleObject | JsonCompatibleArray,
|
|
82
|
+
options: WriteJsonOptions = {},
|
|
83
|
+
): Promise<void> {
|
|
84
|
+
const fileJson = await readJsonFile(path);
|
|
85
|
+
|
|
86
|
+
await writeJsonFile(path, appendJson(fileJson, newData), options);
|
|
87
|
+
}
|