goke 6.7.0 → 6.9.0

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/src/just-bash.ts CHANGED
@@ -26,17 +26,90 @@ interface JustBashCommand {
26
26
  execute(args: string[], context?: JustBashExecutionContext): Promise<JustBashExecResult>
27
27
  }
28
28
 
29
- type JustBashExecutionContext = Pick<CommandContext, 'cwd' | 'env' | 'fs' | 'stdin'>
30
- type JustBashEncoding = 'utf8' | 'utf-8' | 'ascii' | 'binary' | 'base64' | 'hex' | 'latin1'
29
+ type JustBashExecutionContext = Pick<CommandContext, 'cwd' | 'env' | 'fs' | 'stdin' | 'limits'>
30
+
31
+ const TRUNCATION_MESSAGE = '\n[output truncated]\n'
32
+
33
+ function createTextCaptureStreams(maxLength?: number): {
34
+ stdout: GokeOutputStream
35
+ stderr: GokeOutputStream
36
+ getResult(): { stdout: string; stderr: string }
37
+ } {
38
+ const stdoutChunks: string[] = []
39
+ const stderrChunks: string[] = []
40
+ const limit = maxLength != null && maxLength > 0 ? maxLength : Number.POSITIVE_INFINITY
41
+ let totalLength = 0
42
+ let stdoutTruncated = false
43
+ let stderrTruncated = false
44
+
45
+ const createStream = (stream: 'stdout' | 'stderr'): GokeOutputStream => ({
46
+ write(data: string) {
47
+ if (totalLength >= limit) {
48
+ if (stream === 'stdout') {
49
+ stdoutTruncated = true
50
+ } else {
51
+ stderrTruncated = true
52
+ }
53
+ return
54
+ }
31
55
 
32
- function createTextCaptureStream(): GokeOutputStream & { readonly text: string } {
33
- const chunks: string[] = []
34
- return {
35
- get text() {
36
- return chunks.join('')
56
+ const remaining = limit - totalLength
57
+ const text = data.length <= remaining ? data : data.slice(0, remaining)
58
+ if (stream === 'stdout') {
59
+ stdoutChunks.push(text)
60
+ stdoutTruncated ||= text.length !== data.length
61
+ } else {
62
+ stderrChunks.push(text)
63
+ stderrTruncated ||= text.length !== data.length
64
+ }
65
+ totalLength += text.length
37
66
  },
38
- write(data: string) {
39
- chunks.push(data)
67
+ })
68
+
69
+ const trimEnd = (value: string, count: number) => value.slice(0, Math.max(0, value.length - count))
70
+
71
+ return {
72
+ stdout: createStream('stdout'),
73
+ stderr: createStream('stderr'),
74
+ getResult() {
75
+ let stdout = stdoutChunks.join('')
76
+ let stderr = stderrChunks.join('')
77
+
78
+ if (!stdoutTruncated && !stderrTruncated) {
79
+ return { stdout, stderr }
80
+ }
81
+
82
+ const target = stderrTruncated ? 'stderr' : 'stdout'
83
+ const message = limit === Number.POSITIVE_INFINITY
84
+ ? TRUNCATION_MESSAGE
85
+ : TRUNCATION_MESSAGE.slice(0, Math.min(TRUNCATION_MESSAGE.length, limit))
86
+
87
+ let overflow = stdout.length + stderr.length + message.length - limit
88
+ if (Number.isFinite(limit) && overflow > 0) {
89
+ if (target === 'stderr') {
90
+ const stderrTrim = Math.min(overflow, stderr.length)
91
+ stderr = trimEnd(stderr, stderrTrim)
92
+ overflow -= stderrTrim
93
+ if (overflow > 0) {
94
+ stdout = trimEnd(stdout, overflow)
95
+ }
96
+ } else {
97
+ const stdoutTrim = Math.min(overflow, stdout.length)
98
+ stdout = trimEnd(stdout, stdoutTrim)
99
+ overflow -= stdoutTrim
100
+ if (overflow > 0) {
101
+ stderr = trimEnd(stderr, overflow)
102
+ }
103
+ }
104
+ }
105
+
106
+ if (target === 'stderr') {
107
+ stderr += message
108
+ } else {
109
+ stdout += message
110
+ }
111
+
112
+ return { stdout, stderr }
40
113
  },
41
114
  }
42
115
  }
@@ -55,7 +128,7 @@ const getEncoding = (options?: GokeFsEncodingOption) => {
55
128
  return options.encoding
56
129
  }
57
130
 
58
- const toJustBashEncoding = (encoding?: BufferEncoding | null): JustBashEncoding | null | undefined => {
131
+ const toJustBashEncoding = (encoding?: BufferEncoding | null): 'utf8' | 'utf-8' | 'ascii' | 'binary' | 'base64' | 'hex' | 'latin1' | null | undefined => {
59
132
  if (encoding == null) {
60
133
  return encoding
61
134
  }
@@ -232,16 +305,15 @@ export function createJustBashCommand(
232
305
  name,
233
306
  trusted: true,
234
307
  async execute(args: string[], context?: JustBashExecutionContext) {
235
- const stdout = createTextCaptureStream()
236
- const stderr = createTextCaptureStream()
308
+ const output = createTextCaptureStreams(context?.limits?.maxOutputSize)
237
309
  const argv = ['node', name, ...args]
238
310
  const cloned = cli.clone({
239
311
  cwd: context?.cwd,
240
312
  env: context ? createJustBashEnvProxy(context.env) : cli.env,
241
313
  fs: context ? createJustBashFs(context.fs, context.cwd) : cli.fs,
242
314
  stdin: context?.stdin,
243
- stdout,
244
- stderr,
315
+ stdout: output.stdout,
316
+ stderr: output.stderr,
245
317
  argv,
246
318
  exit: (code) => {
247
319
  throw new GokeProcessExit(code)
@@ -253,16 +325,18 @@ export function createJustBashCommand(
253
325
  try {
254
326
  cloned.parse(argv, { run: false })
255
327
  await cloned.runMatchedCommand()
328
+ const result = output.getResult()
256
329
  return {
257
- stdout: stdout.text,
258
- stderr: stderr.text,
330
+ stdout: result.stdout,
331
+ stderr: result.stderr,
259
332
  exitCode: 0,
260
333
  }
261
334
  } catch (error) {
262
335
  if (error instanceof GokeProcessExit) {
336
+ const result = output.getResult()
263
337
  return {
264
- stdout: stdout.text,
265
- stderr: stderr.text,
338
+ stdout: result.stdout,
339
+ stderr: result.stderr,
266
340
  exitCode: error.code,
267
341
  }
268
342
  }