@sshkeda/task 0.0.0-stage → 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.
Files changed (147) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -2
  3. package/package.json +41 -4
  4. package/src/index.ts +2 -0
  5. package/src/lib/errors/index.ts +15 -0
  6. package/src/lib/errors/internal/errors.ts +147 -0
  7. package/src/lib/kill-leftover-processes/index.ts +17 -0
  8. package/src/lib/kill-leftover-processes/internal/command-words.ts +16 -0
  9. package/src/lib/kill-leftover-processes/internal/find-process-ids.ts +36 -0
  10. package/src/lib/kill-leftover-processes/internal/kill-process-groups.ts +270 -0
  11. package/src/lib/kill-leftover-processes/internal/list-linux-processes-from-proc.ts +191 -0
  12. package/src/lib/kill-leftover-processes/internal/list-macos-processes-with-sysctl.ts +335 -0
  13. package/src/lib/kill-leftover-processes/internal/list-processes.ts +58 -0
  14. package/src/lib/kill-leftover-processes/internal/mark-run-processes.ts +367 -0
  15. package/src/lib/kill-leftover-processes/internal/process-cleanup-errors.ts +17 -0
  16. package/src/lib/kill-leftover-processes/internal/send-signal.ts +32 -0
  17. package/src/lib/kill-leftover-processes/internal/signal-run-processes.ts +48 -0
  18. package/src/lib/kill-leftover-processes/internal/started-process-groups.ts +107 -0
  19. package/src/lib/main-thread/index.ts +3 -0
  20. package/src/lib/main-thread/internal/listen-for-stop-requests.ts +102 -0
  21. package/src/lib/main-thread/internal/run-main-thread.ts +81 -0
  22. package/src/lib/main-thread/internal/run-root-task-fiber.ts +191 -0
  23. package/src/lib/main-thread/internal/stops-at-first-failure.ts +20 -0
  24. package/src/lib/main-thread/internal/trace-the-run.ts +107 -0
  25. package/src/lib/main-thread/lib/find-code-each-task-depends-on/index.ts +7 -0
  26. package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/follow-imports-from-entry-files.ts +306 -0
  27. package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/load-all-reporting-first-failure.ts +11 -0
  28. package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/make-module-graph.ts +48 -0
  29. package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/read-configured-loaders.ts +64 -0
  30. package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/resolve-module-specifiers.ts +60 -0
  31. package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/rewrite-worker-imports.ts +118 -0
  32. package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/worker-import-errors.ts +19 -0
  33. package/src/lib/main-thread/lib/run-tasks-on-workers/index.ts +7 -0
  34. package/src/lib/main-thread/lib/run-tasks-on-workers/internal/handle-worker-events.ts +348 -0
  35. package/src/lib/main-thread/lib/run-tasks-on-workers/internal/make-task-runs.ts +207 -0
  36. package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/index.ts +1 -0
  37. package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/await-within-counted-limit.ts +29 -0
  38. package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/execute-task.ts +272 -0
  39. package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/make-task-setup-clock.ts +129 -0
  40. package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/run-requested-tasks.ts +135 -0
  41. package/src/lib/main-thread/lib/run-tasks-on-workers/shared/define-task-run-state.ts +288 -0
  42. package/src/lib/main-thread/lib/run-tasks-on-workers/shared/make-stopwatch.ts +39 -0
  43. package/src/lib/main-thread/lib/run-tasks-on-workers/shared/share-standard-input-with-root-task.ts +36 -0
  44. package/src/lib/main-thread/lib/run-tasks-on-workers/shared/stop-task.ts +166 -0
  45. package/src/lib/main-thread/lib/scan-module-source/index.ts +1 -0
  46. package/src/lib/main-thread/lib/scan-module-source/internal/find-lexical-bindings.ts +154 -0
  47. package/src/lib/main-thread/lib/scan-module-source/internal/find-task-declarations.ts +205 -0
  48. package/src/lib/main-thread/lib/scan-module-source/internal/parse-module-text.ts +34 -0
  49. package/src/lib/main-thread/lib/scan-module-source/internal/read-literal-string.ts +15 -0
  50. package/src/lib/main-thread/lib/scan-module-source/internal/read-module-loads.ts +316 -0
  51. package/src/lib/main-thread/lib/scan-module-source/internal/scan-module-text.ts +70 -0
  52. package/src/lib/main-thread/lib/scan-module-source/internal/unwrap-type-assertions.ts +10 -0
  53. package/src/lib/main-thread/lib/scan-module-source/internal/visit-non-reference-identifiers.ts +21 -0
  54. package/src/lib/main-thread/shared/fail-run.ts +36 -0
  55. package/src/lib/main-thread/shared/find-failure-reason.ts +43 -0
  56. package/src/lib/main-thread/shared/find-package-directories.ts +34 -0
  57. package/src/lib/main-thread/shared/find-tasks-reachable-from-root-task-file.ts +246 -0
  58. package/src/lib/main-thread/shared/list-directory-paths-up-to-root.ts +13 -0
  59. package/src/lib/main-thread/shared/main-thread-errors.ts +373 -0
  60. package/src/lib/main-thread/shared/package-json-errors.ts +93 -0
  61. package/src/lib/main-thread/shared/provide-discovered-tasks.ts +26 -0
  62. package/src/lib/main-thread/shared/provide-process-groups.ts +63 -0
  63. package/src/lib/main-thread/shared/provide-run-root-task-config.ts +66 -0
  64. package/src/lib/main-thread/shared/provide-stop-state.ts +150 -0
  65. package/src/lib/main-thread/shared/provide-worker-pool.ts +607 -0
  66. package/src/lib/main-thread/shared/read-bare-package-name.ts +3 -0
  67. package/src/lib/main-thread/shared/read-package-json.ts +149 -0
  68. package/src/lib/main-thread/shared/report-task-failure.ts +31 -0
  69. package/src/lib/preload-rewritten-imports-into-each-worker/index.ts +5 -0
  70. package/src/lib/preload-rewritten-imports-into-each-worker/internal/load-rewritten-imports.ts +34 -0
  71. package/src/lib/supervise-the-run/index.ts +15 -0
  72. package/src/lib/supervise-the-run/internal/adopt-orphans-on-linux.ts +73 -0
  73. package/src/lib/supervise-the-run/internal/check-root-task-file.ts +8 -0
  74. package/src/lib/supervise-the-run/internal/report-ready.ts +12 -0
  75. package/src/lib/supervise-the-run/internal/share-process-groups.ts +25 -0
  76. package/src/lib/supervise-the-run/internal/start-watching-the-supervisor.ts +6 -0
  77. package/src/lib/supervise-the-run/internal/supervise-run.ts +155 -0
  78. package/src/lib/supervise-the-run/internal/talk-to-the-supervisor.ts +53 -0
  79. package/src/lib/supervise-the-run/internal/watch-the-supervisor.ts +61 -0
  80. package/src/lib/task/index.ts +5 -0
  81. package/src/lib/task/internal/make-tasks.ts +82 -0
  82. package/src/lib/task/internal/run-command.ts +194 -0
  83. package/src/lib/task/internal/run-root-task.ts +263 -0
  84. package/src/lib/task/internal/run-task-lists.ts +51 -0
  85. package/src/lib/task/internal/trust-output-mode-names.ts +18 -0
  86. package/src/lib/task/internal/trust-task-results.ts +35 -0
  87. package/src/lib/task/internal/use-task-services.ts +4 -0
  88. package/src/lib/worker-thread/index.ts +3 -0
  89. package/src/lib/worker-thread/internal/answer-main-thread-messages.ts +141 -0
  90. package/src/lib/worker-thread/internal/describe-task-body-result.ts +91 -0
  91. package/src/lib/worker-thread/internal/name-config-key-not-set.ts +57 -0
  92. package/src/lib/worker-thread/internal/run-task-body.ts +117 -0
  93. package/src/lib/worker-thread/internal/serve-on-the-parent-port.ts +63 -0
  94. package/src/lib/worker-thread/internal/start-worker-thread.ts +41 -0
  95. package/src/lib/worker-thread/lib/provide-task-services/index.ts +1 -0
  96. package/src/lib/worker-thread/lib/provide-task-services/internal/make-task-logger.ts +34 -0
  97. package/src/lib/worker-thread/lib/provide-task-services/internal/provide-task-services.ts +86 -0
  98. package/src/lib/worker-thread/lib/provide-task-services/internal/read-standard-input-from-main-thread.ts +58 -0
  99. package/src/lib/worker-thread/lib/provide-task-services/internal/resolve-paths-from-package-directory.ts +61 -0
  100. package/src/lib/worker-thread/lib/provide-task-services/internal/run-commands-in-package-directory.ts +24 -0
  101. package/src/lib/worker-thread/lib/provide-task-services/internal/spawn-commands-with-bun.ts +545 -0
  102. package/src/lib/worker-thread/lib/provide-task-services/internal/spawn-reported-process.ts +151 -0
  103. package/src/lib/worker-thread/shared/await-main-thread-answers.ts +76 -0
  104. package/src/lib/worker-thread/shared/decode-task-result-json.ts +114 -0
  105. package/src/lib/worker-thread/shared/define-task-body-record.ts +11 -0
  106. package/src/lib/worker-thread/shared/describe-issue-input.ts +52 -0
  107. package/src/lib/worker-thread/shared/plain-object.ts +8 -0
  108. package/src/lib/worker-thread/shared/provide-worker-state.ts +160 -0
  109. package/src/lib/worker-thread/shared/worker-thread-errors.ts +41 -0
  110. package/src/shared/attribute-reported-failures.ts +25 -0
  111. package/src/shared/claim-processes-to-report.ts +22 -0
  112. package/src/shared/command-errors.ts +44 -0
  113. package/src/shared/complete-discarded-calls-once-sent.ts +12 -0
  114. package/src/shared/cut-to-graphemes.ts +12 -0
  115. package/src/shared/environment-config-errors.ts +74 -0
  116. package/src/shared/error-fields.ts +13 -0
  117. package/src/shared/find-bun-loader-for-script.ts +32 -0
  118. package/src/shared/find-node-modules-package-directory.ts +35 -0
  119. package/src/shared/find-user-locations-in-stack-traces.ts +64 -0
  120. package/src/shared/format-error-lines.ts +53 -0
  121. package/src/shared/format-task-lines.ts +21 -0
  122. package/src/shared/hold-task-logs.ts +24 -0
  123. package/src/shared/invariant-errors.ts +28 -0
  124. package/src/shared/mark-task-worker-thread-contexts.ts +29 -0
  125. package/src/shared/module-graph-errors.ts +49 -0
  126. package/src/shared/name-config-key.ts +42 -0
  127. package/src/shared/names-from-package-json.ts +7 -0
  128. package/src/shared/print-to-standard-error.ts +48 -0
  129. package/src/shared/process-errors.ts +191 -0
  130. package/src/shared/read-environment-config.ts +239 -0
  131. package/src/shared/record-tasks-made.ts +10 -0
  132. package/src/shared/report-cause.ts +140 -0
  133. package/src/shared/report-errors.ts +121 -0
  134. package/src/shared/resolve-task-options.ts +20 -0
  135. package/src/shared/rpc.ts +161 -0
  136. package/src/shared/run-in-child-fiber-and-join.ts +16 -0
  137. package/src/shared/run-marker.ts +7 -0
  138. package/src/shared/save-standard-stream-flags.ts +57 -0
  139. package/src/shared/select-cause-reasons.ts +14 -0
  140. package/src/shared/signal-run-called-twice.ts +11 -0
  141. package/src/shared/standard-input-streams.ts +41 -0
  142. package/src/shared/stop-signals.ts +41 -0
  143. package/src/shared/task-errors.ts +224 -0
  144. package/src/shared/task-package.ts +5 -0
  145. package/src/shared/task-results.ts +55 -0
  146. package/src/shared/task-services.ts +30 -0
  147. package/src/shared/write-trace-file.ts +112 -0
@@ -0,0 +1,373 @@
1
+ import { Array as Arrays, Duration, Schema } from "effect";
2
+
3
+ import { TASK_ERROR_FIELDS, UNATTRIBUTED_FAILURE_FIELDS } from "../../../shared/error-fields.ts";
4
+ import { ImportResolutionThrew } from "../../../shared/module-graph-errors.ts";
5
+ import { describeCauseMessage, reportError } from "../../../shared/report-errors.ts";
6
+ import { TASK_OPTION_NAMES } from "../../../shared/resolve-task-options.ts";
7
+ import { exitCodeAfterStopSignal, STOP_REASONS } from "../../../shared/stop-signals.ts";
8
+ import { TASK_PACKAGE } from "../../../shared/task-package.ts";
9
+ import type { PrintableError } from "../../errors/index.ts";
10
+
11
+ export class ImportNotFound extends Schema.TaggedError<ImportNotFound>()("ImportNotFound", {
12
+ ...TASK_ERROR_FIELDS,
13
+ importingFilePath: Schema.String,
14
+ specifier: Schema.String,
15
+ }) {
16
+ public get location() {
17
+ return this.importingFilePath;
18
+ }
19
+ public override get message() {
20
+ return `imports "${this.specifier}", which the resolver reported as not found`;
21
+ }
22
+ }
23
+
24
+ export class MissingPackageJson extends Schema.TaggedError<MissingPackageJson>()(
25
+ "MissingPackageJson",
26
+ { ...TASK_ERROR_FIELDS, filePath: Schema.String },
27
+ ) {
28
+ public get location() {
29
+ return this.filePath;
30
+ }
31
+ public override get message() {
32
+ return "no package.json found in its directory or any parent directory";
33
+ }
34
+ }
35
+
36
+ export class RootTaskFileUnavailable extends Schema.TaggedError<RootTaskFileUnavailable>()(
37
+ "RootTaskFileUnavailable",
38
+ { ...TASK_ERROR_FIELDS, filePath: Schema.String, cause: Schema.Defect() },
39
+ ) {
40
+ public get location() {
41
+ return this.filePath;
42
+ }
43
+ public override get message() {
44
+ return `could not resolve the root task file because ${describeCauseMessage(this.cause)}`;
45
+ }
46
+ }
47
+
48
+ export class RootTaskWorkingDirectoryUnavailable extends Schema.TaggedError<RootTaskWorkingDirectoryUnavailable>()(
49
+ "RootTaskWorkingDirectoryUnavailable",
50
+ { ...TASK_ERROR_FIELDS, packageJsonDirectoryPath: Schema.String, cause: Schema.Defect() },
51
+ ) {
52
+ public override get message() {
53
+ return `could not enter root task directory "${this.packageJsonDirectoryPath}" because ${describeCauseMessage(this.cause)}`;
54
+ }
55
+ }
56
+
57
+ const SCANNED_LINE_FIELDS = { line: Schema.NullOr(Schema.Int) };
58
+
59
+ export class ModuleSyntaxInvalid extends Schema.TaggedError<ModuleSyntaxInvalid>()(
60
+ "ModuleSyntaxInvalid",
61
+ { ...TASK_ERROR_FIELDS, ...SCANNED_LINE_FIELDS, parserMessage: Schema.String },
62
+ ) {
63
+ public override get message() {
64
+ return `has the syntax error ${this.parserMessage}`;
65
+ }
66
+ }
67
+
68
+ export class TaskPackageImportedWithoutTask extends Schema.TaggedError<TaskPackageImportedWithoutTask>()(
69
+ "TaskPackageImportedWithoutTask",
70
+ { ...SCANNED_LINE_FIELDS, ...TASK_ERROR_FIELDS },
71
+ ) {
72
+ public override get message() {
73
+ return `imports ${TASK_PACKAGE.name} without naming Task`;
74
+ }
75
+ }
76
+
77
+ export class ModuleLoaderRefused extends Schema.TaggedError<ModuleLoaderRefused>()(
78
+ "ModuleLoaderRefused",
79
+ { ...TASK_ERROR_FIELDS, ...SCANNED_LINE_FIELDS, loader: Schema.String },
80
+ ) {
81
+ public override get message() {
82
+ return `uses the module loader ${this.loader}`;
83
+ }
84
+ }
85
+
86
+ export class ModuleLoadedByComputedPath extends Schema.TaggedError<ModuleLoadedByComputedPath>()(
87
+ "ModuleLoadedByComputedPath",
88
+ {
89
+ ...TASK_ERROR_FIELDS,
90
+ ...SCANNED_LINE_FIELDS,
91
+ loader: Schema.String,
92
+ argumentText: Schema.String,
93
+ },
94
+ ) {
95
+ public override get message() {
96
+ return `calls ${this.loader}(${this.argumentText}), which has no literal path`;
97
+ }
98
+ }
99
+
100
+ export class TaskNameMissing extends Schema.TaggedError<TaskNameMissing>()("TaskNameMissing", {
101
+ ...SCANNED_LINE_FIELDS,
102
+ ...TASK_ERROR_FIELDS,
103
+ }) {
104
+ public override get message() {
105
+ return "calls Task.make() without a name";
106
+ }
107
+ }
108
+
109
+ export class TaskNameNotString extends Schema.TaggedError<TaskNameNotString>()(
110
+ "TaskNameNotString",
111
+ { ...SCANNED_LINE_FIELDS, ...TASK_ERROR_FIELDS, nameText: Schema.String },
112
+ ) {
113
+ public override get message() {
114
+ return `calls Task.make() with the name ${this.nameText}, which is not a string`;
115
+ }
116
+ }
117
+
118
+ export class TaskNameComputed extends Schema.TaggedError<TaskNameComputed>()("TaskNameComputed", {
119
+ ...SCANNED_LINE_FIELDS,
120
+ ...TASK_ERROR_FIELDS,
121
+ nameText: Schema.String,
122
+ }) {
123
+ public override get message() {
124
+ return `calls Task.make() with the name ${this.nameText}, which is computed`;
125
+ }
126
+ }
127
+
128
+ export class TaskNameEmpty extends Schema.TaggedError<TaskNameEmpty>()("TaskNameEmpty", {
129
+ ...SCANNED_LINE_FIELDS,
130
+ ...TASK_ERROR_FIELDS,
131
+ }) {
132
+ public override get message() {
133
+ return "calls Task.make() with an empty name";
134
+ }
135
+ }
136
+
137
+ export class TaskCalledBelowTopLevel extends Schema.TaggedError<TaskCalledBelowTopLevel>()(
138
+ "TaskCalledBelowTopLevel",
139
+ { ...SCANNED_LINE_FIELDS, ...TASK_ERROR_FIELDS },
140
+ ) {
141
+ public override get message() {
142
+ return "calls Task.make() below the top level of its module";
143
+ }
144
+ }
145
+
146
+ export const ModuleScanRefusal = Schema.Union([
147
+ ModuleSyntaxInvalid,
148
+ TaskPackageImportedWithoutTask,
149
+ ModuleLoaderRefused,
150
+ ModuleLoadedByComputedPath,
151
+ TaskNameMissing,
152
+ TaskNameNotString,
153
+ TaskNameComputed,
154
+ TaskNameEmpty,
155
+ TaskCalledBelowTopLevel,
156
+ ]);
157
+
158
+ const ScannedModuleRefusal = Schema.Struct({
159
+ filePath: Schema.String,
160
+ refusal: ModuleScanRefusal,
161
+ });
162
+
163
+ export const ModuleRefusal = Schema.Union([
164
+ ScannedModuleRefusal,
165
+ ImportNotFound,
166
+ ImportResolutionThrew,
167
+ ]);
168
+
169
+ export class TaskFileImportsItself extends Schema.TaggedError<TaskFileImportsItself>()(
170
+ "TaskFileImportsItself",
171
+ { ...TASK_ERROR_FIELDS, filePath: Schema.String, cycle: Schema.Array(Schema.String) },
172
+ ) {
173
+ public override get message() {
174
+ return `imports ${this.cycle.slice(1).join(", which imports ")}; a worker imports only the file of the task it runs, so a task file in an import cycle would load in a different order there than when Task.main() loaded it`;
175
+ }
176
+ }
177
+
178
+ export class TaskMadeMoreThanOnce extends Schema.TaggedError<TaskMadeMoreThanOnce>()(
179
+ "TaskMadeMoreThanOnce",
180
+ { ...TASK_ERROR_FIELDS, taskName: Schema.String, location: Schema.String, madeCount: Schema.Int },
181
+ ) {
182
+ public override get message() {
183
+ return `makes the task "${this.taskName}", which ${this.madeCount} Task.make() calls make`;
184
+ }
185
+ }
186
+
187
+ export class TaskNotMadeByImport extends Schema.TaggedError<TaskNotMadeByImport>()(
188
+ "TaskNotMadeByImport",
189
+ { ...TASK_ERROR_FIELDS, taskName: Schema.String, filePath: Schema.String },
190
+ ) {
191
+ public override get message() {
192
+ return `importing this file did not make task "${this.taskName}" in the copy of ${TASK_PACKAGE.name} that calls Task.main()`;
193
+ }
194
+ }
195
+
196
+ export class TaskMadeAgainInThread extends Schema.TaggedError<TaskMadeAgainInThread>()(
197
+ "TaskMadeAgainInThread",
198
+ {
199
+ ...TASK_ERROR_FIELDS,
200
+ taskName: Schema.String,
201
+ filePath: Schema.String,
202
+ madeCount: Schema.Int,
203
+ },
204
+ ) {
205
+ public override get message() {
206
+ return `Task.make() made "${this.taskName}" ${this.madeCount} times in this thread, but this file has its only top-level Task.make() call`;
207
+ }
208
+ }
209
+
210
+ export class TaskTimeoutInvalid extends Schema.TaggedError<TaskTimeoutInvalid>()(
211
+ "TaskTimeoutInvalid",
212
+ { ...TASK_ERROR_FIELDS, taskName: Schema.String, timeout: Schema.String },
213
+ ) {
214
+ public override get message() {
215
+ return `has the timeout ${this.timeout}, which is not a duration above 0`;
216
+ }
217
+ }
218
+
219
+ export class TaskCleanupInvalid extends Schema.TaggedError<TaskCleanupInvalid>()(
220
+ "TaskCleanupInvalid",
221
+ { ...TASK_ERROR_FIELDS, taskName: Schema.String, cleanup: Schema.String },
222
+ ) {
223
+ public override get message() {
224
+ return `has the cleanup ${this.cleanup}, which is not a duration of 0 or more`;
225
+ }
226
+ }
227
+
228
+ export class TaskOptionsUnknown extends Schema.TaggedError<TaskOptionsUnknown>()(
229
+ "TaskOptionsUnknown",
230
+ {
231
+ ...TASK_ERROR_FIELDS,
232
+ taskName: Schema.String,
233
+ location: Schema.String,
234
+ optionNames: Schema.NonEmptyArray(Schema.String),
235
+ },
236
+ ) {
237
+ public override get message() {
238
+ return `task "${this.taskName}" has the ${this.optionNames.length === 1 ? "option" : "options"} ${this.optionNames.join(", ")} outside the task options (${TASK_OPTION_NAMES.join(", ")})`;
239
+ }
240
+ }
241
+
242
+ export class RootTaskNotMadeAtTopLevel extends Schema.TaggedError<RootTaskNotMadeAtTopLevel>()(
243
+ "RootTaskNotMadeAtTopLevel",
244
+ { ...TASK_ERROR_FIELDS, rootTaskFilePath: Schema.String },
245
+ ) {
246
+ public override get message() {
247
+ return `${this.rootTaskFilePath} runs a task that is not made by a top-level Task.make() call`;
248
+ }
249
+ }
250
+
251
+ export const TaskDefinitionViolation = Schema.Union([
252
+ ModuleRefusal,
253
+ TaskFileImportsItself,
254
+ TaskMadeMoreThanOnce,
255
+ TaskNotMadeByImport,
256
+ TaskMadeAgainInThread,
257
+ TaskTimeoutInvalid,
258
+ TaskCleanupInvalid,
259
+ TaskOptionsUnknown,
260
+ RootTaskNotMadeAtTopLevel,
261
+ ]);
262
+
263
+ const isScannedModuleRefusal = Schema.is(ScannedModuleRefusal);
264
+
265
+ export const reportTaskDefinitionViolation = (
266
+ violation: typeof TaskDefinitionViolation.Type,
267
+ ): PrintableError =>
268
+ isScannedModuleRefusal(violation)
269
+ ? {
270
+ tag: violation.refusal._tag,
271
+ location: `${violation.filePath}${violation.refusal.line === null ? "" : `:${violation.refusal.line}`}`,
272
+ message: violation.refusal.message,
273
+ }
274
+ : reportError(violation);
275
+
276
+ export class BrokenTasksFound extends Schema.TaggedError<BrokenTasksFound>()("BrokenTasksFound", {
277
+ ...TASK_ERROR_FIELDS,
278
+ ...UNATTRIBUTED_FAILURE_FIELDS,
279
+ violations: Schema.NonEmptyArray(TaskDefinitionViolation),
280
+ }) {
281
+ public get reportedErrors() {
282
+ return Arrays.map(this.violations, reportTaskDefinitionViolation);
283
+ }
284
+ public override get message() {
285
+ return `found ${this.violations.length} task definition ${this.violations.length === 1 ? "violation" : "violations"}`;
286
+ }
287
+ }
288
+
289
+ export class StopRequested extends Schema.TaggedError<StopRequested>()("StopRequested", {
290
+ ...TASK_ERROR_FIELDS,
291
+ ...UNATTRIBUTED_FAILURE_FIELDS,
292
+ signal: Schema.Literals(STOP_REASONS),
293
+ }) {
294
+ public override get message() {
295
+ return this.signal === "SIGPIPE"
296
+ ? "standard error closed, so task stopped the run"
297
+ : `received ${this.signal}`;
298
+ }
299
+ public get exitCode() {
300
+ return exitCodeAfterStopSignal(this.signal);
301
+ }
302
+ }
303
+
304
+ export class ServingTaskRequestsItself extends Schema.TaggedError<ServingTaskRequestsItself>()(
305
+ "ServingTaskRequestsItself",
306
+ { ...TASK_ERROR_FIELDS, taskName: Schema.String, cycle: Schema.Array(Schema.String) },
307
+ ) {
308
+ public override get message() {
309
+ return `requests itself while serving through ${this.cycle.join(" -> ")}, so task cannot stop each of them after the tasks that requested it`;
310
+ }
311
+ }
312
+
313
+ export class TaskCleanupTimedOut extends Schema.TaggedError<TaskCleanupTimedOut>()(
314
+ "TaskCleanupTimedOut",
315
+ { ...TASK_ERROR_FIELDS, taskName: Schema.String, cleanupMilliseconds: Schema.Finite },
316
+ ) {
317
+ public override get message() {
318
+ return `its cleanup did not finish within ${Duration.format(Duration.millis(this.cleanupMilliseconds))}`;
319
+ }
320
+ }
321
+
322
+ export class TaskCleanupNotStarted extends Schema.TaggedError<TaskCleanupNotStarted>()(
323
+ "TaskCleanupNotStarted",
324
+ { ...TASK_ERROR_FIELDS, taskName: Schema.String, cleanupMilliseconds: Schema.Finite },
325
+ ) {
326
+ public override get message() {
327
+ return `its worker did not start its cleanup within ${Duration.format(Duration.millis(this.cleanupMilliseconds))}, so task terminated that worker`;
328
+ }
329
+ }
330
+
331
+ export class TaskCleanupFrozeWorker extends Schema.TaggedError<TaskCleanupFrozeWorker>()(
332
+ "TaskCleanupFrozeWorker",
333
+ { ...TASK_ERROR_FIELDS, taskName: Schema.String, cleanupMilliseconds: Schema.Finite },
334
+ ) {
335
+ public override get message() {
336
+ const cleanupLimit = Duration.format(Duration.millis(this.cleanupMilliseconds));
337
+ return `its cleanup ran past its ${cleanupLimit} limit and then stopped its worker from answering, so task terminated that worker`;
338
+ }
339
+ }
340
+
341
+ export class UnawaitedTasksFailed extends Schema.TaggedError<UnawaitedTasksFailed>()(
342
+ "UnawaitedTasksFailed",
343
+ {
344
+ ...TASK_ERROR_FIELDS,
345
+ ...UNATTRIBUTED_FAILURE_FIELDS,
346
+ failedTaskNames: Schema.NonEmptyArray(Schema.String),
347
+ },
348
+ ) {
349
+ public override get message() {
350
+ return `no task awaited the failure of ${this.failedTaskNames.join(", ")}`;
351
+ }
352
+ }
353
+
354
+ export class TraceFileIncomplete extends Schema.TaggedError<TraceFileIncomplete>()(
355
+ "TraceFileIncomplete",
356
+ { ...TASK_ERROR_FIELDS, traceFilePath: Schema.String, reason: Schema.String },
357
+ ) {
358
+ public get location() {
359
+ return this.traceFilePath;
360
+ }
361
+ public override get message() {
362
+ return `the trace file is incomplete because ${this.reason}`;
363
+ }
364
+ }
365
+
366
+ export class FailureReasonNotFormatted extends Schema.TaggedError<FailureReasonNotFormatted>()(
367
+ "FailureReasonNotFormatted",
368
+ { ...TASK_ERROR_FIELDS, cause: Schema.Defect() },
369
+ ) {
370
+ public override get message() {
371
+ return "the reason the run failed could not be formatted";
372
+ }
373
+ }
@@ -0,0 +1,93 @@
1
+ import { Schema } from "effect";
2
+
3
+ import { TASK_ERROR_FIELDS } from "../../../shared/error-fields.ts";
4
+ import { describeCauseMessage } from "../../../shared/report-errors.ts";
5
+
6
+ export class PackageJsonUnreadable extends Schema.TaggedError<PackageJsonUnreadable>()(
7
+ "PackageJsonUnreadable",
8
+ { ...TASK_ERROR_FIELDS, filePath: Schema.String, cause: Schema.Defect() },
9
+ ) {
10
+ public override get message() {
11
+ return `could not read it because ${describeCauseMessage(this.cause)}`;
12
+ }
13
+ }
14
+
15
+ export class PackageJsonNotJson extends Schema.TaggedError<PackageJsonNotJson>()(
16
+ "PackageJsonNotJson",
17
+ { ...TASK_ERROR_FIELDS, filePath: Schema.String, parserMessage: Schema.NullOr(Schema.String) },
18
+ ) {
19
+ public override get message() {
20
+ return this.parserMessage === null
21
+ ? "is not JSON"
22
+ : `is not JSON because ${this.parserMessage}`;
23
+ }
24
+ }
25
+
26
+ export class PackageJsonNotObject extends Schema.TaggedError<PackageJsonNotObject>()(
27
+ "PackageJsonNotObject",
28
+ { ...TASK_ERROR_FIELDS, filePath: Schema.String, valueText: Schema.String },
29
+ ) {
30
+ public override get message() {
31
+ return `holds ${this.valueText}, not an object`;
32
+ }
33
+ }
34
+
35
+ export class PackageJsonNameNotString extends Schema.TaggedError<PackageJsonNameNotString>()(
36
+ "PackageJsonNameNotString",
37
+ { ...TASK_ERROR_FIELDS, filePath: Schema.String, nameText: Schema.String },
38
+ ) {
39
+ public override get message() {
40
+ return `has the "name" ${this.nameText}, which is not a string`;
41
+ }
42
+ }
43
+
44
+ export class PackageJsonNameBlank extends Schema.TaggedError<PackageJsonNameBlank>()(
45
+ "PackageJsonNameBlank",
46
+ { ...TASK_ERROR_FIELDS, filePath: Schema.String, packageName: Schema.String },
47
+ ) {
48
+ public override get message() {
49
+ return `has the "name" "${this.packageName}", which is blank`;
50
+ }
51
+ }
52
+
53
+ export class PackageJsonNameInvalid extends Schema.TaggedError<PackageJsonNameInvalid>()(
54
+ "PackageJsonNameInvalid",
55
+ { ...TASK_ERROR_FIELDS, filePath: Schema.String, packageName: Schema.String },
56
+ ) {
57
+ public override get message() {
58
+ return `has the "name" "${this.packageName}", which is not a package name: "name" or "@scope/name" with no part starting with "."`;
59
+ }
60
+ }
61
+
62
+ export class PackageJsonFieldInvalid extends Schema.TaggedError<PackageJsonFieldInvalid>()(
63
+ "PackageJsonFieldInvalid",
64
+ {
65
+ ...TASK_ERROR_FIELDS,
66
+ filePath: Schema.String,
67
+ fieldName: Schema.String,
68
+ valueText: Schema.String,
69
+ expected: Schema.String,
70
+ },
71
+ ) {
72
+ public override get message() {
73
+ return `has the "${this.fieldName}" ${this.valueText}, which is not ${this.expected}`;
74
+ }
75
+ }
76
+
77
+ export class PackageJsonWithoutName extends Schema.TaggedError<PackageJsonWithoutName>()(
78
+ "PackageJsonWithoutName",
79
+ { ...TASK_ERROR_FIELDS, filePath: Schema.String },
80
+ ) {
81
+ public override get message() {
82
+ return `has no "name"`;
83
+ }
84
+ }
85
+
86
+ export class PackageJsonSearchFailed extends Schema.TaggedError<PackageJsonSearchFailed>()(
87
+ "PackageJsonSearchFailed",
88
+ { ...TASK_ERROR_FIELDS, filePath: Schema.String, cause: Schema.Defect() },
89
+ ) {
90
+ public override get message() {
91
+ return `could not check for package.json because ${describeCauseMessage(this.cause)}`;
92
+ }
93
+ }
@@ -0,0 +1,26 @@
1
+ import { Context, Effect, Layer, Schema } from "effect";
2
+
3
+ import { TASK_PACKAGE } from "../../../shared/task-package.ts";
4
+ import { findTasksReachableFromRootTaskFile } from "./find-tasks-reachable-from-root-task-file.ts";
5
+ import { BrokenTasksFound } from "./main-thread-errors.ts";
6
+ import { StopState } from "./provide-stop-state.ts";
7
+
8
+ const isBrokenTasksFound = Schema.is(BrokenTasksFound);
9
+
10
+ const discoverTasks = Effect.gen(function* () {
11
+ const shutdown = yield* StopState;
12
+ const discovery = yield* findTasksReachableFromRootTaskFile().pipe(
13
+ Effect.catchTag("BrokenTasksFound", (brokenTasks) => Effect.succeed(brokenTasks)),
14
+ Effect.raceFirst(shutdown.awaitFailure.pipe(Effect.as(null))),
15
+ );
16
+ return isBrokenTasksFound(discovery)
17
+ ? { tasksByName: null, brokenTasks: discovery }
18
+ : { tasksByName: discovery, brokenTasks: null };
19
+ });
20
+
21
+ export class DiscoveredTasks extends Context.Service<
22
+ DiscoveredTasks,
23
+ Effect.Success<typeof discoverTasks>
24
+ >()(`${TASK_PACKAGE.name}/DiscoveredTasks`) {}
25
+
26
+ export const DiscoveredTasksLive = Layer.effect(DiscoveredTasks, discoverTasks);
@@ -0,0 +1,63 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+
3
+ import { makeProcessReportClaimer } from "../../../shared/claim-processes-to-report.ts";
4
+ import { TASK_PACKAGE } from "../../../shared/task-package.ts";
5
+ import {
6
+ forgetEndedProcessGroups,
7
+ killAllProcessGroups,
8
+ killTaskProcessGroups,
9
+ recordLeaderExit,
10
+ startedProcessGroup,
11
+ } from "../../kill-leftover-processes/index.ts";
12
+ import {
13
+ processGroupsSharedWithTheSupervisor,
14
+ shareProcessGroups,
15
+ } from "../../supervise-the-run/index.ts";
16
+ import { RunRootTaskConfig } from "./provide-run-root-task-config.ts";
17
+
18
+ const makeProcessGroups = Effect.gen(function* () {
19
+ const { ownsSession, runMarker, runProcessId } = yield* RunRootTaskConfig;
20
+ const supervisorAdoptsOrphans = ownsSession && process.platform === "linux";
21
+ const startedProcessGroups = ownsSession
22
+ ? processGroupsSharedWithTheSupervisor
23
+ : new Map<number, ReturnType<typeof startedProcessGroup>>();
24
+ const shareWithTheSupervisor = (processGroupIds: Iterable<number>) =>
25
+ ownsSession ? shareProcessGroups(processGroupIds) : Effect.void;
26
+ const forget = (endedProcessGroupIds: ReadonlySet<number>) =>
27
+ forgetEndedProcessGroups(startedProcessGroups)(endedProcessGroupIds).pipe(
28
+ Effect.andThen(shareWithTheSupervisor(endedProcessGroupIds)),
29
+ );
30
+ const { claimProcessesToReport } = makeProcessReportClaimer();
31
+ return {
32
+ claimProcessesToReport,
33
+ record: (processId: number, taskName: string, leaderStartTime: string | null) =>
34
+ Effect.sync(() => {
35
+ startedProcessGroups.set(processId, startedProcessGroup(taskName, leaderStartTime, null));
36
+ }).pipe(Effect.andThen(shareWithTheSupervisor([processId]))),
37
+ recordLeaderExit: (processId: number, latestMemberStartTime: string | null) =>
38
+ Effect.sync(() => {
39
+ recordLeaderExit(startedProcessGroups, processId, latestMemberStartTime);
40
+ }).pipe(Effect.andThen(shareWithTheSupervisor([processId]))),
41
+ killForTask: (taskName: string) =>
42
+ killTaskProcessGroups(startedProcessGroups, taskName, claimProcessesToReport).pipe(
43
+ Effect.flatMap(forget),
44
+ ),
45
+ killAll: (isForced: boolean) => {
46
+ const killsTheSession = isForced && ownsSession;
47
+ return killAllProcessGroups(
48
+ startedProcessGroups,
49
+ claimProcessesToReport,
50
+ killsTheSession ? process.pid : null,
51
+ runMarker,
52
+ supervisorAdoptsOrphans ? runProcessId : null,
53
+ ).pipe(Effect.flatMap(forget));
54
+ },
55
+ };
56
+ });
57
+
58
+ export class ProcessGroups extends Context.Service<
59
+ ProcessGroups,
60
+ Effect.Success<typeof makeProcessGroups>
61
+ >()(`${TASK_PACKAGE.name}/ProcessGroups`) {}
62
+
63
+ export const ProcessGroupsLive = Layer.effect(ProcessGroups, makeProcessGroups);
@@ -0,0 +1,66 @@
1
+ import { Clock, Context, Effect, FileSystem, Layer } from "effect";
2
+
3
+ import { setProjectDirectoryPath } from "../../../shared/find-user-locations-in-stack-traces.ts";
4
+ import {
5
+ readSetupLimit,
6
+ passStateDirectoryToCommands,
7
+ readStateDirectory,
8
+ readWorkerCount,
9
+ requireSupportedBunVersion,
10
+ inheritedRunMarkers,
11
+ workingDirectoryPath,
12
+ } from "../../../shared/read-environment-config.ts";
13
+ import { TASK_PACKAGE } from "../../../shared/task-package.ts";
14
+ import { createRunMarker } from "../../kill-leftover-processes/index.ts";
15
+ import { readSupervisorRunMarker } from "../../supervise-the-run/index.ts";
16
+ import type { Task } from "../../task/index.ts";
17
+ import { requirePackageJsonDirectoryPath } from "./find-package-directories.ts";
18
+ import { RootTaskFileUnavailable } from "./main-thread-errors.ts";
19
+ import { readPackageJson } from "./read-package-json.ts";
20
+
21
+ const readRunRootTaskConfig = Effect.fn("readRunRootTaskConfig")(function* (
22
+ rootTask: Task,
23
+ supervisorProcessId: number | null,
24
+ ) {
25
+ yield* requireSupportedBunVersion(Bun.version);
26
+ const fileSystem = yield* FileSystem.FileSystem;
27
+ const runStartedAtMilliseconds = yield* Clock.currentTimeMillis;
28
+ const rootTaskFilePath = yield* fileSystem
29
+ .realPath(Bun.main)
30
+ .pipe(Effect.mapError((cause) => new RootTaskFileUnavailable({ filePath: Bun.main, cause })));
31
+ const rootTaskPackageJsonDirectoryPath = yield* requirePackageJsonDirectoryPath(rootTaskFilePath);
32
+ yield* setProjectDirectoryPath(rootTaskPackageJsonDirectoryPath);
33
+ const rootTaskPackageJsonName = (yield* readPackageJson(rootTaskPackageJsonDirectoryPath)).name;
34
+
35
+ const { stateDirectoryPath, configuredStateDirectoryPath } = yield* readStateDirectory();
36
+ yield* passStateDirectoryToCommands(configuredStateDirectoryPath);
37
+ const workerCount = yield* readWorkerCount();
38
+ const setupLimit = yield* readSetupLimit();
39
+ const runMarker =
40
+ supervisorProcessId === null
41
+ ? yield* createRunMarker(yield* inheritedRunMarkers)
42
+ : yield* readSupervisorRunMarker;
43
+ return {
44
+ rootTask,
45
+ workerCount,
46
+ setupLimit,
47
+ stateDirectoryPath,
48
+ configuredStateDirectoryPath,
49
+ rootTaskFilePath,
50
+ rootTaskPackageJsonDirectoryPath,
51
+ rootTaskPackageJsonName,
52
+ workingDirectoryPathAtStart: yield* workingDirectoryPath,
53
+ runStartedAtMilliseconds,
54
+ runProcessId: supervisorProcessId ?? process.pid,
55
+ ownsSession: supervisorProcessId !== null,
56
+ runMarker,
57
+ };
58
+ });
59
+
60
+ export class RunRootTaskConfig extends Context.Service<
61
+ RunRootTaskConfig,
62
+ Effect.Success<ReturnType<typeof readRunRootTaskConfig>>
63
+ >()(`${TASK_PACKAGE.name}/RunRootTaskConfig`) {}
64
+
65
+ export const RunRootTaskConfigLive = (rootTask: Task, supervisorProcessId: number | null) =>
66
+ Layer.effect(RunRootTaskConfig, readRunRootTaskConfig(rootTask, supervisorProcessId));