@skippercorp/skipper 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/package.json +48 -0
- package/src/app/cli.ts +31 -0
- package/src/app/register-commands.ts +37 -0
- package/src/command/a.ts +213 -0
- package/src/command/aws/bootstrap.ts +508 -0
- package/src/command/aws/cloudformation.ts +243 -0
- package/src/command/aws/defaults.ts +103 -0
- package/src/command/aws/deploy-template.ts +308 -0
- package/src/command/aws/deploy.ts +593 -0
- package/src/command/aws/github.ts +358 -0
- package/src/command/aws/index.ts +17 -0
- package/src/command/aws/lambda/eventbridge-handler.ts +83 -0
- package/src/command/aws/lambda/handler.ts +521 -0
- package/src/command/aws/lambda/types.ts +86 -0
- package/src/command/aws/network.ts +51 -0
- package/src/command/aws/run.ts +566 -0
- package/src/command/aws/template.ts +406 -0
- package/src/command/aws/verify-issue-subscription.ts +782 -0
- package/src/command/clone.ts +67 -0
- package/src/command/rm.ts +126 -0
- package/src/command/run.ts +43 -0
- package/src/index.ts +16 -0
- package/src/shared/command/interactive.ts +120 -0
- package/src/shared/validation/parse-json.ts +59 -0
- package/src/worker/aws-params.ts +54 -0
- package/src/worker/contract.ts +324 -0
- package/src/worker/github-events.ts +57 -0
- package/src/worker/load.ts +86 -0
- package/src/worker/route.ts +91 -0
- package/src/worker/serialize.ts +175 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Register clone command.
|
|
5
|
+
*
|
|
6
|
+
* @since 1.0.0
|
|
7
|
+
* @category Worktree
|
|
8
|
+
*/
|
|
9
|
+
export function registerCloneCommand(program: Command): void {
|
|
10
|
+
program
|
|
11
|
+
.command("clone")
|
|
12
|
+
.description("Clone a GitHub repository using SSH")
|
|
13
|
+
.argument("<url>", "GitHub repository URL or owner/repo")
|
|
14
|
+
.action(runClone);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Execute clone command.
|
|
19
|
+
*
|
|
20
|
+
* @since 1.0.0
|
|
21
|
+
* @category Worktree
|
|
22
|
+
*/
|
|
23
|
+
async function runClone(url: string): Promise<void> {
|
|
24
|
+
const repo = normalizeRepoInput(url);
|
|
25
|
+
const targetDir = buildTargetDir(repo);
|
|
26
|
+
await Bun.$`mkdir -p ${targetDir}`;
|
|
27
|
+
await Bun.$`gh repo clone ${repo} ${targetDir} -- --recursive`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Normalize clone input to owner/repo.
|
|
32
|
+
*
|
|
33
|
+
* @since 1.0.0
|
|
34
|
+
* @category Worktree
|
|
35
|
+
*/
|
|
36
|
+
function normalizeRepoInput(url: string): string {
|
|
37
|
+
if (url.startsWith("https://github.com/")) {
|
|
38
|
+
return parseRepo(url, /github\.com\/([^\/]+\/[^\/]+?)(?:\.git)?$/);
|
|
39
|
+
}
|
|
40
|
+
if (url.startsWith("git@github.com:")) {
|
|
41
|
+
return parseRepo(url, /github\.com:([^\/]+\/[^\/]+?)(?:\.git)?$/);
|
|
42
|
+
}
|
|
43
|
+
return url;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Parse repo with regex fallback.
|
|
48
|
+
*
|
|
49
|
+
* @since 1.0.0
|
|
50
|
+
* @category Worktree
|
|
51
|
+
*/
|
|
52
|
+
function parseRepo(url: string, matcher: RegExp): string {
|
|
53
|
+
const match = url.match(matcher);
|
|
54
|
+
return match?.[1] ?? url;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Build clone target directory path.
|
|
59
|
+
*
|
|
60
|
+
* @since 1.0.0
|
|
61
|
+
* @category Worktree
|
|
62
|
+
*/
|
|
63
|
+
function buildTargetDir(repo: string): string {
|
|
64
|
+
const baseDir = `${process.env.HOME}/.local/share/github`;
|
|
65
|
+
const repoName = repo.split("/")[1] ?? repo;
|
|
66
|
+
return `${baseDir}/${repoName}`;
|
|
67
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import {
|
|
3
|
+
assertNonEmpty,
|
|
4
|
+
listDirectory,
|
|
5
|
+
selectWithFzf,
|
|
6
|
+
} from "../shared/command/interactive.js";
|
|
7
|
+
|
|
8
|
+
type WorktreeRef = {
|
|
9
|
+
repo: string;
|
|
10
|
+
worktree: string;
|
|
11
|
+
path: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Register remove worktree command.
|
|
16
|
+
*
|
|
17
|
+
* @since 1.0.0
|
|
18
|
+
* @category Worktree
|
|
19
|
+
*/
|
|
20
|
+
export function registerRemoveCommand(program: Command): void {
|
|
21
|
+
program
|
|
22
|
+
.command("rm")
|
|
23
|
+
.description("Remove a worktree (repo+worktree)")
|
|
24
|
+
.action(runRemoveCommand);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Execute remove command flow.
|
|
29
|
+
*
|
|
30
|
+
* @since 1.0.0
|
|
31
|
+
* @category Worktree
|
|
32
|
+
*/
|
|
33
|
+
async function runRemoveCommand(): Promise<void> {
|
|
34
|
+
const worktreeBaseDir = `${process.env.HOME}/.local/share/skipper/worktree`;
|
|
35
|
+
const allWorktrees = await collectWorktrees(worktreeBaseDir);
|
|
36
|
+
assertNonEmpty(allWorktrees, "No worktrees found");
|
|
37
|
+
const selected = await selectWorktree(allWorktrees);
|
|
38
|
+
if (!selected) {
|
|
39
|
+
console.log("No worktree selected");
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
await removeWorktree(selected);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Collect all worktrees under base path.
|
|
47
|
+
*
|
|
48
|
+
* @since 1.0.0
|
|
49
|
+
* @category Worktree
|
|
50
|
+
*/
|
|
51
|
+
async function collectWorktrees(worktreeBaseDir: string): Promise<WorktreeRef[]> {
|
|
52
|
+
const repos = await listDirectory(worktreeBaseDir);
|
|
53
|
+
if (repos.length === 0) {
|
|
54
|
+
console.error("No worktrees found in ~/.local/share/skipper/worktree");
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
const allWorktrees: WorktreeRef[] = [];
|
|
58
|
+
for (const repo of repos) {
|
|
59
|
+
const repoDir = `${worktreeBaseDir}/${repo}`;
|
|
60
|
+
const worktrees = await listDirectory(repoDir);
|
|
61
|
+
for (const worktree of worktrees) {
|
|
62
|
+
allWorktrees.push({ repo, worktree, path: `${repoDir}/${worktree}` });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return allWorktrees;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Select worktree with fzf.
|
|
70
|
+
*
|
|
71
|
+
* @since 1.0.0
|
|
72
|
+
* @category Worktree
|
|
73
|
+
*/
|
|
74
|
+
async function selectWorktree(worktrees: WorktreeRef[]): Promise<WorktreeRef | undefined> {
|
|
75
|
+
const labels = worktrees.map((item) => `${item.repo}/${item.worktree}`);
|
|
76
|
+
const selected = await selectWithFzf(labels, "Select worktree to remove: ");
|
|
77
|
+
const trimmed = selected?.trim();
|
|
78
|
+
if (!trimmed) return undefined;
|
|
79
|
+
return worktrees.find((item) => `${item.repo}/${item.worktree}` === trimmed);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Remove worktree and tmux session.
|
|
84
|
+
*
|
|
85
|
+
* @since 1.0.0
|
|
86
|
+
* @category Worktree
|
|
87
|
+
*/
|
|
88
|
+
async function removeWorktree(target: WorktreeRef): Promise<void> {
|
|
89
|
+
const githubDir = `${process.env.HOME}/.local/share/github`;
|
|
90
|
+
const repoPath = `${githubDir}/${target.repo}`;
|
|
91
|
+
const sessionName = `${target.repo}-${target.worktree}`;
|
|
92
|
+
const name = `${target.repo}/${target.worktree}`;
|
|
93
|
+
console.log(`Removing worktree: ${name}`);
|
|
94
|
+
await removeGitWorktree(repoPath, target.path);
|
|
95
|
+
await Bun.$`rm -rf ${target.path}`;
|
|
96
|
+
if (await tmuxSessionExists(sessionName)) {
|
|
97
|
+
await Bun.$`tmux kill-session -t ${sessionName}`;
|
|
98
|
+
console.log(`Killed tmux session: ${sessionName}`);
|
|
99
|
+
}
|
|
100
|
+
console.log(`Removed worktree: ${name}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Remove git worktree from repository.
|
|
105
|
+
*
|
|
106
|
+
* @since 1.0.0
|
|
107
|
+
* @category Worktree
|
|
108
|
+
*/
|
|
109
|
+
async function removeGitWorktree(repoPath: string, worktreePath: string): Promise<void> {
|
|
110
|
+
const result = await Bun.$`git -C ${repoPath} worktree remove ${worktreePath}`.nothrow();
|
|
111
|
+
if (result.exitCode === 0) return;
|
|
112
|
+
const stderr = result.stderr.toString().trim();
|
|
113
|
+
const fallback = `git worktree remove failed with code ${result.exitCode}`;
|
|
114
|
+
throw new Error(stderr || fallback);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check tmux session existence.
|
|
119
|
+
*
|
|
120
|
+
* @since 1.0.0
|
|
121
|
+
* @category Worktree
|
|
122
|
+
*/
|
|
123
|
+
async function tmuxSessionExists(sessionName: string): Promise<boolean> {
|
|
124
|
+
const output = await Bun.$`tmux has-session -t ${sessionName} 2>/dev/null && echo "yes" || echo "no"`.text();
|
|
125
|
+
return output.trim() === "yes";
|
|
126
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import {
|
|
3
|
+
assertNonEmpty,
|
|
4
|
+
listDirectory,
|
|
5
|
+
selectWithFzf,
|
|
6
|
+
} from "../shared/command/interactive.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Register run command.
|
|
10
|
+
*
|
|
11
|
+
* @since 1.0.0
|
|
12
|
+
* @category Worktree
|
|
13
|
+
*/
|
|
14
|
+
export function registerRunCommand(program: Command): void {
|
|
15
|
+
program
|
|
16
|
+
.command("run")
|
|
17
|
+
.description("Pull changes and run opencode with a prompt")
|
|
18
|
+
.argument("<prompt>", "The prompt to pass to opencode")
|
|
19
|
+
.action(runCommand);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Execute run command flow.
|
|
24
|
+
*
|
|
25
|
+
* @since 1.0.0
|
|
26
|
+
* @category Worktree
|
|
27
|
+
*/
|
|
28
|
+
async function runCommand(prompt: string): Promise<void> {
|
|
29
|
+
const baseDir = `${process.env.HOME}/.local/share/github`;
|
|
30
|
+
const repoList = await listDirectory(baseDir);
|
|
31
|
+
assertNonEmpty(repoList, "No repositories found in ~/.local/share/github");
|
|
32
|
+
const repo = await selectWithFzf(repoList, "Select repository: ");
|
|
33
|
+
if (!repo) {
|
|
34
|
+
console.log("No repository selected");
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
const repoPath = `${baseDir}/${repo}`;
|
|
38
|
+
console.log(`Selected: ${repo}`);
|
|
39
|
+
console.log("Pulling latest changes...");
|
|
40
|
+
await Bun.$`git -C ${repoPath} pull`;
|
|
41
|
+
console.log("Running opencode...");
|
|
42
|
+
await Bun.$`opencode run ${prompt}`.cwd(repoPath);
|
|
43
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { runCli } from "./app/cli.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Process entrypoint.
|
|
6
|
+
*
|
|
7
|
+
* @since 1.0.0
|
|
8
|
+
* @category CLI
|
|
9
|
+
*/
|
|
10
|
+
try {
|
|
11
|
+
await runCli();
|
|
12
|
+
} catch (error) {
|
|
13
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
14
|
+
console.error(message);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Glob } from "bun";
|
|
2
|
+
import type { $ } from "bun";
|
|
3
|
+
|
|
4
|
+
const DIRECTORY_GLOB = new Glob("*");
|
|
5
|
+
const FZF_NO_MATCH_EXIT_CODE = 1;
|
|
6
|
+
const FZF_CANCELLED_EXIT_CODES = new Set([130]);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* List direct directory entries.
|
|
10
|
+
*
|
|
11
|
+
* @since 1.0.0
|
|
12
|
+
* @category Shared.Command
|
|
13
|
+
*/
|
|
14
|
+
export async function listDirectory(path: string): Promise<string[]> {
|
|
15
|
+
try {
|
|
16
|
+
const entries = await Array.fromAsync(
|
|
17
|
+
DIRECTORY_GLOB.scan({ cwd: path, onlyFiles: false }),
|
|
18
|
+
);
|
|
19
|
+
return entries
|
|
20
|
+
.filter((entry) => entry.trim().length > 0)
|
|
21
|
+
.sort((a, b) => a.localeCompare(b));
|
|
22
|
+
} catch (error) {
|
|
23
|
+
if (isMissingDirectoryError(error)) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Exit when values list is empty.
|
|
32
|
+
*
|
|
33
|
+
* @since 1.0.0
|
|
34
|
+
* @category Shared.Command
|
|
35
|
+
*/
|
|
36
|
+
export function assertNonEmpty<T>(values: T[], message: string): void {
|
|
37
|
+
if (values.length > 0) return;
|
|
38
|
+
console.error(message);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Select one value with fzf.
|
|
44
|
+
*
|
|
45
|
+
* @since 1.0.0
|
|
46
|
+
* @category Shared.Command
|
|
47
|
+
*/
|
|
48
|
+
export async function selectWithFzf(
|
|
49
|
+
values: string[],
|
|
50
|
+
prompt: string,
|
|
51
|
+
): Promise<string | undefined> {
|
|
52
|
+
const output = await runFzf(values, ["--prompt", prompt]);
|
|
53
|
+
const trimmed = output?.trim();
|
|
54
|
+
if (!trimmed) return undefined;
|
|
55
|
+
return trimmed;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Select one value or return typed query with fzf.
|
|
60
|
+
*
|
|
61
|
+
* @since 1.0.0
|
|
62
|
+
* @category Shared.Command
|
|
63
|
+
*/
|
|
64
|
+
export async function selectOrQueryWithFzf(
|
|
65
|
+
values: string[],
|
|
66
|
+
prompt: string,
|
|
67
|
+
): Promise<string | undefined> {
|
|
68
|
+
const output = await runFzf(values, ["--prompt", prompt, "--print-query"]);
|
|
69
|
+
if (!output) return undefined;
|
|
70
|
+
return output.trim();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Run fzf with values as stdin.
|
|
75
|
+
*
|
|
76
|
+
* @since 1.0.0
|
|
77
|
+
* @category Shared.Command
|
|
78
|
+
*/
|
|
79
|
+
async function runFzf(values: string[], args: string[]): Promise<string | undefined> {
|
|
80
|
+
const input = values.join("\n");
|
|
81
|
+
const result = await Bun.$`echo ${input} | fzf ${args}`.nothrow();
|
|
82
|
+
const output = result.stdout.toString();
|
|
83
|
+
if (result.exitCode === 0) {
|
|
84
|
+
return output;
|
|
85
|
+
}
|
|
86
|
+
if (result.exitCode === FZF_NO_MATCH_EXIT_CODE) {
|
|
87
|
+
if (output.trim().length > 0) return output;
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
if (FZF_CANCELLED_EXIT_CODES.has(result.exitCode)) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
throw new Error(`fzf failed: ${formatShellFailure(result)}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Format shell output for error reporting.
|
|
98
|
+
*
|
|
99
|
+
* @since 1.0.0
|
|
100
|
+
* @category Shared.Command
|
|
101
|
+
*/
|
|
102
|
+
function formatShellFailure(result: $.ShellOutput): string {
|
|
103
|
+
const stderr = result.stderr.toString().trim();
|
|
104
|
+
if (stderr.length > 0) return stderr;
|
|
105
|
+
const stdout = result.text().trim();
|
|
106
|
+
if (stdout.length > 0) return stdout;
|
|
107
|
+
return `exit code ${result.exitCode}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Check if directory is missing.
|
|
112
|
+
*
|
|
113
|
+
* @since 1.0.0
|
|
114
|
+
* @category Shared.Command
|
|
115
|
+
*/
|
|
116
|
+
function isMissingDirectoryError(error: unknown): boolean {
|
|
117
|
+
if (!error || typeof error !== "object") return false;
|
|
118
|
+
const maybe = error as { code?: string };
|
|
119
|
+
return maybe.code === "ENOENT" || maybe.code === "ENOTDIR";
|
|
120
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime validator function.
|
|
3
|
+
*
|
|
4
|
+
* @since 1.0.0
|
|
5
|
+
* @category Shared
|
|
6
|
+
*/
|
|
7
|
+
export type Validator<T> = (value: unknown) => value is T;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Parse JSON and validate with type guard.
|
|
11
|
+
*
|
|
12
|
+
* @since 1.0.0
|
|
13
|
+
* @category Shared
|
|
14
|
+
*/
|
|
15
|
+
export function parseJson<T>(raw: string, validate: Validator<T>, ctx: string): T {
|
|
16
|
+
const parsed = parseUnknownJson(raw, ctx);
|
|
17
|
+
if (!validate(parsed)) {
|
|
18
|
+
throw new Error(`invalid ${ctx}`);
|
|
19
|
+
}
|
|
20
|
+
return parsed;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parse unknown JSON safely.
|
|
25
|
+
*
|
|
26
|
+
* @since 1.0.0
|
|
27
|
+
* @category Shared
|
|
28
|
+
*/
|
|
29
|
+
export function parseUnknownJson(raw: string, ctx: string): unknown {
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(raw);
|
|
32
|
+
} catch {
|
|
33
|
+
throw new Error(`invalid JSON for ${ctx}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Check plain object shape.
|
|
39
|
+
*
|
|
40
|
+
* @since 1.0.0
|
|
41
|
+
* @category Shared
|
|
42
|
+
*/
|
|
43
|
+
export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
44
|
+
return typeof value === "object" && value !== null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Read string field from object.
|
|
49
|
+
*
|
|
50
|
+
* @since 1.0.0
|
|
51
|
+
* @category Shared
|
|
52
|
+
*/
|
|
53
|
+
export function readOptionalString(
|
|
54
|
+
value: Record<string, unknown>,
|
|
55
|
+
key: string,
|
|
56
|
+
): string | undefined {
|
|
57
|
+
const found = value[key];
|
|
58
|
+
return typeof found === "string" ? found : undefined;
|
|
59
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export const WORKER_SCHEMA_VERSION = "1";
|
|
2
|
+
export const WORKER_CHUNK_COUNT = 12;
|
|
3
|
+
export const WORKER_CHUNK_SIZE = 3500;
|
|
4
|
+
|
|
5
|
+
export const WORKERS_ENCODING_PARAM = "WorkersEncoding";
|
|
6
|
+
export const WORKERS_SHA256_PARAM = "WorkersSha256";
|
|
7
|
+
export const WORKERS_SCHEMA_VERSION_PARAM = "WorkersSchemaVersion";
|
|
8
|
+
export const WORKERS_CHUNK_COUNT_PARAM = "WorkersChunkCount";
|
|
9
|
+
export const WORKERS_ENCODING = "gzip-base64-v1";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Build worker chunk parameter key from index.
|
|
13
|
+
*
|
|
14
|
+
* @since 1.0.0
|
|
15
|
+
* @category Shared
|
|
16
|
+
*/
|
|
17
|
+
export function getWorkerChunkParameterKey(index: number): string {
|
|
18
|
+
return `WorkersChunk${index.toString().padStart(2, "0")}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* List worker chunk parameter keys.
|
|
23
|
+
*
|
|
24
|
+
* @since 1.0.0
|
|
25
|
+
* @category Shared
|
|
26
|
+
*/
|
|
27
|
+
export function listWorkerChunkParameterKeys(count = WORKER_CHUNK_COUNT): string[] {
|
|
28
|
+
const keys: string[] = [];
|
|
29
|
+
for (let index = 0; index < count; index += 1) {
|
|
30
|
+
keys.push(getWorkerChunkParameterKey(index));
|
|
31
|
+
}
|
|
32
|
+
return keys;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Build empty/default worker parameter values.
|
|
37
|
+
*
|
|
38
|
+
* @since 1.0.0
|
|
39
|
+
* @category Shared
|
|
40
|
+
*/
|
|
41
|
+
export function createDefaultWorkerParameterValues(
|
|
42
|
+
count = WORKER_CHUNK_COUNT,
|
|
43
|
+
): Record<string, string> {
|
|
44
|
+
const values: Record<string, string> = {
|
|
45
|
+
[WORKERS_ENCODING_PARAM]: "",
|
|
46
|
+
[WORKERS_SHA256_PARAM]: "",
|
|
47
|
+
[WORKERS_SCHEMA_VERSION_PARAM]: WORKER_SCHEMA_VERSION,
|
|
48
|
+
[WORKERS_CHUNK_COUNT_PARAM]: "0",
|
|
49
|
+
};
|
|
50
|
+
for (const key of listWorkerChunkParameterKeys(count)) {
|
|
51
|
+
values[key] = "";
|
|
52
|
+
}
|
|
53
|
+
return values;
|
|
54
|
+
}
|