@decohere/devkit 0.0.1-dev.125 → 0.0.1-dev.5

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 (121) hide show
  1. package/package.json +3 -12
  2. package/src/cli/action.ts +77 -0
  3. package/src/cli/command.ts +173 -0
  4. package/src/cli/index.ts +30 -0
  5. package/src/cli/module.type.ts +48 -0
  6. package/src/cli/options.ts +81 -0
  7. package/src/diagnostics/trace.ts +92 -0
  8. package/src/interop/docker/docker.ts +57 -0
  9. package/src/interop/errors/interop.error.ts +1 -0
  10. package/src/interop/git/git.ts +98 -0
  11. package/{types/src/interop/git/git.types.d.ts → src/interop/git/git.types.ts} +9 -6
  12. package/{es6/src/interop/index.js → src/interop/index.ts} +1 -0
  13. package/src/interop/node/node.ts +38 -0
  14. package/src/interop/npm/npm.ts +168 -0
  15. package/src/interop/npm/npm.types.ts +28 -0
  16. package/src/interop/pnpm/pnpm.ts +22 -0
  17. package/{types/src/interop/pnpm/pnpm.types.d.ts → src/interop/pnpm/pnpm.types.ts} +1 -1
  18. package/src/interop/shell/shell.argv.ts +31 -0
  19. package/src/interop/shell/shell.ts +100 -0
  20. package/src/interop/shell/shell.types.ts +9 -0
  21. package/src/interop/types/detect-package-manager.ts +45 -0
  22. package/src/interop/types/package-manager.ts +7 -0
  23. package/src/interop/yarn/yarn.ts +28 -0
  24. package/{types/src/interop/yarn/yarn.types.d.ts → src/interop/yarn/yarn.types.ts} +1 -1
  25. package/src/io/find-directories.ts +13 -0
  26. package/src/io/find-file.ts +37 -0
  27. package/src/io/watch.ts +290 -0
  28. package/tests/cli/command.model.spec.ts +91 -0
  29. package/tests/diagnostics/trace.spec.ts +319 -0
  30. package/tests/interop/detect-package-manager.spec.ts +70 -0
  31. package/tests/interop/npm.spec.ts +129 -0
  32. package/tests/interop/pnpm.spec.ts +30 -0
  33. package/tests/interop/shell.spec.ts +47 -0
  34. package/tests/interop/yarn.spec.ts +18 -0
  35. package/tests/io/find-file.spec.ts +188 -0
  36. package/tests/io/watch.real-fs.spec.ts +76 -0
  37. package/tests/io/watch.spec.ts +291 -0
  38. package/LICENSE +0 -15
  39. package/commonjs/src/cli/action.js +0 -65
  40. package/commonjs/src/cli/command.js +0 -127
  41. package/commonjs/src/cli/index.js +0 -61
  42. package/commonjs/src/cli/module.type.js +0 -18
  43. package/commonjs/src/cli/options.js +0 -115
  44. package/commonjs/src/diagnostics/index.js +0 -22
  45. package/commonjs/src/diagnostics/trace.js +0 -79
  46. package/commonjs/src/interop/docker/docker.js +0 -54
  47. package/commonjs/src/interop/errors/interop.error.js +0 -12
  48. package/commonjs/src/interop/git/git.js +0 -134
  49. package/commonjs/src/interop/git/git.types.js +0 -4
  50. package/commonjs/src/interop/index.js +0 -97
  51. package/commonjs/src/interop/node/index.js +0 -18
  52. package/commonjs/src/interop/node/node.js +0 -45
  53. package/commonjs/src/interop/npm/npm.js +0 -231
  54. package/commonjs/src/interop/npm/npm.types.js +0 -16
  55. package/commonjs/src/interop/pnpm/pnpm.js +0 -34
  56. package/commonjs/src/interop/pnpm/pnpm.types.js +0 -4
  57. package/commonjs/src/interop/shell/shell.argv.js +0 -45
  58. package/commonjs/src/interop/shell/shell.js +0 -105
  59. package/commonjs/src/interop/shell/shell.types.js +0 -4
  60. package/commonjs/src/interop/types/detect-package-manager.js +0 -71
  61. package/commonjs/src/interop/types/package-manager.js +0 -4
  62. package/commonjs/src/interop/yarn/yarn.js +0 -34
  63. package/commonjs/src/interop/yarn/yarn.types.js +0 -4
  64. package/commonjs/src/io/find-directories.js +0 -21
  65. package/commonjs/src/io/find-file.js +0 -45
  66. package/commonjs/src/io/index.js +0 -20
  67. package/commonjs/src/io/watch.js +0 -261
  68. package/es6/src/cli/action.js +0 -47
  69. package/es6/src/cli/command.js +0 -106
  70. package/es6/src/cli/index.js +0 -4
  71. package/es6/src/cli/module.type.js +0 -11
  72. package/es6/src/cli/options.js +0 -76
  73. package/es6/src/diagnostics/trace.js +0 -61
  74. package/es6/src/interop/docker/docker.js +0 -44
  75. package/es6/src/interop/errors/interop.error.js +0 -2
  76. package/es6/src/interop/git/git.js +0 -124
  77. package/es6/src/interop/git/git.types.js +0 -1
  78. package/es6/src/interop/node/node.js +0 -35
  79. package/es6/src/interop/npm/npm.js +0 -180
  80. package/es6/src/interop/npm/npm.types.js +0 -6
  81. package/es6/src/interop/pnpm/pnpm.js +0 -24
  82. package/es6/src/interop/pnpm/pnpm.types.js +0 -1
  83. package/es6/src/interop/shell/shell.argv.js +0 -35
  84. package/es6/src/interop/shell/shell.js +0 -95
  85. package/es6/src/interop/shell/shell.types.js +0 -1
  86. package/es6/src/interop/types/detect-package-manager.js +0 -50
  87. package/es6/src/interop/types/package-manager.js +0 -1
  88. package/es6/src/interop/yarn/yarn.js +0 -24
  89. package/es6/src/interop/yarn/yarn.types.js +0 -1
  90. package/es6/src/io/find-directories.js +0 -11
  91. package/es6/src/io/find-file.js +0 -27
  92. package/es6/src/io/watch.js +0 -238
  93. package/types/src/cli/action.d.ts +0 -36
  94. package/types/src/cli/command.d.ts +0 -34
  95. package/types/src/cli/index.d.ts +0 -7
  96. package/types/src/cli/module.type.d.ts +0 -36
  97. package/types/src/cli/options.d.ts +0 -28
  98. package/types/src/diagnostics/index.d.ts +0 -1
  99. package/types/src/diagnostics/trace.d.ts +0 -10
  100. package/types/src/interop/docker/docker.d.ts +0 -19
  101. package/types/src/interop/errors/interop.error.d.ts +0 -2
  102. package/types/src/interop/git/git.d.ts +0 -16
  103. package/types/src/interop/index.d.ts +0 -14
  104. package/types/src/interop/node/index.d.ts +0 -1
  105. package/types/src/interop/node/node.d.ts +0 -5
  106. package/types/src/interop/npm/npm.d.ts +0 -29
  107. package/types/src/interop/npm/npm.types.d.ts +0 -24
  108. package/types/src/interop/pnpm/pnpm.d.ts +0 -9
  109. package/types/src/interop/shell/shell.argv.d.ts +0 -5
  110. package/types/src/interop/shell/shell.d.ts +0 -6
  111. package/types/src/interop/shell/shell.types.d.ts +0 -9
  112. package/types/src/interop/types/detect-package-manager.d.ts +0 -4
  113. package/types/src/interop/types/package-manager.d.ts +0 -10
  114. package/types/src/interop/yarn/yarn.d.ts +0 -9
  115. package/types/src/io/find-directories.d.ts +0 -2
  116. package/types/src/io/find-file.d.ts +0 -9
  117. package/types/src/io/index.d.ts +0 -3
  118. package/types/src/io/watch.d.ts +0 -16
  119. /package/{es6/src/diagnostics/index.js → src/diagnostics/index.ts} +0 -0
  120. /package/{es6/src/interop/node/index.js → src/interop/node/index.ts} +0 -0
  121. /package/{es6/src/io/index.js → src/io/index.ts} +0 -0
@@ -1,261 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- function _export(target, all) {
6
- for(var name in all)Object.defineProperty(target, name, {
7
- enumerable: true,
8
- get: Object.getOwnPropertyDescriptor(all, name).get
9
- });
10
- }
11
- _export(exports, {
12
- get WatchEvents () {
13
- return WatchEvents;
14
- },
15
- get watch () {
16
- return watch;
17
- }
18
- });
19
- const _nodefs = require("node:fs");
20
- const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
21
- const _picomatch = /*#__PURE__*/ _interop_require_default(require("picomatch"));
22
- const _io = require("@decohere/core/io");
23
- function _interop_require_default(obj) {
24
- return obj && obj.__esModule ? obj : {
25
- default: obj
26
- };
27
- }
28
- const WatchEvents = {
29
- add: "add",
30
- change: "change",
31
- addDir: "addDir",
32
- unlink: "unlink",
33
- unlinkDir: "unlinkDir"
34
- };
35
- function watch(directory, options = {
36
- cwd: "."
37
- }) {
38
- const { include, ignore, ignoreInitial = false } = options;
39
- const changeCoalesceMs = 25;
40
- const handlers = {
41
- ready: new Set(),
42
- add: new Set(),
43
- change: new Set(),
44
- addDir: new Set(),
45
- unlink: new Set(),
46
- unlinkDir: new Set()
47
- };
48
- let resolveReady;
49
- const ready = new Promise((resolve)=>{
50
- resolveReady = resolve;
51
- });
52
- const snapshot = buildSnapshot(directory);
53
- const isFiltered = (relativePath)=>{
54
- const posixRelativePath = toPosixPath(relativePath);
55
- const posixFullPath = toPosixPath(_nodepath.default.resolve(directory, relativePath));
56
- const matchesEitherPath = (patterns)=>_picomatch.default.isMatch(posixRelativePath, patterns) || _picomatch.default.isMatch(posixFullPath, patterns);
57
- if (include && include.length > 0) {
58
- return !matchesEitherPath(include);
59
- }
60
- if (ignore && ignore.length > 0) {
61
- return matchesEitherPath(ignore);
62
- }
63
- return false;
64
- };
65
- const emit = (event, relativePath)=>{
66
- if (isFiltered(relativePath)) {
67
- return;
68
- }
69
- const fullPath = _nodepath.default.resolve(directory, relativePath);
70
- for (const handler of handlers[event]){
71
- handler(new _io.FileEx({
72
- fullPath,
73
- relativePath
74
- }));
75
- }
76
- };
77
- const pendingChangeTimers = new Map();
78
- const emitChange = (relativePath)=>{
79
- const existing = pendingChangeTimers.get(relativePath);
80
- if (existing != null) {
81
- clearTimeout(existing);
82
- }
83
- const timer = setTimeout(()=>{
84
- pendingChangeTimers.delete(relativePath);
85
- emit("change", relativePath);
86
- }, changeCoalesceMs);
87
- pendingChangeTimers.set(relativePath, timer);
88
- };
89
- const emitReady = ()=>{
90
- for (const handler of handlers.ready){
91
- handler();
92
- }
93
- };
94
- queueMicrotask(()=>{
95
- if (!ignoreInitial) {
96
- for (const [relativePath, entry] of snapshot){
97
- emit(entry.isDirectory ? "addDir" : "add", relativePath);
98
- }
99
- }
100
- emitReady();
101
- resolveReady?.();
102
- resolveReady = undefined;
103
- });
104
- // One non-recursive watcher per directory. Node's recursive mode on Linux tracks files
105
- // individually and goes silent for a file once an editor replaces it with an atomic save
106
- // (write temp file, rename over the original); directory watchers keep seeing it (#505).
107
- const directoryWatchers = new Map();
108
- const watchDirectory = (relativeDir)=>{
109
- if (directoryWatchers.has(relativeDir)) {
110
- return;
111
- }
112
- try {
113
- const watcher = (0, _nodefs.watch)(_nodepath.default.resolve(directory, relativeDir), (_eventType, fileName)=>{
114
- if (fileName != null) {
115
- onFsEvent(_nodepath.default.posix.join(relativeDir, toPosixPath(fileName.toString())));
116
- }
117
- });
118
- watcher.on("error", ()=>unwatchDirectory(relativeDir));
119
- directoryWatchers.set(relativeDir, watcher);
120
- } catch {
121
- // The directory vanished before we could watch it; its unlink event covers it.
122
- }
123
- };
124
- const unwatchDirectory = (relativeDir)=>{
125
- for (const [watchedDir, watcher] of directoryWatchers){
126
- if (watchedDir === relativeDir || watchedDir.startsWith(`${relativeDir}/`)) {
127
- watcher.close();
128
- directoryWatchers.delete(watchedDir);
129
- }
130
- }
131
- };
132
- /** A directory appeared: watch it, and report what was created in it before we were watching. */ const addDirectory = (relativeDir)=>{
133
- watchDirectory(relativeDir);
134
- const found = new Map();
135
- try {
136
- collectSnapshotEntries(directory, _nodepath.default.resolve(directory, relativeDir), found);
137
- } catch {
138
- return;
139
- }
140
- for (const [relativePath, entry] of found){
141
- if (snapshot.has(relativePath)) continue;
142
- snapshot.set(relativePath, entry);
143
- if (entry.isDirectory) watchDirectory(relativePath);
144
- emit(entry.isDirectory ? "addDir" : "add", relativePath);
145
- }
146
- };
147
- /** A directory went away: stop watching it and forget everything that was under it. */ const removeDirectoryContents = (relativeDir)=>{
148
- unwatchDirectory(relativeDir);
149
- // oxlint-disable-next-line unicorn/no-useless-spread -- iterate a copy: the loop deletes from the snapshot
150
- for (const [relativePath, entry] of [
151
- ...snapshot
152
- ]){
153
- if (relativePath.startsWith(`${relativeDir}/`)) {
154
- snapshot.delete(relativePath);
155
- emit(entry.isDirectory ? "unlinkDir" : "unlink", relativePath);
156
- }
157
- }
158
- };
159
- const onFsEvent = (eventPath)=>{
160
- const relativePath = eventPath === "." ? "" : eventPath;
161
- if (relativePath.length === 0) {
162
- return;
163
- }
164
- const fullPath = _nodepath.default.resolve(directory, relativePath);
165
- const previous = snapshot.get(relativePath);
166
- try {
167
- const stat = (0, _nodefs.statSync)(fullPath);
168
- const current = toSnapshotEntry(stat);
169
- snapshot.set(relativePath, current);
170
- if (previous == null) {
171
- emit(current.isDirectory ? "addDir" : "add", relativePath);
172
- if (current.isDirectory) addDirectory(relativePath);
173
- return;
174
- }
175
- if (previous.isDirectory !== current.isDirectory) {
176
- if (previous.isDirectory) removeDirectoryContents(relativePath);
177
- emit(previous.isDirectory ? "unlinkDir" : "unlink", relativePath);
178
- emit(current.isDirectory ? "addDir" : "add", relativePath);
179
- return;
180
- }
181
- if (!current.isDirectory && previous.fingerprint !== current.fingerprint) {
182
- emitChange(relativePath);
183
- }
184
- } catch {
185
- if (previous == null) {
186
- return;
187
- }
188
- snapshot.delete(relativePath);
189
- const pending = pendingChangeTimers.get(relativePath);
190
- if (pending != null) {
191
- clearTimeout(pending);
192
- pendingChangeTimers.delete(relativePath);
193
- }
194
- emit(previous.isDirectory ? "unlinkDir" : "unlink", relativePath);
195
- }
196
- };
197
- watchDirectory("");
198
- for (const [relativePath, entry] of snapshot){
199
- if (entry.isDirectory) watchDirectory(relativePath);
200
- }
201
- const watchApi = {
202
- ready,
203
- on (event, handler) {
204
- if (event === "ready") {
205
- handlers.ready.add(handler);
206
- return watchApi;
207
- }
208
- handlers[event].add(handler);
209
- return watchApi;
210
- },
211
- close: ()=>{
212
- for (const pending of pendingChangeTimers.values()){
213
- clearTimeout(pending);
214
- }
215
- pendingChangeTimers.clear();
216
- for (const watcher of directoryWatchers.values()){
217
- watcher.close();
218
- }
219
- directoryWatchers.clear();
220
- }
221
- };
222
- return watchApi;
223
- }
224
- function buildSnapshot(directoryPath) {
225
- const snapshot = new Map();
226
- collectSnapshotEntries(directoryPath, directoryPath, snapshot);
227
- return snapshot;
228
- }
229
- /** Never source, and huge: watching every folder inside them could exhaust OS watch limits. */ const UNWATCHED_DIRECTORIES = new Set([
230
- "node_modules",
231
- ".git"
232
- ]);
233
- function collectSnapshotEntries(rootDirectoryPath, currentDirectoryPath, snapshot) {
234
- for (const entry of (0, _nodefs.readdirSync)(currentDirectoryPath, {
235
- encoding: "utf8"
236
- })){
237
- const name = typeof entry === "string" ? entry : String(entry);
238
- const fullPath = _nodepath.default.join(currentDirectoryPath, name);
239
- if (UNWATCHED_DIRECTORIES.has(name)) {
240
- continue;
241
- }
242
- const relativePath = toPosixPath(_nodepath.default.relative(rootDirectoryPath, fullPath));
243
- const stat = (0, _nodefs.statSync)(fullPath);
244
- const snapshotEntry = toSnapshotEntry(stat);
245
- snapshot.set(relativePath, snapshotEntry);
246
- if (!snapshotEntry.isDirectory) {
247
- continue;
248
- }
249
- collectSnapshotEntries(rootDirectoryPath, fullPath, snapshot);
250
- }
251
- }
252
- function toSnapshotEntry(stat) {
253
- const isDirectory = stat.isDirectory();
254
- return {
255
- isDirectory,
256
- fingerprint: isDirectory ? `dir:${stat.mtimeMs}` : `file:${stat.mtimeMs}:${stat.size}`
257
- };
258
- }
259
- function toPosixPath(filePath) {
260
- return filePath.split(_nodepath.default.sep).join(_nodepath.default.posix.sep);
261
- }
@@ -1,47 +0,0 @@
1
- export const ActionMap = Symbol("ActionMap");
2
- export class ActionResponse {
3
- static Success(name, data) {
4
- return new ActionResponse({
5
- name,
6
- data,
7
- succeeded: true
8
- });
9
- }
10
- static Skipped(name) {
11
- return new ActionResponse({
12
- name,
13
- skipped: true
14
- });
15
- }
16
- static Blocking(name) {
17
- return new ActionResponse({
18
- name,
19
- succeeded: true,
20
- blocking: true
21
- });
22
- }
23
- static Error(name, messageOrData, data) {
24
- if (Array.isArray(messageOrData)) {
25
- const message = messageOrData.filter((r)=>!r.succeeded).map((r)=>r.error ?? "Unknown error").join("\n");
26
- return new ActionResponse({
27
- name,
28
- message,
29
- data: messageOrData
30
- });
31
- }
32
- return new ActionResponse({
33
- name,
34
- message: messageOrData,
35
- data
36
- });
37
- }
38
- constructor(data){
39
- Object.assign(this, data);
40
- }
41
- name;
42
- succeeded = false;
43
- skipped = false;
44
- blocking = false;
45
- message;
46
- data;
47
- }
@@ -1,106 +0,0 @@
1
- import { Command as CommanderType } from "commander";
2
- export const CommandResponse = {
3
- success (_ctx, responses) {
4
- return responses;
5
- },
6
- help () {
7
- return {};
8
- },
9
- fromAction (name, response) {
10
- return {
11
- [name]: response
12
- };
13
- }
14
- };
15
- /** Thrown when a command execution fails due to action errors. */ export class CommandError extends Error {
16
- }
17
- export class DecohereCommand extends CommanderType {
18
- get actions() {
19
- return [];
20
- }
21
- constructor(name, handler){
22
- super();
23
- if (typeof name === "string" || typeof name === "function") {
24
- this.name(this.parseCommandName(name));
25
- } else {
26
- this.name(this.parseCommandName(this.constructor));
27
- if (name) {
28
- this.registerSubcommand(name, handler);
29
- }
30
- }
31
- if (handler && !this.commands.length) {
32
- this.action(handler);
33
- }
34
- this.exitOverride();
35
- }
36
- /**
37
- * Whether the command, with these options, runs until it is stopped (`flux serve`,
38
- * `forge build --watch`). The CLI then listens for SIGINT and SIGTERM from the moment it
39
- * starts, so a stop during start-up still shuts down cleanly; other commands keep Node's
40
- * default, where Ctrl+C ends them at once.
41
- */ runsUntilStopped(_options) {
42
- return false;
43
- }
44
- async run(actions, options) {
45
- const responses = await this.runActions(actions, options);
46
- this.throwOnErrors(responses);
47
- return CommandResponse.success(options, responses);
48
- }
49
- registerSubcommand(commands, onAction) {
50
- if (typeof commands === "function") {
51
- const instance = new commands(commands, onAction);
52
- this.addCommand(instance);
53
- if (onAction && !instance.commands.length) {
54
- instance.action(onAction);
55
- }
56
- return;
57
- }
58
- for (const [key, command] of Object.entries(commands)){
59
- const instance = command instanceof DecohereCommand ? command : new command(key, onAction);
60
- this.addCommand(instance);
61
- if (onAction && !instance.commands.length) {
62
- instance.action(onAction);
63
- }
64
- }
65
- }
66
- async runActions(actions, options) {
67
- const selectedActions = this.getSelectedActions(actions);
68
- const responses = {};
69
- for (const [actionKey, action] of selectedActions){
70
- responses[actionKey] = await action.run(options);
71
- }
72
- return responses;
73
- }
74
- getSelectedActions(actions) {
75
- if (!this.actions.length) {
76
- return [];
77
- }
78
- const actionEntries = Object.entries(actions);
79
- return this.actions.map((ActionClass)=>{
80
- const selectedAction = actionEntries.find(([, action])=>action instanceof ActionClass);
81
- if (!selectedAction) {
82
- throw new CommandError(`${this.name()} command missing action instance for ${ActionClass.name}`);
83
- }
84
- return selectedAction;
85
- });
86
- }
87
- throwOnErrors(responses) {
88
- const failures = Object.entries(responses).filter(([, response])=>{
89
- if (!response) {
90
- return false;
91
- }
92
- return !response.succeeded && !response.skipped;
93
- });
94
- if (!failures.length) {
95
- return;
96
- }
97
- throw new CommandError(`${this.name()} command failed. ${failures.map(([actionName, response])=>`${actionName}: ${response?.message ?? "Unknown error"}`).join("\n")}`);
98
- }
99
- parseCommandName(stringLike) {
100
- const command = typeof stringLike === "string" ? stringLike : stringLike.name?.toLowerCase().replace("command", "") ?? "";
101
- if (command === "") {
102
- throw new CommandError(`Could not parse command name from ${stringLike.toString()}`);
103
- }
104
- return command;
105
- }
106
- }
@@ -1,4 +0,0 @@
1
- export { ActionMap, ActionResponse } from "./action.js";
2
- export { CommandError, CommandResponse, DecohereCommand } from "./command.js";
3
- export { ModuleManifest } from "./module.type.js";
4
- export { ConfigFileOption, ConfigurationOption, DryRunOption, EnvFileIfExistsOption, EnvFileOption, LogLevelOption, NoBuildOptions, NoCleanOptions, ProjectOption } from "./options.js";
@@ -1,11 +0,0 @@
1
- /**
2
- * Simple declaration interface for a CLI module.
3
- * Extend this and implement the four abstract members.
4
- */ export class ModuleManifest {
5
- /** Sub-command classes this module contributes. */ commands = {};
6
- /** Action classes this module contributes. */ actions = {};
7
- /** Register infrastructure services into the DI container. */ registerServices(_builder) {}
8
- /** Lazily create a workspace instance. Return null if this module does not use a workspace. */ createWorkspace(_options) {
9
- return Promise.resolve(null);
10
- }
11
- }
@@ -1,76 +0,0 @@
1
- import { Option } from "commander";
2
- function parseOptionalBoolean(input) {
3
- if (typeof input === "boolean") {
4
- return input;
5
- }
6
- const value = input.trim().toLowerCase();
7
- if ([
8
- "true",
9
- "1",
10
- "yes",
11
- "on"
12
- ].includes(value)) {
13
- return true;
14
- }
15
- if ([
16
- "false",
17
- "0",
18
- "no",
19
- "off"
20
- ].includes(value)) {
21
- return false;
22
- }
23
- throw new Error(`Invalid boolean value: "${input}". Use true/false.`);
24
- }
25
- export class ConfigurationOption extends Option {
26
- constructor(defaultValue){
27
- super("--configuration <configuration>", "Configuration name");
28
- this.default(defaultValue);
29
- }
30
- }
31
- export class ConfigFileOption extends Option {
32
- constructor(defaultValue = ".decohere.js"){
33
- super("--config-file <file>", "Path to Decohere config file");
34
- this.default(defaultValue);
35
- }
36
- }
37
- export class LogLevelOption extends Option {
38
- constructor(){
39
- super("--log-level <level>", "Log level (trace, debug, info, warn, error)");
40
- }
41
- }
42
- export class EnvFileOption extends Option {
43
- constructor(){
44
- super("--env-file <file>", "Load environment variables from this file before running");
45
- }
46
- }
47
- export class EnvFileIfExistsOption extends Option {
48
- constructor(){
49
- super("--env-file-if-exists <file>", "Like --env-file, but skip it when the file is missing");
50
- }
51
- }
52
- export class NoCleanOptions extends Option {
53
- constructor(defaultValue){
54
- super("--no-clean", "Will not clean the project directories");
55
- this.default(defaultValue);
56
- }
57
- }
58
- export class NoBuildOptions extends Option {
59
- constructor(defaultValue){
60
- super("--no-build", "Will not run the build step");
61
- this.default(defaultValue);
62
- }
63
- }
64
- export class DryRunOption extends Option {
65
- constructor(description, defaultValue = false){
66
- super("--dry-run [dryRun]", description);
67
- this.default(defaultValue);
68
- this.preset(true);
69
- this.argParser(parseOptionalBoolean);
70
- }
71
- }
72
- export class ProjectOption extends Option {
73
- constructor(){
74
- super("--project <project>", "Project name to target");
75
- }
76
- }
@@ -1,61 +0,0 @@
1
- import { isPromise } from "node:util/types";
2
- import { scribe } from "@decohere/scribe";
3
- /**
4
- * Trace mode is toggled with FORGE_TRACE, read when a method is decorated. Raising the log level
5
- * to see the output is the CLI's job at its entry point: importing this module changes nothing.
6
- */ function isTraceEnabled() {
7
- return process.env.FORGE_TRACE != null;
8
- }
9
- function serializeArgs(args) {
10
- if (args.length === 0) return "";
11
- return args.map((arg)=>{
12
- if (Array.isArray(arg)) return `[${arg.map(String).join(", ")}]`;
13
- const s = String(arg);
14
- return s.length > 60 ? `${s.slice(0, 57)}...` : s;
15
- }).join(", ");
16
- }
17
- function wrapWithTiming(fn, className, methodName, logLevel) {
18
- let logger;
19
- return function(...args) {
20
- if (logger == null) {
21
- logger = scribe.logger(className);
22
- }
23
- const log = logger;
24
- const label = `${methodName}(${serializeArgs(args)})`;
25
- const start = performance.now();
26
- const logDuration = ()=>{
27
- log[logLevel](`${label}: ${(performance.now() - start).toFixed(2)}ms`);
28
- };
29
- const result = fn.apply(this, args);
30
- if (isPromise(result)) {
31
- return result.finally(logDuration);
32
- }
33
- logDuration();
34
- return result;
35
- };
36
- }
37
- export function trace(target, propertyKey, descriptor) {
38
- if (!isTraceEnabled()) return descriptor;
39
- const className = target.constructor.name;
40
- descriptor.value = wrapWithTiming(descriptor.value, className, propertyKey, "trace");
41
- return descriptor;
42
- }
43
- /**
44
- * Always-on method timing. Logs duration at `info` regardless of `FORGE_TRACE`.
45
- * Use this to time a specific method without raising the process log level.
46
- */ export function timed(target, propertyKey, descriptor) {
47
- const className = target.constructor.name;
48
- descriptor.value = wrapWithTiming(descriptor.value, className, propertyKey, "info");
49
- return descriptor;
50
- }
51
- export function Trace(constructor) {
52
- if (!isTraceEnabled()) return;
53
- const className = constructor.name;
54
- for (const key of Object.getOwnPropertyNames(constructor.prototype)){
55
- if (key === "constructor") continue;
56
- const descriptor = Object.getOwnPropertyDescriptor(constructor.prototype, key);
57
- if (descriptor == null || typeof descriptor.value !== "function") continue;
58
- descriptor.value = wrapWithTiming(descriptor.value, className, key, "trace");
59
- Object.defineProperty(constructor.prototype, key, descriptor);
60
- }
61
- }
@@ -1,44 +0,0 @@
1
- import { InteropError } from "../errors/interop.error.js";
2
- import { Shell } from "../shell/shell.js";
3
- export class Docker extends Shell {
4
- name = "docker";
5
- program = "docker";
6
- async build(contextDir, options = {}) {
7
- if (contextDir == null || contextDir.trim() === "") {
8
- throw new InteropError("Docker build context is required");
9
- }
10
- const args = [
11
- "build"
12
- ];
13
- if (options.dockerfile) {
14
- args.push("-f", options.dockerfile);
15
- }
16
- for (const tag of options.tags ?? []){
17
- args.push("-t", tag);
18
- }
19
- for (const [key, value] of Object.entries(options.buildArgs ?? {})){
20
- args.push("--build-arg", `${key}=${value}`);
21
- }
22
- for (const [id, env] of Object.entries(options.secrets ?? {})){
23
- args.push("--secret", `id=${id},env=${env}`);
24
- }
25
- if (options.noCache) {
26
- args.push("--no-cache");
27
- }
28
- if (options.pull) {
29
- args.push("--pull");
30
- }
31
- args.push(contextDir);
32
- return await this.run({
33
- cwd: options.cwd
34
- }, ...args);
35
- }
36
- async push(tag, options = {}) {
37
- if (tag == null || tag.trim() === "") {
38
- throw new InteropError("Docker tag is required");
39
- }
40
- return await this.run({
41
- cwd: options.cwd
42
- }, "push", tag);
43
- }
44
- }
@@ -1,2 +0,0 @@
1
- export class InteropError extends Error {
2
- }