@repokit/core 0.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.
Files changed (79) hide show
  1. package/.vscode/settings.json +11 -0
  2. package/Cargo.lock +517 -0
  3. package/Cargo.toml +26 -0
  4. package/README.md +204 -0
  5. package/dist/cjs/CommandParser.js +68 -0
  6. package/dist/cjs/ConfigurationParser.js +46 -0
  7. package/dist/cjs/RepoKitCommand.js +16 -0
  8. package/dist/cjs/RepoKitConfig.js +10 -0
  9. package/dist/cjs/TaskPooler.js +42 -0
  10. package/dist/cjs/commands/parse_commands.js +15 -0
  11. package/dist/cjs/commands/parse_configuration.js +15 -0
  12. package/dist/cjs/index.js +19 -0
  13. package/dist/cjs/package.json +3 -0
  14. package/dist/cjs/types.js +2 -0
  15. package/dist/mjs/CommandParser.js +51 -0
  16. package/dist/mjs/ConfigurationParser.js +31 -0
  17. package/dist/mjs/RepoKitCommand.js +16 -0
  18. package/dist/mjs/RepoKitConfig.js +8 -0
  19. package/dist/mjs/TaskPooler.js +28 -0
  20. package/dist/mjs/commands/parse_commands.js +4 -0
  21. package/dist/mjs/commands/parse_configuration.js +4 -0
  22. package/dist/mjs/index.js +3 -0
  23. package/dist/mjs/package.json +4 -0
  24. package/dist/mjs/types.js +1 -0
  25. package/dist/types/CommandParser.d.ts +5 -0
  26. package/dist/types/ConfigurationParser.d.ts +4 -0
  27. package/dist/types/RepoKitCommand.d.ts +14 -0
  28. package/dist/types/RepoKitConfig.d.ts +6 -0
  29. package/dist/types/TaskPooler.d.ts +10 -0
  30. package/dist/types/commands/parse_commands.d.ts +1 -0
  31. package/dist/types/commands/parse_configuration.d.ts +1 -0
  32. package/dist/types/index.d.ts +3 -0
  33. package/dist/types/types.d.ts +18 -0
  34. package/install.sh +43 -0
  35. package/package.json +40 -0
  36. package/repokit.ts +36 -0
  37. package/src/CommandParser.ts +59 -0
  38. package/src/ConfigurationParser.ts +34 -0
  39. package/src/RepoKitCommand.ts +24 -0
  40. package/src/RepoKitConfig.ts +10 -0
  41. package/src/TaskPooler.ts +31 -0
  42. package/src/commands/parse_commands.ts +5 -0
  43. package/src/commands/parse_configuration.ts +5 -0
  44. package/src/index.ts +3 -0
  45. package/src/types.ts +22 -0
  46. package/tsconfig.json +24 -0
  47. package/workspaces/concurrency/mod.rs +1 -0
  48. package/workspaces/concurrency/thread_pool.rs +32 -0
  49. package/workspaces/configuration/configuration.rs +47 -0
  50. package/workspaces/configuration/configuration_template.ts +23 -0
  51. package/workspaces/configuration/mod.rs +1 -0
  52. package/workspaces/executables/external_executable.rs +4 -0
  53. package/workspaces/executables/intenal_executable.rs +9 -0
  54. package/workspaces/executables/internal_executable_definition.rs +8 -0
  55. package/workspaces/executables/mod.rs +3 -0
  56. package/workspaces/executor/executor.rs +62 -0
  57. package/workspaces/executor/mod.rs +1 -0
  58. package/workspaces/external_commands/external_commands.rs +125 -0
  59. package/workspaces/external_commands/mod.rs +1 -0
  60. package/workspaces/internal_commands/command_template.ts +24 -0
  61. package/workspaces/internal_commands/help.rs +146 -0
  62. package/workspaces/internal_commands/internal_registry.rs +59 -0
  63. package/workspaces/internal_commands/list_commands.rs +106 -0
  64. package/workspaces/internal_commands/list_owners.rs +74 -0
  65. package/workspaces/internal_commands/locate_command.rs +78 -0
  66. package/workspaces/internal_commands/mod.rs +10 -0
  67. package/workspaces/internal_commands/onboarder.rs +66 -0
  68. package/workspaces/internal_commands/register_command.rs +117 -0
  69. package/workspaces/internal_commands/search_commands.rs +189 -0
  70. package/workspaces/internal_commands/typescript_command.rs +63 -0
  71. package/workspaces/internal_commands/upgrade_repokit.rs +76 -0
  72. package/workspaces/logger/logger.rs +98 -0
  73. package/workspaces/logger/mod.rs +1 -0
  74. package/workspaces/main.rs +21 -0
  75. package/workspaces/repokit/interfaces.rs +41 -0
  76. package/workspaces/repokit/mod.rs +2 -0
  77. package/workspaces/repokit/repokit.rs +141 -0
  78. package/workspaces/validations/command_validations.rs +140 -0
  79. package/workspaces/validations/mod.rs +1 -0
package/README.md ADDED
@@ -0,0 +1,204 @@
1
+ <img src="media/repokit.webp" alt="Alt text" width="150px" />
2
+
3
+ # repokit
4
+
5
+ A knowledgebase for your repository - wrapped in a CLI.
6
+
7
+ Repokit is designed for large teams in complex codebases to publish self-documenting commands, API's, and workflows to a central CLI.
8
+
9
+ The Repokit CLI exists as a living source of documentation and knowledge - growing alongside your team.
10
+
11
+ ## Getting Started
12
+
13
+ ### Installation
14
+
15
+ If you do not have node.js setup in your repository, you'll first want to install node.js.
16
+
17
+ [NVM is a populat posix compliant installer](https://github.com/nvm-sh/nvm)
18
+
19
+ Once installed, you can run the following in the root of your repository
20
+
21
+ ```bash
22
+ npm init
23
+ ```
24
+
25
+ If you don't have `typescript` already setup in your repository, you can run:
26
+
27
+ ```bash
28
+ npm i -D typescript && tsc --init
29
+ ```
30
+
31
+ Next, install repokit:
32
+
33
+ ```bash
34
+ npm i -D @repokit/core
35
+ # or
36
+ yarn add -D @repokit/core
37
+ # or
38
+ pnpm add -D @repokit/core
39
+ ```
40
+
41
+ Repokit will automatically create a config file named `repokit.ts` for you upon installing. Fill out this file with your desired settings.
42
+
43
+ Here's an example of what Repokit's internal config looks like:
44
+
45
+ ```typescript
46
+ import { RepoKitConfig } from "@repokit/core";
47
+
48
+ export const RepoKit = new RepoKitConfig({
49
+ project: "Repokit",
50
+ commands: {
51
+ "build:rust": {
52
+ command: "cargo build --release",
53
+ description: "Build CLI in production mode",
54
+ },
55
+ "install:rust": {
56
+ command: "cargo install --path .",
57
+ description: "Installs the production CLI and adds it to your path",
58
+ },
59
+ "lint:ts": {
60
+ command:
61
+ "yarn oxlint --type-aware --type-check --report-unused-disable-directives --fix && yarn oxfmt",
62
+ description: "Lints typescript files using oxc",
63
+ },
64
+ "build:ts": {
65
+ command: "yarn ts-packager -e src",
66
+ description: "Builds the typescript package",
67
+ },
68
+ },
69
+ });
70
+ ```
71
+
72
+ To verify your configuration, run
73
+
74
+ ```bash
75
+ repokit
76
+ ```
77
+
78
+ The CLI will list out its internal commands as well as any commands you registered in your config file.
79
+
80
+ Next run:
81
+
82
+ ```bash
83
+ repokit onboard
84
+ ```
85
+
86
+ ### Building Your CLI
87
+
88
+ To begin building your CLI, run:
89
+
90
+ ```bash
91
+ repokit register ./path/to/your/feature
92
+ ```
93
+
94
+ This command generates a tool definition for your feature that you can fill out using your tool's API's. When complete, save the file and run:
95
+
96
+ ```bash
97
+ repokit <your-tool-name>
98
+ ```
99
+
100
+ The CLI will list out your new tool's API's. To invoke any of them, run:
101
+
102
+ ```bash
103
+ repokit <your-tool-name> <your-command-name>
104
+ ```
105
+
106
+ ### Reasoning about your toolchain
107
+
108
+ As your toolchain grows it's possible to find yourself with hundreds, if not thousands of registered commands.
109
+
110
+ To make reasoning about your commands easier, there are a few internal commands worth getting to know
111
+
112
+ #### `repokit search`
113
+
114
+ `repokit search` is a blanket search over all command definitions. Using it you can search for commands by name, owner, definition, location, or even the tools that it invokes.
115
+
116
+ For example, let's say you wanted to list all commands that invoke `cargo`, you could run
117
+
118
+ ```bash
119
+ repokit search cargo
120
+ ```
121
+
122
+ If you wanted to search for all commands owned by an individual or team you could run
123
+
124
+ ```bash
125
+ repokit search <person or team name>
126
+ ```
127
+
128
+ If you wanted to search for commands under a given path you could run
129
+
130
+ ```bash
131
+ repokit search path/within/your/codebase
132
+ ```
133
+
134
+ You can query for just about anything you can imagine
135
+
136
+ #### `repokit locate`
137
+
138
+ Code changes can sometimes require updating command definitions. Repokit can easily locate any command's definition by name:
139
+
140
+ ```bash
141
+ repokit locate <your-tool-name>
142
+ ```
143
+
144
+ #### `repokit owners`
145
+
146
+ If your team makes use of the `owners` attribute when defining your commands, you can easily list all commands owned by an individual or team
147
+
148
+ ```bash
149
+ repokit list <owner>
150
+ ```
151
+
152
+ `repokit list` can also accept `internal | registered | root` as an argument.
153
+
154
+ `internal` will cause repokit to list out all of its internal commands
155
+
156
+ `registered` will cause repokit to list out all of the commands your team has defined around your codebase
157
+
158
+ `root` will cause repokit to list out all commands in your `repokit.ts` config
159
+
160
+ ### Best Practices for Registering Commands
161
+
162
+ First and most simply - use verbose descriptions. Document flags, positionals, and environment variables required to invoke your tool.
163
+
164
+ If your tool requires arguments, abstract common combinations of arguments into their own sub-commands. For example, instead of a single `build` command requiring flags to configure it, create sub commands that abstract commonly used combinations of parameters:
165
+
166
+ ```typescript
167
+ import { RepoKitCommand } from "@repokit/core";
168
+
169
+ export const Commands = new RepoKitCommand({
170
+ // ... command definition
171
+ commands: {
172
+ "build:local": {
173
+ command: "bazel build --env development",
174
+ description: "Builds in development mode",
175
+ },
176
+ "build:production": {
177
+ command: "bazel build --progress --stats --env production",
178
+ description: "Builds in production mode",
179
+ },
180
+ },
181
+ });
182
+ ```
183
+
184
+ When possible, prefer flags and positionals over environment variables. Often times your argv parsers will provide some out-of-the-box validations for free that environment variables simply don't get.
185
+
186
+ #### Working Directories
187
+
188
+ The commands you register onto the repokit toolchain will always be invoked using the working directory of the command's definition.
189
+
190
+ If your command needs to reason about the file system, keep this in mind.
191
+
192
+ ## Motivation
193
+
194
+ I worked in a codebase at Google that used just about every programming language in existence. Each team had their own methodology for exposing commands, scripts, and API's for their team's day-to-day development needs.
195
+
196
+ Some teams used shell scripts, some used a tool called `bazel`, and some relied on good old `python ./path/to/my-script.py` or something similar.
197
+
198
+ For engineers new and old to onboard to new features, they were often left stuck combing through these undocumented scripts and tools - tracking down environment variables, positionals, and flags to get necessary commands to succeed.
199
+
200
+ Most of the time landing them in GChat asking for help.
201
+
202
+ During my time there, I never met an engineer with a fully functioning local environment.
203
+
204
+ It was there that I designed an early version **repokit.**
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.CommandParser = void 0;
13
+ const node_util_1 = require("node:util");
14
+ const node_path_1 = require("node:path");
15
+ const promises_1 = require("node:fs/promises");
16
+ const node_fs_1 = require("node:fs");
17
+ const TaskPooler_1 = require("./TaskPooler");
18
+ const RepoKitCommand_1 = require("./RepoKitCommand");
19
+ class CommandParser {
20
+ static parse() {
21
+ return __awaiter(this, void 0, void 0, function* () {
22
+ const { paths, root } = this.parsePaths();
23
+ if (!root || !(0, node_fs_1.existsSync)(root) || !(yield (0, promises_1.stat)(root)).isDirectory()) {
24
+ return console.log(JSON.stringify([]));
25
+ }
26
+ const pathList = paths.split(",").filter(Boolean);
27
+ const pool = new TaskPooler_1.TaskPooler();
28
+ const results = yield Promise.all(pathList.map(path => pool.enqueue(() => this.parseCommand((0, node_path_1.join)(root, path)))));
29
+ console.log(JSON.stringify(results.flat()));
30
+ });
31
+ }
32
+ static parseCommand(path) {
33
+ return __awaiter(this, void 0, void 0, function* () {
34
+ const commands = [];
35
+ const declaredExports = yield Promise.resolve(`${path}`).then(s => require(s));
36
+ for (const key in declaredExports) {
37
+ if (declaredExports[key] instanceof RepoKitCommand_1.RepoKitCommand) {
38
+ commands.push(Object.assign(Object.assign({}, declaredExports[key].toJSON()), { location: path }));
39
+ }
40
+ }
41
+ return commands;
42
+ });
43
+ }
44
+ static parsePaths() {
45
+ try {
46
+ return (0, node_util_1.parseArgs)({
47
+ options: {
48
+ paths: {
49
+ default: "",
50
+ multiple: false,
51
+ short: "p",
52
+ type: "string",
53
+ },
54
+ root: {
55
+ default: "",
56
+ multiple: false,
57
+ short: "r",
58
+ type: "string",
59
+ },
60
+ },
61
+ }).values;
62
+ }
63
+ catch (_a) {
64
+ return { paths: "", root: "" };
65
+ }
66
+ }
67
+ }
68
+ exports.CommandParser = CommandParser;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ConfigurationParser = void 0;
13
+ const node_util_1 = require("node:util");
14
+ const node_path_1 = require("node:path");
15
+ const node_fs_1 = require("node:fs");
16
+ const RepoKitConfig_1 = require("./RepoKitConfig");
17
+ class ConfigurationParser {
18
+ static parse() {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ const root = this.parseRoot();
21
+ const path = (0, node_path_1.join)(root, "repokit.ts");
22
+ if (!(0, node_fs_1.existsSync)(path)) {
23
+ return;
24
+ }
25
+ const config = yield Promise.resolve(`${path}`).then(s => require(s));
26
+ for (const key in config) {
27
+ if (config[key] instanceof RepoKitConfig_1.RepoKitConfig) {
28
+ return console.log(JSON.stringify(config[key]));
29
+ }
30
+ }
31
+ });
32
+ }
33
+ static parseRoot() {
34
+ return (0, node_util_1.parseArgs)({
35
+ options: {
36
+ root: {
37
+ default: "",
38
+ multiple: false,
39
+ short: "r",
40
+ type: "string",
41
+ },
42
+ },
43
+ }).values.root;
44
+ }
45
+ }
46
+ exports.ConfigurationParser = ConfigurationParser;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RepoKitCommand = void 0;
4
+ class RepoKitCommand {
5
+ constructor({ name, description, owner = "", commands = {}, }) {
6
+ this.name = name;
7
+ this.owner = owner;
8
+ this.commands = commands;
9
+ this.description = description;
10
+ }
11
+ toJSON() {
12
+ const { name, owner, commands, description } = this;
13
+ return { name, owner, commands, description };
14
+ }
15
+ }
16
+ exports.RepoKitCommand = RepoKitCommand;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RepoKitConfig = void 0;
4
+ class RepoKitConfig {
5
+ constructor({ project, commands = {} }) {
6
+ this.project = project;
7
+ this.commands = commands;
8
+ }
9
+ }
10
+ exports.RepoKitConfig = RepoKitConfig;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TaskPooler = void 0;
13
+ const event_emitter_1 = require("@figliolia/event-emitter");
14
+ class TaskPooler {
15
+ constructor(maxSize = 10) {
16
+ this.maxSize = maxSize;
17
+ this.IDs = new event_emitter_1.AutoIncrementingID();
18
+ this.runningTasks = new Map();
19
+ }
20
+ enqueue(task) {
21
+ return new Promise(resolve => {
22
+ if (this.runningTasks.size < 10) {
23
+ return resolve(this.indexRunningTask(task));
24
+ }
25
+ resolve(this.indexBehindNextOpening(task));
26
+ });
27
+ }
28
+ indexRunningTask(task) {
29
+ const ID = this.IDs.get();
30
+ const promise = task();
31
+ this.runningTasks.set(ID, promise);
32
+ void promise.finally(() => this.runningTasks.delete(ID));
33
+ return promise;
34
+ }
35
+ indexBehindNextOpening(task) {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ yield Promise.race(this.runningTasks.values());
38
+ return this.indexRunningTask(task);
39
+ });
40
+ }
41
+ }
42
+ exports.TaskPooler = TaskPooler;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const CommandParser_1 = require("../CommandParser");
13
+ void (() => __awaiter(void 0, void 0, void 0, function* () {
14
+ yield CommandParser_1.CommandParser.parse();
15
+ }))();
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const ConfigurationParser_1 = require("../ConfigurationParser");
13
+ void (() => __awaiter(void 0, void 0, void 0, function* () {
14
+ yield ConfigurationParser_1.ConfigurationParser.parse();
15
+ }))();
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./RepoKitConfig"), exports);
18
+ __exportStar(require("./RepoKitCommand"), exports);
19
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,51 @@
1
+ import { parseArgs } from "node:util";
2
+ import { join } from "node:path";
3
+ import { stat } from "node:fs/promises";
4
+ import { existsSync } from "node:fs";
5
+ import { TaskPooler } from "./TaskPooler.js";
6
+ import { RepoKitCommand } from "./RepoKitCommand.js";
7
+ export class CommandParser {
8
+ static async parse() {
9
+ const { paths, root } = this.parsePaths();
10
+ if (!root || !existsSync(root) || !(await stat(root)).isDirectory()) {
11
+ return console.log(JSON.stringify([]));
12
+ }
13
+ const pathList = paths.split(",").filter(Boolean);
14
+ const pool = new TaskPooler();
15
+ const results = await Promise.all(pathList.map(path => pool.enqueue(() => this.parseCommand(join(root, path)))));
16
+ console.log(JSON.stringify(results.flat()));
17
+ }
18
+ static async parseCommand(path) {
19
+ const commands = [];
20
+ const declaredExports = await import(path);
21
+ for (const key in declaredExports) {
22
+ if (declaredExports[key] instanceof RepoKitCommand) {
23
+ commands.push({ ...declaredExports[key].toJSON(), location: path });
24
+ }
25
+ }
26
+ return commands;
27
+ }
28
+ static parsePaths() {
29
+ try {
30
+ return parseArgs({
31
+ options: {
32
+ paths: {
33
+ default: "",
34
+ multiple: false,
35
+ short: "p",
36
+ type: "string",
37
+ },
38
+ root: {
39
+ default: "",
40
+ multiple: false,
41
+ short: "r",
42
+ type: "string",
43
+ },
44
+ },
45
+ }).values;
46
+ }
47
+ catch {
48
+ return { paths: "", root: "" };
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,31 @@
1
+ import { parseArgs } from "node:util";
2
+ import { join } from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ import { RepoKitConfig } from "./RepoKitConfig.js";
5
+ export class ConfigurationParser {
6
+ static async parse() {
7
+ const root = this.parseRoot();
8
+ const path = join(root, "repokit.ts");
9
+ if (!existsSync(path)) {
10
+ return;
11
+ }
12
+ const config = await import(path);
13
+ for (const key in config) {
14
+ if (config[key] instanceof RepoKitConfig) {
15
+ return console.log(JSON.stringify(config[key]));
16
+ }
17
+ }
18
+ }
19
+ static parseRoot() {
20
+ return parseArgs({
21
+ options: {
22
+ root: {
23
+ default: "",
24
+ multiple: false,
25
+ short: "r",
26
+ type: "string",
27
+ },
28
+ },
29
+ }).values.root;
30
+ }
31
+ }
@@ -0,0 +1,16 @@
1
+ export class RepoKitCommand {
2
+ name;
3
+ owner;
4
+ description;
5
+ commands;
6
+ constructor({ name, description, owner = "", commands = {}, }) {
7
+ this.name = name;
8
+ this.owner = owner;
9
+ this.commands = commands;
10
+ this.description = description;
11
+ }
12
+ toJSON() {
13
+ const { name, owner, commands, description } = this;
14
+ return { name, owner, commands, description };
15
+ }
16
+ }
@@ -0,0 +1,8 @@
1
+ export class RepoKitConfig {
2
+ project;
3
+ commands;
4
+ constructor({ project, commands = {} }) {
5
+ this.project = project;
6
+ this.commands = commands;
7
+ }
8
+ }
@@ -0,0 +1,28 @@
1
+ import { AutoIncrementingID } from "@figliolia/event-emitter";
2
+ export class TaskPooler {
3
+ maxSize;
4
+ IDs = new AutoIncrementingID();
5
+ runningTasks = new Map();
6
+ constructor(maxSize = 10) {
7
+ this.maxSize = maxSize;
8
+ }
9
+ enqueue(task) {
10
+ return new Promise(resolve => {
11
+ if (this.runningTasks.size < 10) {
12
+ return resolve(this.indexRunningTask(task));
13
+ }
14
+ resolve(this.indexBehindNextOpening(task));
15
+ });
16
+ }
17
+ indexRunningTask(task) {
18
+ const ID = this.IDs.get();
19
+ const promise = task();
20
+ this.runningTasks.set(ID, promise);
21
+ void promise.finally(() => this.runningTasks.delete(ID));
22
+ return promise;
23
+ }
24
+ async indexBehindNextOpening(task) {
25
+ await Promise.race(this.runningTasks.values());
26
+ return this.indexRunningTask(task);
27
+ }
28
+ }
@@ -0,0 +1,4 @@
1
+ import { CommandParser } from "../CommandParser.js";
2
+ void (async () => {
3
+ await CommandParser.parse();
4
+ })();
@@ -0,0 +1,4 @@
1
+ import { ConfigurationParser } from "../ConfigurationParser.js";
2
+ void (async () => {
3
+ await ConfigurationParser.parse();
4
+ })();
@@ -0,0 +1,3 @@
1
+ export * from "./RepoKitConfig.js";
2
+ export * from "./RepoKitCommand.js";
3
+ export * from "./types.js";
@@ -0,0 +1,4 @@
1
+ {
2
+ "type": "module",
3
+ "exports": "./index.js"
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ export declare class CommandParser {
2
+ static parse(): Promise<void>;
3
+ private static parseCommand;
4
+ private static parsePaths;
5
+ }
@@ -0,0 +1,4 @@
1
+ export declare class ConfigurationParser {
2
+ static parse(): Promise<void>;
3
+ private static parseRoot;
4
+ }
@@ -0,0 +1,14 @@
1
+ import type { ICommand, IRepoKitCommand } from "./types";
2
+ export declare class RepoKitCommand {
3
+ name: string;
4
+ owner: string;
5
+ description: string;
6
+ commands: Record<string, ICommand>;
7
+ constructor({ name, description, owner, commands, }: IRepoKitCommand);
8
+ toJSON(): {
9
+ name: string;
10
+ owner: string;
11
+ commands: Record<string, ICommand>;
12
+ description: string;
13
+ };
14
+ }
@@ -0,0 +1,6 @@
1
+ import type { ICommand, IRepoKitConfig } from "./types";
2
+ export declare class RepoKitConfig implements Required<IRepoKitConfig> {
3
+ project: string;
4
+ commands: Record<string, ICommand>;
5
+ constructor({ project, commands }: IRepoKitConfig);
6
+ }
@@ -0,0 +1,10 @@
1
+ import type { AsyncTask } from "./types";
2
+ export declare class TaskPooler<T> {
3
+ maxSize: number;
4
+ private readonly IDs;
5
+ private readonly runningTasks;
6
+ constructor(maxSize?: number);
7
+ enqueue(task: AsyncTask<T>): Promise<T>;
8
+ private indexRunningTask;
9
+ private indexBehindNextOpening;
10
+ }
@@ -0,0 +1 @@
1
+ export {};