@procedures/restic 0.3.0 → 0.4.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.
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The lookup of the restic program. WinGet installs restic as a portable program in its own
3
+ * folder, and that folder is not always on the `PATH`.
4
+ */
5
+ /** The settings of `findRestic()`. A test gives other values. */
6
+ export interface FindResticOptions {
7
+ /** The environment to read (default: `process.env`) */
8
+ readonly env?: Readonly<Record<string, string | undefined>> | undefined;
9
+ /** The platform (default: `process.platform`) */
10
+ readonly platform?: NodeJS.Platform | undefined;
11
+ /** This function tells if a path exists (default: `existsSync`). */
12
+ readonly exists?: ((path: string) => boolean) | undefined;
13
+ /** This function gives the names in a folder (default: `readdir`). */
14
+ readonly list?: ((folder: string) => Promise<string[]>) | undefined;
15
+ }
16
+ /**
17
+ * This function finds the restic program. It gives the first of these:
18
+ *
19
+ * 1. `RESTIC_EXE` of the environment, when it is set.
20
+ * 2. On Windows, the `restic*.exe` of a WinGet package `restic.restic*` in
21
+ * `%LOCALAPPDATA%\Microsoft\WinGet\Packages`.
22
+ * 3. `restic`: the `PATH` gives the program.
23
+ */
24
+ export declare function findRestic(options?: FindResticOptions): Promise<string>;
25
+ //# sourceMappingURL=executable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executable.d.ts","sourceRoot":"","sources":["../src/executable.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,SAAS,CAAC;IACxE,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,GAAG,SAAS,CAAC;IAChD,oEAAoE;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,SAAS,CAAC;IAC1D,sEAAsE;IACtE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC;CACrE;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBjF"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The lookup of the restic program. WinGet installs restic as a portable program in its own
3
+ * folder, and that folder is not always on the `PATH`.
4
+ */
5
+ import { existsSync } from "node:fs";
6
+ import { readdir } from "node:fs/promises";
7
+ import { win32 } from "node:path";
8
+ /**
9
+ * This function finds the restic program. It gives the first of these:
10
+ *
11
+ * 1. `RESTIC_EXE` of the environment, when it is set.
12
+ * 2. On Windows, the `restic*.exe` of a WinGet package `restic.restic*` in
13
+ * `%LOCALAPPDATA%\Microsoft\WinGet\Packages`.
14
+ * 3. `restic`: the `PATH` gives the program.
15
+ */
16
+ export async function findRestic(options = {}) {
17
+ const env = options.env ?? process.env;
18
+ const exists = options.exists ?? existsSync;
19
+ const list = options.list ?? ((folder) => readdir(folder));
20
+ const override = env["RESTIC_EXE"];
21
+ if (override !== undefined && override.length > 0)
22
+ return override;
23
+ const localAppData = env["LOCALAPPDATA"];
24
+ if ((options.platform ?? process.platform) === "win32" && localAppData !== undefined && localAppData.length > 0) {
25
+ const packages = win32.join(localAppData, "Microsoft", "WinGet", "Packages");
26
+ if (exists(packages)) {
27
+ try {
28
+ for (const folder of (await list(packages)).sort()) {
29
+ if (!folder.toLowerCase().startsWith("restic.restic"))
30
+ continue;
31
+ const names = (await list(win32.join(packages, folder))).sort();
32
+ const program = names.find((name) => name.toLowerCase().startsWith("restic") && name.toLowerCase().endsWith(".exe"));
33
+ if (program !== undefined)
34
+ return win32.join(packages, folder, program);
35
+ }
36
+ }
37
+ catch {
38
+ // A folder that cannot be read gives no program: the PATH is the last choice
39
+ }
40
+ }
41
+ }
42
+ return "restic";
43
+ }
44
+ //# sourceMappingURL=executable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executable.js","sourceRoot":"","sources":["../src/executable.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAclC;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAO,GAAsB,EAAE;IAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,UAAU,CAAC;IAC5C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,MAAc,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC;IACnC,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAEnE,MAAM,YAAY,GAAG,GAAG,CAAC,cAAc,CAAC,CAAC;IACzC,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,OAAO,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChH,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;QAC7E,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrB,IAAI,CAAC;gBACH,KAAK,MAAM,MAAM,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;oBACnD,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC;wBAAE,SAAS;oBAChE,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;oBAChE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;oBACrH,IAAI,OAAO,KAAK,SAAS;wBAAE,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;gBAC1E,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,6EAA6E;YAC/E,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./module.js";
2
2
  export * from "./procedures.js";
3
3
  export * from "./argv.js";
4
+ export * from "./executable.js";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -2,4 +2,5 @@
2
2
  export * from "./module.js";
3
3
  export * from "./procedures.js";
4
4
  export * from "./argv.js";
5
+ export * from "./executable.js";
5
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC"}
package/dist/module.d.ts CHANGED
@@ -2,11 +2,22 @@
2
2
  * The procedures of @procedures/restic as data. Importing this file registers nothing.
3
3
  */
4
4
  import { type ProcedureModule, type ProcedureRegistry } from "@procedures/client";
5
+ import { type ResticProceduresOptions } from "./procedures.js";
5
6
  export declare const resticModule: ProcedureModule;
6
7
  export default resticModule;
8
+ /**
9
+ * This function gives the module with the settings of the host, for example the secrets of the
10
+ * repository in `env`. Without options, it gives `resticModule`.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const registry = createRegistry(createResticModule({ env: { RESTIC_REPOSITORY: repository, RESTIC_PASSWORD: password } }));
15
+ * ```
16
+ */
17
+ export declare function createResticModule(options?: ResticProceduresOptions): ProcedureModule;
7
18
  /**
8
19
  * This function registers the procedures of @procedures/restic in a registry. The default is the
9
- * global registry.
20
+ * global registry. The options are the options of `createResticModule()`.
10
21
  */
11
- export declare function registerResticProcedures(registry?: ProcedureRegistry): void;
22
+ export declare function registerResticProcedures(registry?: ProcedureRegistry, options?: ResticProceduresOptions): void;
12
23
  //# sourceMappingURL=module.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAqD,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGrI,eAAO,MAAM,YAAY,EAAE,eAAsE,CAAC;eAEnF,YAAY;AAE3B;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,GAAE,iBAAsC,GAAG,IAAI,CAE/F"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAqD,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACrI,OAAO,EAA4C,KAAK,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAEzG,eAAO,MAAM,YAAY,EAAE,eAAsE,CAAC;eAEnF,YAAY;AAE3B;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,uBAA4B,GAAG,eAAe,CAGzF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,GAAE,iBAAsC,EAAE,OAAO,GAAE,uBAA4B,GAAG,IAAI,CAEtI"}
package/dist/module.js CHANGED
@@ -2,14 +2,28 @@
2
2
  * The procedures of @procedures/restic as data. Importing this file registers nothing.
3
3
  */
4
4
  import { defineModule, registerModules, PROCEDURE_REGISTRY } from "@procedures/client";
5
- import { resticProcedures } from "./procedures.js";
5
+ import { createResticProcedures, resticProcedures } from "./procedures.js";
6
6
  export const resticModule = defineModule("@procedures/restic", resticProcedures);
7
7
  export default resticModule;
8
+ /**
9
+ * This function gives the module with the settings of the host, for example the secrets of the
10
+ * repository in `env`. Without options, it gives `resticModule`.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const registry = createRegistry(createResticModule({ env: { RESTIC_REPOSITORY: repository, RESTIC_PASSWORD: password } }));
15
+ * ```
16
+ */
17
+ export function createResticModule(options = {}) {
18
+ if (Object.values(options).every((value) => value === undefined))
19
+ return resticModule;
20
+ return defineModule("@procedures/restic", createResticProcedures(options));
21
+ }
8
22
  /**
9
23
  * This function registers the procedures of @procedures/restic in a registry. The default is the
10
- * global registry.
24
+ * global registry. The options are the options of `createResticModule()`.
11
25
  */
12
- export function registerResticProcedures(registry = PROCEDURE_REGISTRY) {
13
- registerModules(registry, [resticModule]);
26
+ export function registerResticProcedures(registry = PROCEDURE_REGISTRY, options = {}) {
27
+ registerModules(registry, [createResticModule(options)]);
14
28
  }
15
29
  //# sourceMappingURL=module.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAgD,MAAM,oBAAoB,CAAC;AACrI,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,MAAM,CAAC,MAAM,YAAY,GAAoB,YAAY,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC;AAElG,eAAe,YAAY,CAAC;AAE5B;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,QAAQ,GAAsB,kBAAkB;IACvF,eAAe,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;AAC5C,CAAC"}
1
+ {"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAgD,MAAM,oBAAoB,CAAC;AACrI,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAgC,MAAM,iBAAiB,CAAC;AAEzG,MAAM,CAAC,MAAM,YAAY,GAAoB,YAAY,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC;AAElG,eAAe,YAAY,CAAC;AAE5B;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAO,GAA4B,EAAE;IACtE,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC;QAAE,OAAO,YAAY,CAAC;IACtF,OAAO,YAAY,CAAC,oBAAoB,EAAE,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,QAAQ,GAAsB,kBAAkB,EAAE,OAAO,GAA4B,EAAE;IAC9H,eAAe,CAAC,QAAQ,EAAE,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAC3D,CAAC"}
@@ -4,9 +4,27 @@
4
4
  * restic reads the repository and its keys from the environment (`RESTIC_REPOSITORY`,
5
5
  * `RESTIC_PASSWORD`, `B2_ACCOUNT_ID`, `AWS_ACCESS_KEY_ID` and the others), so no secret is an
6
6
  * input. `--password-command` is refused: it starts a program of the caller.
7
+ *
8
+ * A host gives secrets only to the restic process with the `env` option of `runRestic()` or of
9
+ * `createResticProcedures()`. The procedure input has no field for them.
7
10
  */
8
11
  import { type AnyProcedure } from "@procedures/client";
12
+ import { type CommandOptions, type CommandResult } from "@procedures/shell/command";
9
13
  import { type ArgValue } from "./argv.js";
14
+ /** The result of a restic command. */
15
+ export interface ResticResult {
16
+ exitCode: number;
17
+ success: boolean;
18
+ stdout: string;
19
+ stderr: string;
20
+ /** The JSON output, when `json` is true and restic gave JSON */
21
+ json?: unknown;
22
+ /** Present when an output stream was longer than `maxOutputBytes`: which one lost bytes. */
23
+ truncated?: {
24
+ stdout: boolean;
25
+ stderr: boolean;
26
+ } | undefined;
27
+ }
10
28
  /** A restic command: the subcommand path, its arguments and the repository. */
11
29
  export interface ResticCommand {
12
30
  readonly path: readonly string[];
@@ -17,6 +35,61 @@ export interface ResticCommand {
17
35
  }
18
36
  /** This function gives the argv of a restic command. It refuses the flags that start a program. */
19
37
  export declare function resticArgv(input: ResticCommand): string[];
20
- export declare const resticRunProcedure: AnyProcedure;
38
+ /** A function that starts a program: `runCommand` of `@procedures/shell/command`, or a fake in a test. */
39
+ export type ResticRunner = (command: string, options: CommandOptions) => Promise<CommandResult>;
40
+ /** The options of `runRestic()` that do not come from the command. */
41
+ export interface ResticRunOptions {
42
+ /** The restic program (default: `RESTIC_EXE` of the environment of the host, then `restic`) */
43
+ readonly executable?: string | undefined;
44
+ /**
45
+ * The variables for the restic process only, for example `RESTIC_PASSWORD`,
46
+ * `RESTIC_REPOSITORY` or `B2_ACCOUNT_ID`. They go on top of the environment of the host. The
47
+ * value `undefined` removes a variable of the host from the environment of restic.
48
+ */
49
+ readonly env?: Readonly<Record<string, string | undefined>> | undefined;
50
+ /** The working folder */
51
+ readonly cwd?: string | undefined;
52
+ /** The time limit, in milliseconds. At the limit, the process tree stops. */
53
+ readonly timeout?: number | undefined;
54
+ /** When the signal aborts, the process tree stops. */
55
+ readonly signal?: AbortSignal | undefined;
56
+ /**
57
+ * The most bytes kept of each output stream (default: 64 MiB, the default of `runCommand`).
58
+ * `restic ls` of a large snapshot can give more. Past the limit, the result has `truncated`.
59
+ */
60
+ readonly maxOutputBytes?: number | undefined;
61
+ /** The function that starts restic (default: `runCommand`). A test gives a fake. */
62
+ readonly run?: ResticRunner | undefined;
63
+ }
64
+ /**
65
+ * This function starts a restic command with argv, not through a shell, and reads its JSON
66
+ * output. `json` is true when the command does not set it, as in `restic.run`. The `env` values
67
+ * go only to the restic process: the host environment does not change, and the result does not
68
+ * contain them. The function throws when restic does not start. A failed command gives
69
+ * `success: false`.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * const executable = await findRestic();
74
+ * const result = await runRestic(
75
+ * { path: ["forget"], args: { "keep-daily": 14, prune: true } },
76
+ * { executable, env: { RESTIC_REPOSITORY: repository, RESTIC_PASSWORD: password } },
77
+ * );
78
+ * ```
79
+ */
80
+ export declare function runRestic(command: ResticCommand, options?: ResticRunOptions): Promise<ResticResult>;
81
+ /** The settings of the host for the procedures of this package. The procedure input cannot change them. */
82
+ export interface ResticProceduresOptions {
83
+ /** The variables for the restic process only, for example `RESTIC_PASSWORD`. Refer to `ResticRunOptions.env`. */
84
+ readonly env?: Readonly<Record<string, string | undefined>> | undefined;
85
+ /** The function that starts restic (default: `runCommand`). A test gives a fake. */
86
+ readonly run?: ResticRunner | undefined;
87
+ }
88
+ /**
89
+ * This function gives the procedures of this package with the settings of the host. With `env`,
90
+ * each restic process gets those variables, and the caller of `restic.run` does not see them.
91
+ */
92
+ export declare function createResticProcedures(options?: ResticProceduresOptions): AnyProcedure[];
21
93
  export declare const resticProcedures: AnyProcedure[];
94
+ export declare const resticRunProcedure: AnyProcedure;
22
95
  //# sourceMappingURL=procedures.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"procedures.d.ts","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAA+B,KAAK,YAAY,EAAyB,MAAM,oBAAoB,CAAC;AAG3G,OAAO,EAA8B,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAuCtE,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,GAAG,SAAS,CAAC;IAC/D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACrC;AAED,mGAAmG;AACnG,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,EAAE,CAMzD;AAED,eAAO,MAAM,kBAAkB,EAAE,YA+BP,CAAC;AAE3B,eAAO,MAAM,gBAAgB,EAAE,YAAY,EAAyB,CAAC"}
1
+ {"version":3,"file":"procedures.d.ts","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAA+B,KAAK,YAAY,EAAyB,MAAM,oBAAoB,CAAC;AAC3G,OAAO,EAAc,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAEhG,OAAO,EAA8B,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAuBtE,sCAAsC;AACtC,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,4FAA4F;IAC5F,SAAS,CAAC,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;CAC9D;AAWD,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,GAAG,SAAS,CAAC;IAC/D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACrC;AAED,mGAAmG;AACnG,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,EAAE,CAMzD;AAED,0GAA0G;AAC1G,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;AAEhG,sEAAsE;AACtE,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC;;;;OAIG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,SAAS,CAAC;IACxE,yBAAyB;IACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,sDAAsD;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC1C;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,oFAAoF;IACpF,QAAQ,CAAC,GAAG,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;CACzC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,YAAY,CAAC,CAsB7G;AAED,2GAA2G;AAC3G,MAAM,WAAW,uBAAuB;IACtC,iHAAiH;IACjH,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,SAAS,CAAC;IACxE,oFAAoF;IACpF,QAAQ,CAAC,GAAG,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,uBAA4B,GAAG,YAAY,EAAE,CAoB5F;AAED,eAAO,MAAM,gBAAgB,EAAE,YAAY,EAA6B,CAAC;AAEzE,eAAO,MAAM,kBAAkB,EAAE,YAAmC,CAAC"}
@@ -4,6 +4,9 @@
4
4
  * restic reads the repository and its keys from the environment (`RESTIC_REPOSITORY`,
5
5
  * `RESTIC_PASSWORD`, `B2_ACCOUNT_ID`, `AWS_ACCESS_KEY_ID` and the others), so no secret is an
6
6
  * input. `--password-command` is refused: it starts a program of the caller.
7
+ *
8
+ * A host gives secrets only to the restic process with the `env` option of `runRestic()` or of
9
+ * `createResticProcedures()`. The procedure input has no field for them.
7
10
  */
8
11
  import { createProcedure, zodAdapter } from "@procedures/client";
9
12
  import { runCommand } from "@procedures/shell/command";
@@ -33,6 +36,7 @@ const RunOutputSchema = z.object({
33
36
  stdout: z.string(),
34
37
  stderr: z.string(),
35
38
  json: z.unknown().optional(),
39
+ truncated: z.object({ stdout: z.boolean(), stderr: z.boolean() }).optional(),
36
40
  });
37
41
  /** This function gives the argv of a restic command. It refuses the flags that start a program. */
38
42
  export function resticArgv(input) {
@@ -43,38 +47,68 @@ export function resticArgv(input) {
43
47
  const flags = [...(input.repository !== undefined ? [`--repo=${input.repository}`] : []), ...(input.json ? ["--json"] : [])];
44
48
  return buildArgv({ path: input.path, args: input.args, positional: input.positional, flags });
45
49
  }
46
- export const resticRunProcedure = createProcedure()
47
- .path(["restic", "run"])
48
- .input(zodAdapter(RunInputSchema))
49
- .output(zodAdapter(RunOutputSchema))
50
- .meta({
51
- description: "Run a restic command (backup, snapshots, restore, forget, check)",
52
- args: ["path"],
53
- shorts: { cwd: "C", timeout: "t" },
54
- output: "json",
55
- // A backup reads files, a restore writes them, and forget and prune delete snapshots. A
56
- // repository of type sftp or rclone starts that program.
57
- effects: { reads: ["fs", "network", "process"], writes: ["fs", "network", "process"], destructive: true },
58
- })
59
- .handler(async (input, ctx) => {
60
- const executable = input.executable ?? process.env["RESTIC_EXE"] ?? "restic";
61
- const result = await runCommand(executable, {
62
- args: resticArgv(input),
63
- cwd: input.cwd,
64
- timeout: input.timeout,
65
- signal: ctx?.signal,
50
+ /**
51
+ * This function starts a restic command with argv, not through a shell, and reads its JSON
52
+ * output. `json` is true when the command does not set it, as in `restic.run`. The `env` values
53
+ * go only to the restic process: the host environment does not change, and the result does not
54
+ * contain them. The function throws when restic does not start. A failed command gives
55
+ * `success: false`.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * const executable = await findRestic();
60
+ * const result = await runRestic(
61
+ * { path: ["forget"], args: { "keep-daily": 14, prune: true } },
62
+ * { executable, env: { RESTIC_REPOSITORY: repository, RESTIC_PASSWORD: password } },
63
+ * );
64
+ * ```
65
+ */
66
+ export async function runRestic(command, options = {}) {
67
+ const executable = options.executable ?? process.env["RESTIC_EXE"] ?? "restic";
68
+ const json = command.json ?? true;
69
+ const run = options.run ?? runCommand;
70
+ const result = await run(executable, {
71
+ args: resticArgv({ ...command, json }),
72
+ cwd: options.cwd,
73
+ timeout: options.timeout,
74
+ signal: options.signal,
75
+ env: options.env === undefined ? undefined : { ...options.env },
76
+ ...(options.maxOutputBytes !== undefined ? { maxOutputBytes: options.maxOutputBytes } : {}),
66
77
  });
67
78
  if (result.error)
68
79
  throw new Error(`restic did not start: ${result.error}`);
69
- const json = input.json ? parseJsonOutput(result.stdout) : undefined;
80
+ const parsed = json ? parseJsonOutput(result.stdout) : undefined;
70
81
  return {
71
82
  exitCode: result.exitCode,
72
83
  success: result.exitCode === 0,
73
84
  stdout: result.stdout,
74
85
  stderr: result.stderr,
75
- ...(json !== undefined ? { json } : {}),
86
+ ...(parsed !== undefined ? { json: parsed } : {}),
87
+ ...(result.truncated !== undefined ? { truncated: result.truncated } : {}),
76
88
  };
77
- })
78
- .build();
79
- export const resticProcedures = [resticRunProcedure];
89
+ }
90
+ /**
91
+ * This function gives the procedures of this package with the settings of the host. With `env`,
92
+ * each restic process gets those variables, and the caller of `restic.run` does not see them.
93
+ */
94
+ export function createResticProcedures(options = {}) {
95
+ const run = createProcedure()
96
+ .path(["restic", "run"])
97
+ .input(zodAdapter(RunInputSchema))
98
+ .output(zodAdapter(RunOutputSchema))
99
+ .meta({
100
+ description: "Run a restic command (backup, snapshots, restore, forget, check)",
101
+ args: ["path"],
102
+ shorts: { cwd: "C", timeout: "t" },
103
+ output: "json",
104
+ // A backup reads files, a restore writes them, and forget and prune delete snapshots. A
105
+ // repository of type sftp or rclone starts that program.
106
+ effects: { reads: ["fs", "network", "process"], writes: ["fs", "network", "process"], destructive: true },
107
+ })
108
+ .handler(async (input, ctx) => runRestic(input, { executable: input.executable, env: options.env, cwd: input.cwd, timeout: input.timeout, signal: ctx?.signal, run: options.run }))
109
+ .build();
110
+ return [run];
111
+ }
112
+ export const resticProcedures = createResticProcedures();
113
+ export const resticRunProcedure = resticProcedures[0];
80
114
  //# sourceMappingURL=procedures.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"procedures.js","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"procedures.js","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,eAAe,EAAE,UAAU,EAA4C,MAAM,oBAAoB,CAAC;AAC3G,OAAO,EAAE,UAAU,EAA2C,MAAM,2BAA2B,CAAC;AAChG,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAiB,MAAM,WAAW,CAAC;AAEtE,2CAA2C;AAC3C,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAEpD,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,IAAI,EAAE,CAAC;SACJ,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;SACzC,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,yEAAyE,CAAC;IACtF,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6DAA6D,CAAC;IAClH,IAAI,EAAE,CAAC;SACJ,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;SACvF,QAAQ,EAAE;SACV,QAAQ,CAAC,iEAAiE,CAAC;IAC9E,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yEAAyE,CAAC;IAC5H,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,0DAA0D,CAAC;IACpG,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0EAA0E,CAAC;IAC7H,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oBAAoB,CAAC;IACzD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;CAC5F,CAAC,CAAC;AAeH,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;IACpB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC5B,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE;CAC7E,CAAC,CAAC;AAWH,mGAAmG;AACnG,MAAM,UAAU,UAAU,CAAC,KAAoB;IAC7C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;QAChD,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,uBAAuB,CAAC,CAAC;IAClG,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7H,OAAO,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;AAChG,CAAC;AA8BD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAsB,EAAE,OAAO,GAAqB,EAAE;IACpF,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,QAAQ,CAAC;IAC/E,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC;IAClC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,UAAU,CAAC;IACtC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,UAAU,EAAE;QACnC,IAAI,EAAE,UAAU,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC;QACtC,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,GAAG,EAAE,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE;QAC/D,GAAG,CAAC,OAAO,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5F,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAC3E,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,OAAO,EAAE,MAAM,CAAC,QAAQ,KAAK,CAAC;QAC9B,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,GAAG,CAAC,MAAM,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC3E,CAAC;AACJ,CAAC;AAUD;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAO,GAA4B,EAAE;IAC1E,MAAM,GAAG,GAAG,eAAe,EAAE;SAC1B,IAAI,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;SACvB,KAAK,CAAC,UAAU,CAAW,cAAc,CAAC,CAAC;SAC3C,MAAM,CAAC,UAAU,CAAY,eAAe,CAAC,CAAC;SAC9C,IAAI,CAAC;QACJ,WAAW,EAAE,kEAAkE;QAC/E,IAAI,EAAE,CAAC,MAAM,CAAC;QACd,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE;QAClC,MAAM,EAAE,MAAM;QACd,wFAAwF;QACxF,yDAAyD;QACzD,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE;KAC1G,CAAC;SACD,OAAO,CACN,KAAK,EAAE,KAAe,EAAE,GAAsB,EAAsB,EAAE,CACpE,SAAS,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CACtJ;SACA,KAAK,EAAkB,CAAC;IAC3B,OAAO,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAmB,sBAAsB,EAAE,CAAC;AAEzE,MAAM,CAAC,MAAM,kBAAkB,GAAiB,gBAAgB,CAAC,CAAC,CAAE,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@procedures/restic",
4
- "version": "0.3.0",
4
+ "version": "0.4.0",
5
5
  "description": "The restic backup program as one procedure: backup, snapshots, restore and forget, with JSON output and no secret as input.",
6
6
  "keywords": [
7
7
  "restic",
@@ -56,9 +56,9 @@
56
56
  "provenance": true
57
57
  },
58
58
  "dependencies": {
59
- "@procedures/client": "0.3.0",
59
+ "@procedures/client": "0.4.0",
60
60
  "zod": "^4.6.5",
61
- "@procedures/shell": "0.3.0"
61
+ "@procedures/shell": "0.4.0"
62
62
  },
63
63
  "devDependencies": {
64
64
  "typescript": "^7.0.2",
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The lookup of the restic program. WinGet installs restic as a portable program in its own
3
+ * folder, and that folder is not always on the `PATH`.
4
+ */
5
+
6
+ import { existsSync } from "node:fs";
7
+ import { readdir } from "node:fs/promises";
8
+ import { win32 } from "node:path";
9
+
10
+ /** The settings of `findRestic()`. A test gives other values. */
11
+ export interface FindResticOptions {
12
+ /** The environment to read (default: `process.env`) */
13
+ readonly env?: Readonly<Record<string, string | undefined>> | undefined;
14
+ /** The platform (default: `process.platform`) */
15
+ readonly platform?: NodeJS.Platform | undefined;
16
+ /** This function tells if a path exists (default: `existsSync`). */
17
+ readonly exists?: ((path: string) => boolean) | undefined;
18
+ /** This function gives the names in a folder (default: `readdir`). */
19
+ readonly list?: ((folder: string) => Promise<string[]>) | undefined;
20
+ }
21
+
22
+ /**
23
+ * This function finds the restic program. It gives the first of these:
24
+ *
25
+ * 1. `RESTIC_EXE` of the environment, when it is set.
26
+ * 2. On Windows, the `restic*.exe` of a WinGet package `restic.restic*` in
27
+ * `%LOCALAPPDATA%\Microsoft\WinGet\Packages`.
28
+ * 3. `restic`: the `PATH` gives the program.
29
+ */
30
+ export async function findRestic(options: FindResticOptions = {}): Promise<string> {
31
+ const env = options.env ?? process.env;
32
+ const exists = options.exists ?? existsSync;
33
+ const list = options.list ?? ((folder: string) => readdir(folder));
34
+ const override = env["RESTIC_EXE"];
35
+ if (override !== undefined && override.length > 0) return override;
36
+
37
+ const localAppData = env["LOCALAPPDATA"];
38
+ if ((options.platform ?? process.platform) === "win32" && localAppData !== undefined && localAppData.length > 0) {
39
+ const packages = win32.join(localAppData, "Microsoft", "WinGet", "Packages");
40
+ if (exists(packages)) {
41
+ try {
42
+ for (const folder of (await list(packages)).sort()) {
43
+ if (!folder.toLowerCase().startsWith("restic.restic")) continue;
44
+ const names = (await list(win32.join(packages, folder))).sort();
45
+ const program = names.find((name) => name.toLowerCase().startsWith("restic") && name.toLowerCase().endsWith(".exe"));
46
+ if (program !== undefined) return win32.join(packages, folder, program);
47
+ }
48
+ } catch {
49
+ // A folder that cannot be read gives no program: the PATH is the last choice
50
+ }
51
+ }
52
+ }
53
+ return "restic";
54
+ }
package/src/index.ts CHANGED
@@ -2,3 +2,4 @@
2
2
  export * from "./module.js";
3
3
  export * from "./procedures.js";
4
4
  export * from "./argv.js";
5
+ export * from "./executable.js";
package/src/module.ts CHANGED
@@ -3,16 +3,30 @@
3
3
  */
4
4
 
5
5
  import { defineModule, registerModules, PROCEDURE_REGISTRY, type ProcedureModule, type ProcedureRegistry } from "@procedures/client";
6
- import { resticProcedures } from "./procedures.js";
6
+ import { createResticProcedures, resticProcedures, type ResticProceduresOptions } from "./procedures.js";
7
7
 
8
8
  export const resticModule: ProcedureModule = defineModule("@procedures/restic", resticProcedures);
9
9
 
10
10
  export default resticModule;
11
11
 
12
+ /**
13
+ * This function gives the module with the settings of the host, for example the secrets of the
14
+ * repository in `env`. Without options, it gives `resticModule`.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const registry = createRegistry(createResticModule({ env: { RESTIC_REPOSITORY: repository, RESTIC_PASSWORD: password } }));
19
+ * ```
20
+ */
21
+ export function createResticModule(options: ResticProceduresOptions = {}): ProcedureModule {
22
+ if (Object.values(options).every((value) => value === undefined)) return resticModule;
23
+ return defineModule("@procedures/restic", createResticProcedures(options));
24
+ }
25
+
12
26
  /**
13
27
  * This function registers the procedures of @procedures/restic in a registry. The default is the
14
- * global registry.
28
+ * global registry. The options are the options of `createResticModule()`.
15
29
  */
16
- export function registerResticProcedures(registry: ProcedureRegistry = PROCEDURE_REGISTRY): void {
17
- registerModules(registry, [resticModule]);
30
+ export function registerResticProcedures(registry: ProcedureRegistry = PROCEDURE_REGISTRY, options: ResticProceduresOptions = {}): void {
31
+ registerModules(registry, [createResticModule(options)]);
18
32
  }