@typed-assistant/builder 0.0.87 → 0.0.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/package.json +3 -3
- package/src/appProcess.tsx +37 -32
- package/src/setupGitPoller.tsx +8 -7
- package/src/setupWebserver.tsx +21 -43
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @typed-assistant/builder
|
|
2
2
|
|
|
3
|
+
## 0.0.89
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- More robust restarting of app when process error occurs.
|
|
8
|
+
|
|
9
|
+
## 0.0.88
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Fire onProcessError when empty stream is detected in web server.
|
|
14
|
+
|
|
3
15
|
## 0.0.87
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typed-assistant/builder",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.89",
|
|
4
4
|
"exports": {
|
|
5
5
|
"./appProcess": "./src/appProcess.tsx",
|
|
6
6
|
"./bunInstall": "./src/bunInstall.tsx",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"typescript": "^5.4.0",
|
|
31
31
|
"@typed-assistant/eslint-config": "0.0.10",
|
|
32
32
|
"@typed-assistant/logger": "0.0.22",
|
|
33
|
-
"@typed-assistant/
|
|
34
|
-
"@typed-assistant/
|
|
33
|
+
"@typed-assistant/typescript-config": "0.0.11",
|
|
34
|
+
"@typed-assistant/utils": "0.0.19"
|
|
35
35
|
},
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public",
|
package/src/appProcess.tsx
CHANGED
|
@@ -47,16 +47,20 @@ export async function setup({
|
|
|
47
47
|
},
|
|
48
48
|
onProcessError: async (message) => {
|
|
49
49
|
const messageAll = `${message}. Restarting app...`
|
|
50
|
-
logger.fatal({
|
|
50
|
+
logger.fatal({ emoji: "🚨" }, messageAll)
|
|
51
51
|
onProcessError?.(messageAll, addonUrl)
|
|
52
|
-
subprocesses
|
|
52
|
+
if (subprocesses.app) await killSubprocess(subprocesses.app)
|
|
53
|
+
if (watcher) watcher.close()
|
|
54
|
+
if (processChecker) processChecker.stop()
|
|
55
|
+
if ("stop" in gitPoller) gitPoller.stop()
|
|
56
|
+
setup({
|
|
53
57
|
entryFile,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
58
|
+
mdiPaths,
|
|
59
|
+
onProcessError,
|
|
60
|
+
})
|
|
57
61
|
},
|
|
58
62
|
})
|
|
59
|
-
setupWatcher({
|
|
63
|
+
const watcher = setupWatcher({
|
|
60
64
|
directoryToWatch,
|
|
61
65
|
entryFile,
|
|
62
66
|
mdiPaths,
|
|
@@ -66,7 +70,7 @@ export async function setup({
|
|
|
66
70
|
getSubprocesses: () => subprocesses,
|
|
67
71
|
})
|
|
68
72
|
|
|
69
|
-
checkProcesses(entryFile, {
|
|
73
|
+
const processChecker = checkProcesses(entryFile, {
|
|
70
74
|
onMultiProcessError: (ps) => {
|
|
71
75
|
const message = `Multiple processes detected. Restarting addon...`
|
|
72
76
|
logger.fatal({ additionalDetails: ps, emoji: "🚨" }, message)
|
|
@@ -84,7 +88,8 @@ export async function setup({
|
|
|
84
88
|
)
|
|
85
89
|
},
|
|
86
90
|
})
|
|
87
|
-
|
|
91
|
+
|
|
92
|
+
const gitPoller = await setupGitSync({
|
|
88
93
|
onChangesPulled: async () => {
|
|
89
94
|
subprocesses = await killAndRestartApp(
|
|
90
95
|
entryFile,
|
|
@@ -133,7 +138,7 @@ async function killAndRestartApp(
|
|
|
133
138
|
|
|
134
139
|
let multipleProcessesErrorCount = 0
|
|
135
140
|
let noProcessesErrorCount = 0
|
|
136
|
-
const checkProcesses =
|
|
141
|
+
const checkProcesses = (
|
|
137
142
|
entryFile: string,
|
|
138
143
|
{
|
|
139
144
|
onMultiProcessError,
|
|
@@ -143,33 +148,33 @@ const checkProcesses = async (
|
|
|
143
148
|
onNoProcessError?: (psOutput: string) => void | Promise<void>
|
|
144
149
|
},
|
|
145
150
|
) => {
|
|
146
|
-
const
|
|
147
|
-
|
|
151
|
+
const interval = setInterval(async () => {
|
|
152
|
+
const ps = await $`ps -f`.text()
|
|
153
|
+
logger.debug({ emoji: "🔍" }, `Checking processes...\n${ps}`)
|
|
154
|
+
const matches = ps.match(new RegExp(`bun .+${entryFile}`, "gmi")) ?? []
|
|
148
155
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
156
|
+
if (matches.length > 1) {
|
|
157
|
+
multipleProcessesErrorCount++
|
|
158
|
+
if (multipleProcessesErrorCount > 5) {
|
|
159
|
+
await onMultiProcessError?.(ps)
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
multipleProcessesErrorCount = 0
|
|
154
164
|
}
|
|
155
|
-
} else {
|
|
156
|
-
multipleProcessesErrorCount = 0
|
|
157
|
-
}
|
|
158
165
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
166
|
+
if (matches.length === 0) {
|
|
167
|
+
noProcessesErrorCount++
|
|
168
|
+
if (noProcessesErrorCount > 5) {
|
|
169
|
+
await onNoProcessError?.(ps)
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
noProcessesErrorCount = 0
|
|
164
174
|
}
|
|
165
|
-
}
|
|
166
|
-
noProcessesErrorCount = 0
|
|
167
|
-
}
|
|
175
|
+
}, 10000)
|
|
168
176
|
|
|
169
|
-
|
|
170
|
-
() => checkProcesses(entryFile, { onMultiProcessError, onNoProcessError }),
|
|
171
|
-
5000,
|
|
172
|
-
)
|
|
177
|
+
return { stop: () => clearInterval(interval) }
|
|
173
178
|
}
|
|
174
179
|
|
|
175
180
|
const getAddonInfo = async () => {
|
|
@@ -200,7 +205,7 @@ const setupGitSync = async ({
|
|
|
200
205
|
}
|
|
201
206
|
|
|
202
207
|
logger.warn({ emoji: "⬇️" }, "Setting up git poller...")
|
|
203
|
-
|
|
208
|
+
return setupGitPoller({ onChangesPulled })
|
|
204
209
|
}
|
|
205
210
|
|
|
206
211
|
const ig = ignore().add(
|
package/src/setupGitPoller.tsx
CHANGED
|
@@ -11,13 +11,14 @@ export const setupGitPoller = async ({
|
|
|
11
11
|
onChangesPulled: () => void
|
|
12
12
|
}) => {
|
|
13
13
|
const duration = gitPullPollDuration ?? 30
|
|
14
|
-
await pullChanges({ onChangesPulled })
|
|
15
|
-
logger.debug(
|
|
16
|
-
{ emoji: "⬇️⏳" },
|
|
17
|
-
`Pulling changes again in ${duration} seconds...`,
|
|
18
|
-
)
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
const interval = setInterval(async () => {
|
|
16
|
+
await pullChanges({ onChangesPulled })
|
|
17
|
+
logger.debug(
|
|
18
|
+
{ emoji: "⬇️⏳" },
|
|
19
|
+
`Pulling changes again in ${duration} seconds...`,
|
|
20
|
+
)
|
|
22
21
|
}, duration * ONE_SECOND)
|
|
22
|
+
|
|
23
|
+
return { stop: () => clearInterval(interval) }
|
|
23
24
|
}
|
package/src/setupWebserver.tsx
CHANGED
|
@@ -346,7 +346,7 @@ export const startWebappServer = async ({
|
|
|
346
346
|
await server.stop()
|
|
347
347
|
})
|
|
348
348
|
|
|
349
|
-
let
|
|
349
|
+
let fatalErrorMessage = ""
|
|
350
350
|
|
|
351
351
|
// eslint-disable-next-line no-constant-condition
|
|
352
352
|
while (true) {
|
|
@@ -366,15 +366,8 @@ export const startWebappServer = async ({
|
|
|
366
366
|
stdoutResult.done &&
|
|
367
367
|
(stderrResult.done || stderrResult.value === undefined)
|
|
368
368
|
if (streamEnded) {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
emoji: "😴",
|
|
372
|
-
additionalDetails: JSON.stringify({ exitCode: app.exitCode }),
|
|
373
|
-
},
|
|
374
|
-
"Subprocess output streams ended; waiting for restart or new output",
|
|
375
|
-
)
|
|
376
|
-
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
377
|
-
continue
|
|
369
|
+
fatalErrorMessage = "Subprocess output streams ended"
|
|
370
|
+
break
|
|
378
371
|
}
|
|
379
372
|
|
|
380
373
|
const chunk = stdoutResult.value ?? stderrResult.value
|
|
@@ -382,35 +375,22 @@ export const startWebappServer = async ({
|
|
|
382
375
|
const convertedMessage = convert.toHtml(decodedString)
|
|
383
376
|
if (convertedMessage !== "") {
|
|
384
377
|
lastMessage = convertedMessage
|
|
378
|
+
subscribers.forEach((send) => send(convertedMessage))
|
|
379
|
+
} else {
|
|
380
|
+
fatalErrorMessage = "Process is returning an empty string"
|
|
381
|
+
break
|
|
385
382
|
}
|
|
386
|
-
if (convertedMessage === "") {
|
|
387
|
-
emptyStringCount += 1
|
|
388
|
-
const emptyStringMessage =
|
|
389
|
-
"Process is returning an empty string"
|
|
390
|
-
if (emptyStringCount === 10) {
|
|
391
|
-
onProcessError(emptyStringMessage)
|
|
392
|
-
}
|
|
393
|
-
subscribers.forEach((send) =>
|
|
394
|
-
send("Process is returning an empty string. This was the last non-empty message:\n\n" +
|
|
395
|
-
lastMessage),
|
|
396
|
-
)
|
|
397
|
-
logger.fatal(
|
|
398
|
-
{
|
|
399
|
-
emoji: "💀",
|
|
400
|
-
additionalDetails: JSON.stringify({
|
|
401
|
-
exitCode: getSubprocesses().app.exitCode,
|
|
402
|
-
}),
|
|
403
|
-
},
|
|
404
|
-
emptyStringMessage,
|
|
405
|
-
)
|
|
406
|
-
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
407
|
-
continue
|
|
408
|
-
}
|
|
409
|
-
emptyStringCount = 0
|
|
410
|
-
subscribers.forEach((send) => send(convertedMessage))
|
|
411
383
|
}
|
|
412
384
|
|
|
413
|
-
|
|
385
|
+
subscribers.forEach((send) =>
|
|
386
|
+
send(
|
|
387
|
+
"Fatal error occured. This was the last non-empty message:\n\n" +
|
|
388
|
+
lastMessage,
|
|
389
|
+
),
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
logger.warn({ emoji: "💀" }, fatalErrorMessage)
|
|
393
|
+
onProcessError(fatalErrorMessage)
|
|
414
394
|
}
|
|
415
395
|
|
|
416
396
|
export type WebServer = Awaited<ReturnType<typeof startWebappServer>>
|
|
@@ -428,13 +408,11 @@ const getLogsFromFile = async ({
|
|
|
428
408
|
}) => {
|
|
429
409
|
try {
|
|
430
410
|
const parsedLimit = Number(limitProp)
|
|
431
|
-
const limit =
|
|
432
|
-
? parsedLimit
|
|
433
|
-
: undefined
|
|
411
|
+
const limit =
|
|
412
|
+
Number.isFinite(parsedLimit) && parsedLimit > 0 ? parsedLimit : undefined
|
|
434
413
|
const parsedOffset = Number(offsetProp)
|
|
435
|
-
const offset =
|
|
436
|
-
? parsedOffset
|
|
437
|
-
: 0
|
|
414
|
+
const offset =
|
|
415
|
+
Number.isFinite(parsedOffset) && parsedOffset >= 0 ? parsedOffset : 0
|
|
438
416
|
|
|
439
417
|
const normalizedFilter = filter?.toLowerCase().trim()
|
|
440
418
|
|
|
@@ -450,7 +428,7 @@ const getLogsFromFile = async ({
|
|
|
450
428
|
return result.concat({
|
|
451
429
|
msg: e instanceof Error ? e.message : "Unknown parse error",
|
|
452
430
|
level: levels.fatal,
|
|
453
|
-
})
|
|
431
|
+
} as LogSchema)
|
|
454
432
|
}
|
|
455
433
|
}, [] as LogSchema[])
|
|
456
434
|
|