@livestore/utils-dev 0.4.0-dev.7 → 0.4.0-dev.9

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 (68) hide show
  1. package/dist/.tsbuildinfo.json +1 -1
  2. package/dist/node/DockerComposeService/DockerComposeService.d.ts +12 -2
  3. package/dist/node/DockerComposeService/DockerComposeService.d.ts.map +1 -1
  4. package/dist/node/DockerComposeService/DockerComposeService.js +54 -17
  5. package/dist/node/DockerComposeService/DockerComposeService.js.map +1 -1
  6. package/dist/node/mod.d.ts +0 -2
  7. package/dist/node/mod.d.ts.map +1 -1
  8. package/dist/node/mod.js +0 -2
  9. package/dist/node/mod.js.map +1 -1
  10. package/dist/node-vitest/Vitest.d.ts +6 -6
  11. package/dist/node-vitest/Vitest.d.ts.map +1 -1
  12. package/dist/node-vitest/Vitest.js +2 -2
  13. package/dist/node-vitest/Vitest.js.map +1 -1
  14. package/dist/node-vitest/Vitest.test.js +12 -1
  15. package/dist/node-vitest/Vitest.test.js.map +1 -1
  16. package/dist/{node/WranglerDevServer → wrangler}/WranglerDevServer.d.ts +6 -6
  17. package/dist/wrangler/WranglerDevServer.d.ts.map +1 -0
  18. package/dist/wrangler/WranglerDevServer.js +90 -0
  19. package/dist/wrangler/WranglerDevServer.js.map +1 -0
  20. package/dist/wrangler/WranglerDevServer.test.d.ts.map +1 -0
  21. package/dist/wrangler/WranglerDevServer.test.js +77 -0
  22. package/dist/wrangler/WranglerDevServer.test.js.map +1 -0
  23. package/dist/wrangler/fixtures/cf-worker.d.ts.map +1 -0
  24. package/dist/wrangler/fixtures/cf-worker.js.map +1 -0
  25. package/dist/wrangler/mod.d.ts +2 -0
  26. package/dist/wrangler/mod.d.ts.map +1 -0
  27. package/dist/wrangler/mod.js +2 -0
  28. package/dist/wrangler/mod.js.map +1 -0
  29. package/package.json +7 -4
  30. package/src/node/DockerComposeService/DockerComposeService.ts +99 -23
  31. package/src/node/mod.ts +0 -7
  32. package/src/node-vitest/Vitest.test.ts +12 -1
  33. package/src/node-vitest/Vitest.ts +31 -19
  34. package/src/wrangler/WranglerDevServer.test.ts +133 -0
  35. package/src/wrangler/WranglerDevServer.ts +180 -0
  36. package/src/wrangler/mod.ts +6 -0
  37. package/dist/node/WranglerDevServer/WranglerDevServer.d.ts.map +0 -1
  38. package/dist/node/WranglerDevServer/WranglerDevServer.js +0 -122
  39. package/dist/node/WranglerDevServer/WranglerDevServer.js.map +0 -1
  40. package/dist/node/WranglerDevServer/WranglerDevServer.test.d.ts.map +0 -1
  41. package/dist/node/WranglerDevServer/WranglerDevServer.test.js +0 -179
  42. package/dist/node/WranglerDevServer/WranglerDevServer.test.js.map +0 -1
  43. package/dist/node/WranglerDevServer/fixtures/cf-worker.d.ts.map +0 -1
  44. package/dist/node/WranglerDevServer/fixtures/cf-worker.js.map +0 -1
  45. package/dist/node/WranglerDevServer/process-tree-manager.d.ts +0 -55
  46. package/dist/node/WranglerDevServer/process-tree-manager.d.ts.map +0 -1
  47. package/dist/node/WranglerDevServer/process-tree-manager.js +0 -178
  48. package/dist/node/WranglerDevServer/process-tree-manager.js.map +0 -1
  49. package/dist/node/vitest-docker-compose-setup.d.ts +0 -32
  50. package/dist/node/vitest-docker-compose-setup.d.ts.map +0 -1
  51. package/dist/node/vitest-docker-compose-setup.js +0 -131
  52. package/dist/node/vitest-docker-compose-setup.js.map +0 -1
  53. package/dist/node/vitest-wrangler-setup.d.ts +0 -27
  54. package/dist/node/vitest-wrangler-setup.d.ts.map +0 -1
  55. package/dist/node/vitest-wrangler-setup.js +0 -96
  56. package/dist/node/vitest-wrangler-setup.js.map +0 -1
  57. package/dist/node-vitest/polyfill.d.ts +0 -2
  58. package/dist/node-vitest/polyfill.d.ts.map +0 -1
  59. package/dist/node-vitest/polyfill.js +0 -3
  60. package/dist/node-vitest/polyfill.js.map +0 -1
  61. package/src/node/WranglerDevServer/WranglerDevServer.test.ts +0 -266
  62. package/src/node/WranglerDevServer/WranglerDevServer.ts +0 -266
  63. package/src/node/WranglerDevServer/process-tree-manager.ts +0 -263
  64. /package/dist/{node/WranglerDevServer → wrangler}/WranglerDevServer.test.d.ts +0 -0
  65. /package/dist/{node/WranglerDevServer → wrangler}/fixtures/cf-worker.d.ts +0 -0
  66. /package/dist/{node/WranglerDevServer → wrangler}/fixtures/cf-worker.js +0 -0
  67. /package/src/{node/WranglerDevServer → wrangler}/fixtures/cf-worker.ts +0 -0
  68. /package/src/{node/WranglerDevServer → wrangler}/fixtures/wrangler.toml +0 -0
@@ -1,263 +0,0 @@
1
- import { Command, type CommandExecutor, Effect, Schema, type Scope, Stream } from '@livestore/utils/effect'
2
-
3
- export class ProcessTreeError extends Schema.TaggedError<ProcessTreeError>()('ProcessTreeError', {
4
- cause: Schema.Unknown,
5
- message: Schema.String,
6
- pid: Schema.Number,
7
- }) {}
8
-
9
- /**
10
- * Finds all child processes of a given parent PID
11
- */
12
- export const findChildProcesses = (
13
- parentPid: number,
14
- ): Effect.Effect<number[], never, CommandExecutor.CommandExecutor | Scope.Scope> =>
15
- Effect.gen(function* () {
16
- const result = yield* Command.make('ps', '-o', 'pid,ppid', '-ax').pipe(
17
- Command.start,
18
- Effect.flatMap((command) =>
19
- command.stdout.pipe(
20
- Stream.decodeText('utf8'),
21
- Stream.runCollect,
22
- Effect.map((chunks) => Array.from(chunks).join('')),
23
- ),
24
- ),
25
- Effect.catchAll(() => Effect.succeed('')), // Return empty string if command fails
26
- )
27
-
28
- if (!result) return []
29
-
30
- const lines = result.split('\n')
31
- const pattern = new RegExp(`^\\s*([0-9]+)\\s+${parentPid}\\s*$`)
32
-
33
- const childPids = lines
34
- .map((line) => {
35
- const match = line.trim().match(pattern)
36
- return match ? Number.parseInt(match[1]!, 10) : null
37
- })
38
- .filter((pid): pid is number => pid !== null)
39
-
40
- return childPids
41
- })
42
-
43
- /**
44
- * Recursively finds all descendants of a process
45
- */
46
- export const findProcessTree = (
47
- rootPid: number,
48
- ): Effect.Effect<number[], never, CommandExecutor.CommandExecutor | Scope.Scope> =>
49
- Effect.gen(function* () {
50
- const allPids = new Set<number>([rootPid])
51
- const toProcess = [rootPid]
52
-
53
- while (toProcess.length > 0) {
54
- const currentPid = toProcess.pop()!
55
- const children = yield* findChildProcesses(currentPid)
56
-
57
- for (const childPid of children) {
58
- if (!allPids.has(childPid)) {
59
- allPids.add(childPid)
60
- toProcess.push(childPid)
61
- }
62
- }
63
- }
64
-
65
- return Array.from(allPids)
66
- })
67
-
68
- /**
69
- * Checks if a process is running
70
- */
71
- export const isProcessRunning = (pid: number): Effect.Effect<boolean, never, never> =>
72
- Effect.sync(() => {
73
- try {
74
- process.kill(pid, 0)
75
- return true
76
- } catch {
77
- return false
78
- }
79
- })
80
-
81
- /**
82
- * Kills a process tree with escalating signals
83
- */
84
- export const killProcessTree = (
85
- rootPid: number,
86
- options: {
87
- timeout?: number
88
- signals?: NodeJS.Signals[]
89
- includeRoot?: boolean
90
- } = {},
91
- ): Effect.Effect<
92
- { killedPids: number[]; failedPids: number[] },
93
- never,
94
- CommandExecutor.CommandExecutor | Scope.Scope
95
- > =>
96
- Effect.gen(function* () {
97
- const { timeout = 5000, signals = ['SIGTERM', 'SIGKILL'], includeRoot = true } = options
98
-
99
- // Find all processes in the tree
100
- const allPids = yield* findProcessTree(rootPid)
101
- const pidsToKill = includeRoot ? allPids : allPids.filter((pid) => pid !== rootPid)
102
-
103
- if (pidsToKill.length === 0) {
104
- return { killedPids: [], failedPids: [] }
105
- }
106
-
107
- const killedPids: number[] = []
108
- const failedPids: number[] = []
109
-
110
- // Try each signal with timeout
111
- for (const signal of signals) {
112
- // Check which processes are still running and not yet killed
113
- const stillRunningChecks = yield* Effect.all(
114
- pidsToKill
115
- .filter((pid) => !killedPids.includes(pid))
116
- .map((pid) => isProcessRunning(pid).pipe(Effect.map((running) => ({ pid, running })))),
117
- )
118
- const remainingPids = stillRunningChecks.filter(({ running }) => running).map(({ pid }) => pid)
119
-
120
- if (remainingPids.length === 0) break
121
-
122
- // Send signal to all remaining processes
123
- for (const pid of remainingPids) {
124
- yield* Effect.sync(() => {
125
- try {
126
- process.kill(pid, signal)
127
- } catch {
128
- // Process might already be dead, continue
129
- }
130
- })
131
- }
132
-
133
- // Wait for processes to terminate with polling
134
- const waitStart = Date.now()
135
- while (Date.now() - waitStart < timeout) {
136
- const runningChecks = yield* Effect.all(
137
- remainingPids.map((pid) => isProcessRunning(pid).pipe(Effect.map((running) => ({ pid, running })))),
138
- )
139
- const stillRunning = runningChecks.filter(({ running }) => running).map(({ pid }) => pid)
140
-
141
- if (stillRunning.length === 0) {
142
- // All processes terminated
143
- killedPids.push(...remainingPids)
144
- break
145
- }
146
-
147
- // Short sleep before checking again
148
- yield* Effect.sleep('100 millis')
149
- }
150
- }
151
-
152
- // Check final status
153
- const finalChecks = yield* Effect.all(
154
- pidsToKill.map((pid) => isProcessRunning(pid).pipe(Effect.map((running) => ({ pid, running })))),
155
- )
156
-
157
- for (const { pid, running } of finalChecks) {
158
- if (!killedPids.includes(pid) && running) {
159
- failedPids.push(pid)
160
- }
161
- }
162
-
163
- return { killedPids, failedPids }
164
- })
165
-
166
- /**
167
- * Finds orphaned processes by name pattern
168
- */
169
- export const findOrphanedProcesses = (
170
- namePattern: string,
171
- ): Effect.Effect<number[], never, CommandExecutor.CommandExecutor | Scope.Scope> =>
172
- Effect.gen(function* () {
173
- // Find processes that match the pattern and have init (PID 1) as parent
174
- const result = yield* Command.make('ps', '-eo', 'pid,ppid,comm').pipe(
175
- Command.start,
176
- Effect.flatMap((command) =>
177
- command.stdout.pipe(
178
- Stream.decodeText('utf8'),
179
- Stream.runCollect,
180
- Effect.map((chunks) => Array.from(chunks).join('')),
181
- ),
182
- ),
183
- Effect.catchAll(() => Effect.succeed('')), // Return empty string if command fails
184
- )
185
-
186
- if (!result) return []
187
-
188
- const lines = result.split('\n')
189
- const patternRegex = new RegExp(namePattern)
190
- const parentRegex = /^\s*(\d+)\s+1\s+/
191
-
192
- const orphanedPids = lines
193
- .filter((line) => patternRegex.test(line))
194
- .map((line) => {
195
- const match = line.trim().match(parentRegex)
196
- return match ? Number.parseInt(match[1]!, 10) : null
197
- })
198
- .filter((pid): pid is number => pid !== null)
199
-
200
- return orphanedPids
201
- })
202
-
203
- /**
204
- * Defensive cleanup for orphaned processes matching given patterns.
205
- *
206
- * This function provides fallback cleanup for edge cases where normal process
207
- * termination mechanisms fail (e.g., hard crashes, SIGKILL before cleanup runs,
208
- * or limitations in synchronous exit handlers). While proper process tree cleanup
209
- * should prevent orphans in most cases, this serves as a safety net for scenarios
210
- * where child processes become orphaned despite cleanup efforts.
211
- *
212
- * @param processPatterns - Array of process name patterns to search for (e.g., ['wrangler', 'workerd'])
213
- * @returns Object with arrays of successfully cleaned and failed PIDs
214
- */
215
- export const cleanupOrphanedProcesses = (
216
- processPatterns: string[],
217
- ): Effect.Effect<{ cleaned: number[]; failed: number[] }, never, CommandExecutor.CommandExecutor | Scope.Scope> =>
218
- Effect.gen(function* () {
219
- const cleaned: number[] = []
220
- const failed: number[] = []
221
-
222
- // Find all orphaned processes matching the patterns
223
- const allOrphanedPids: number[] = []
224
- const patternCounts: Record<string, number> = {}
225
-
226
- for (const pattern of processPatterns) {
227
- const orphaned = yield* findOrphanedProcesses(pattern)
228
- allOrphanedPids.push(...orphaned)
229
- patternCounts[pattern] = orphaned.length
230
- }
231
-
232
- if (allOrphanedPids.length === 0) {
233
- return { cleaned, failed }
234
- }
235
-
236
- const patternSummary = Object.entries(patternCounts)
237
- .map(([pattern, count]) => `${count} ${pattern}`)
238
- .join(', ')
239
-
240
- yield* Effect.logInfo(
241
- `Found ${allOrphanedPids.length} orphaned processes (${patternSummary}): ${allOrphanedPids.join(', ')}`,
242
- )
243
-
244
- for (const pid of allOrphanedPids) {
245
- const result = yield* killProcessTree(pid, {
246
- timeout: 2000,
247
- signals: ['SIGTERM', 'SIGKILL'],
248
- includeRoot: true,
249
- }).pipe(Effect.orElse(() => Effect.succeed({ killedPids: [], failedPids: [pid] })))
250
-
251
- if (result.failedPids.length === 0) {
252
- cleaned.push(...result.killedPids)
253
- yield* Effect.logInfo(
254
- `Cleaned up orphaned process tree starting with ${pid} (${result.killedPids.length} processes)`,
255
- )
256
- } else {
257
- failed.push(pid, ...result.failedPids)
258
- yield* Effect.logWarning(`Failed to clean up some processes in tree ${pid}: ${result.failedPids.join(', ')}`)
259
- }
260
- }
261
-
262
- return { cleaned, failed }
263
- })