@poe-platform/safe-bash 0.1.745 → 0.1.747

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 (59) hide show
  1. package/README.md +5 -1
  2. package/dist/image-ast/browser/sharp.d.ts +1 -1
  3. package/dist/image-ast/index.js +133 -35
  4. package/dist/image-ast/sharp.d.ts +1 -1
  5. package/dist/safe-bash/chunks/{chunk-2F7Q532M.js → chunk-4GK4FAT6.js} +3 -3
  6. package/dist/safe-bash/chunks/{chunk-EAUCLWEZ.js → chunk-55AGFLG5.js} +540 -284
  7. package/dist/safe-bash/chunks/{chunk-WCNFCN2G.js → chunk-BS6QFRNX.js} +2 -2
  8. package/dist/safe-bash/chunks/{chunk-IZZ7KX4E.js → chunk-JCXAGOEK.js} +40 -3
  9. package/dist/safe-bash/chunks/{chunk-I3DOFJSJ.js → chunk-MUWJPSGN.js} +2 -2
  10. package/dist/safe-bash/chunks/{chunk-7RP6WOWK.js → chunk-OF2PDE3W.js} +2 -2
  11. package/dist/safe-bash/chunks/{src-FTMTU65U.js → src-T672TQJC.js} +2 -2
  12. package/dist/safe-bash/command-chunks/{chunk-DRCPJVZF.js → chunk-44N2IRVP.js} +17 -5
  13. package/dist/safe-bash/command-chunks/{chunk-LYKC7WJB.js → chunk-6742OVAC.js} +37 -3
  14. package/dist/safe-bash/command-chunks/{chunk-BMBXSLVX.js → chunk-6QU5OLPJ.js} +2 -2
  15. package/dist/safe-bash/command-chunks/{chunk-YHANNN7I.js → chunk-S3BO2NJT.js} +7 -0
  16. package/dist/safe-bash/commands/basic.d.ts +2 -0
  17. package/dist/safe-bash/commands/basic.js +236 -9
  18. package/dist/safe-bash/commands/pr/index.browser.js +1 -1
  19. package/dist/safe-bash/commands/streams.js +9 -56
  20. package/dist/safe-bash/commands/table-text/index.js +5 -1
  21. package/dist/safe-bash/commands/text.js +13 -2
  22. package/dist/safe-bash/commands/time-env/shared.js +2 -2
  23. package/dist/safe-bash/commands/time-env/sleep.js +6 -4
  24. package/dist/safe-bash/commands/timeout/index.d.ts +7 -0
  25. package/dist/safe-bash/commands/timeout/index.js +52 -32
  26. package/dist/safe-bash/commands/timeout/scheduler.js +7 -6
  27. package/dist/safe-bash/core.browser.js +2698 -543
  28. package/dist/safe-bash/opt-in/commands/shuf/random.js +2 -3
  29. package/dist/safe-bash/opt-in/shell/extensions/read/index.js +1 -1
  30. package/dist/safe-bash/opt-in/shell/extensions/trap/index.d.ts +1 -2
  31. package/dist/safe-bash/opt-in/shell/extensions/trap/index.js +1 -8
  32. package/dist/safe-bash/playwright/adapter.d.ts +2 -0
  33. package/dist/safe-bash/playwright/private-target-transport.d.ts +1 -0
  34. package/dist/safe-bash/playwright/private-target-transport.js +21 -21
  35. package/dist/safe-bash/shell/runtime.d.ts +20 -1
  36. package/dist/safe-bash/shell/runtime.js +3021 -532
  37. package/dist/safe-bash/shell/sync-jq-expression.d.ts +6 -0
  38. package/dist/safe-bash/shell/sync-jq-expression.js +46 -0
  39. package/dist/safe-bash-command-cal/index.js +32 -12
  40. package/dist/safe-bash-command-hostname/index.js +22 -7
  41. package/dist/safe-bash-command-id/index.js +28 -9
  42. package/dist/safe-bash-command-pandoc/command.js +1 -1
  43. package/dist/safe-bash-command-pandoc/index.js +1 -1
  44. package/dist/safe-bash-command-ssconvert/command.d.ts +2 -2
  45. package/dist/safe-bash-command-ssconvert/contracts.d.ts +4 -0
  46. package/dist/safe-bash-command-ssconvert/engine.d.ts +2 -2
  47. package/dist/safe-bash-command-ssconvert/index.js +524 -276
  48. package/dist/safe-bash-command-whoami/index.js +26 -12
  49. package/dist/safe-bash-query-engine/index.js +3 -3
  50. package/dist/safe-bash-query-engine/interpreter.js +1 -1
  51. package/dist/safe-bash-query-engine/parser.js +1 -1
  52. package/dist/safe-bash-query-engine/query-core.js +3 -3
  53. package/dist/safe-playwright-cloudflare/browser-private-transport.js +8 -13
  54. package/dist/safe-playwright-cloudflare/browser-storage-control.js +13 -4
  55. package/dist/safe-playwright-cloudflare/browser-trace-archive.js +1 -1
  56. package/package.json +18 -14
  57. package/dist/image-ast/streams/node.d.ts +0 -10
  58. package/dist/image-ast/streams/node.js +0 -5
  59. package/dist/image-ast/streams/web.js +0 -94
@@ -2363,6 +2363,7 @@ async function executeUniqGeneral(context, preReadSource) {
2363
2363
  const onlyRepeated = parsed.flags.has("d");
2364
2364
  const onlyUnique = parsed.flags.has("u");
2365
2365
  const identityKey = skipFields === 0 && skipCharacters === 0 && width === Infinity && !ignoreCase;
2366
+ const uniqDecoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
2366
2367
  const key = (bytes) => {
2367
2368
  if (identityKey)
2368
2369
  return bytes;
@@ -2373,8 +2374,18 @@ async function executeUniqGeneral(context, preReadSource) {
2373
2374
  while (offset < bytes.length && bytes[offset] !== 32 && bytes[offset] !== 9)
2374
2375
  offset++;
2375
2376
  }
2376
- offset += skipCharacters;
2377
- const result = bytes.subarray(offset, width === Infinity ? undefined : offset + width);
2377
+ let result;
2378
+ try {
2379
+ const chars = Array.from(uniqDecoder.decode(bytes.subarray(offset)));
2380
+ result = encoder.encode((width === Infinity ? chars.slice(skipCharacters) : chars.slice(skipCharacters, skipCharacters + width)).join(""));
2381
+ }
2382
+ catch (error) {
2383
+ if (!(error instanceof TypeError))
2384
+ throw error;
2385
+ // Invalid UTF-8 records retain byte comparisons without replacement characters.
2386
+ offset += skipCharacters;
2387
+ result = bytes.subarray(offset, width === Infinity ? undefined : offset + width);
2388
+ }
2378
2389
  return ignoreCase ? fold(result) : result;
2379
2390
  };
2380
2391
  const records = (async function* () {
@@ -8,8 +8,8 @@ export function settings(options) {
8
8
  throw new RangeError(`Invalid time-env limit: ${name}`);
9
9
  }
10
10
  const maxTimerMilliseconds = options.maxTimerMilliseconds ?? 2147483647;
11
- if (!Number.isInteger(maxTimerMilliseconds) || maxTimerMilliseconds < 1 || maxTimerMilliseconds > 2147483647) {
12
- throw new RangeError("maxTimerMilliseconds must be between1 and2147483647");
11
+ if (maxTimerMilliseconds !== Infinity && (!Number.isInteger(maxTimerMilliseconds) || maxTimerMilliseconds < 1 || maxTimerMilliseconds > 2147483647)) {
12
+ throw new RangeError("maxTimerMilliseconds must be between 1 and 2147483647 or Infinity");
13
13
  }
14
14
  const scheduler = options.scheduler ?? {
15
15
  now: () => performance.now(),
@@ -96,7 +96,8 @@ function delay(milliseconds, signal, configuration) {
96
96
  let handle;
97
97
  let armed = false;
98
98
  let settled = false;
99
- let started;
99
+ let remaining = milliseconds;
100
+ let step = 0;
100
101
  let previous;
101
102
  const finish = (failed, reason) => {
102
103
  if (settled)
@@ -128,14 +129,15 @@ function delay(milliseconds, signal, configuration) {
128
129
  if (!Number.isFinite(now) || Math.abs(now) > Number.MAX_SAFE_INTEGER || (previous !== undefined && now < previous)) {
129
130
  throw new RangeError("sleep scheduler must supply finite monotonic milliseconds");
130
131
  }
131
- started ??= now;
132
+ if (previous !== undefined)
133
+ remaining -= Math.max(step, now - previous);
132
134
  previous = now;
133
- const remaining = milliseconds - (now - started);
134
135
  if (remaining <= 0) {
135
136
  finish(false);
136
137
  return;
137
138
  }
138
- const timer = scheduler.setTimeout(() => { armed = false; schedule(); }, Math.min(configuration.maxTimerMilliseconds, Math.max(1, Math.ceil(remaining))));
139
+ step = Math.min(2147483647, configuration.maxTimerMilliseconds, Math.max(1, Math.ceil(remaining)));
140
+ const timer = scheduler.setTimeout(() => { armed = false; schedule(); }, step);
139
141
  if (settled)
140
142
  scheduler.clearTimeout(timer);
141
143
  else {
@@ -4,7 +4,14 @@ export interface TimeoutScheduler {
4
4
  setTimeout(callback: () => void, milliseconds: number): unknown;
5
5
  clearTimeout(handle: unknown): void;
6
6
  }
7
+ export interface TimeoutLimits {
8
+ readonly maxArguments: number;
9
+ readonly maxArgumentBytes: number;
10
+ /** Bytes emitted by timeout itself; child output uses the child's budgets. */
11
+ readonly maxOutputBytes: number;
12
+ }
7
13
  export interface TimeoutCommandOptions {
14
+ readonly limits?: Partial<TimeoutLimits> | undefined;
8
15
  /** Trusted host binding responsible for signalling, hard escalation and child cleanup. */
9
16
  readonly killAfterPolicy?: KillAfterPolicy | undefined;
10
17
  readonly invoke?: CommandInvoker | undefined;
@@ -1,4 +1,5 @@
1
- import { getCommandArguments, writeBytes } from "../../contracts/index.js";
1
+ import { FsError, getCommandArguments, writeBytes } from "../../contracts/index.js";
2
+ import { shellValueByteLength } from "../../contracts/value.js";
2
3
  import { parseDuration } from "./duration.js";
3
4
  import { parseSignal } from "./signal.js";
4
5
  import { createDeadline, defaultSchedulerBinding } from "./scheduler.js";
@@ -25,6 +26,14 @@ function optionsObject(value) {
25
26
  }
26
27
  function settings(value, includeReplace) {
27
28
  const options = optionsObject(value);
29
+ const configuredLimits = optionsObject(options?.limits);
30
+ const limits = {
31
+ maxArguments: Infinity, maxArgumentBytes: Infinity, maxOutputBytes: Infinity, ...configuredLimits,
32
+ };
33
+ for (const [name, limit] of Object.entries(limits)) {
34
+ if (limit !== Infinity && (!Number.isSafeInteger(limit) || limit < 1))
35
+ throw new RangeError(`Invalid timeout limit: ${name}`);
36
+ }
28
37
  const killAfterPolicy = options?.killAfterPolicy;
29
38
  if (killAfterPolicy !== undefined && typeof killAfterPolicy !== "function")
30
39
  throw new TypeError("Timeout killAfterPolicy must be a function");
@@ -54,8 +63,8 @@ function settings(value, includeReplace) {
54
63
  const maximum = options?.maxTimerMilliseconds;
55
64
  if (maximum !== undefined && typeof maximum !== "number")
56
65
  throw new TypeError("Timeout maxTimerMilliseconds must be a number");
57
- if (maximum !== undefined && (!Number.isInteger(maximum) || maximum < 1 || maximum > 2147483647)) {
58
- throw new RangeError("Timeout maxTimerMilliseconds must be an integer from 1 through 2147483647");
66
+ if (maximum !== undefined && maximum !== Infinity && (!Number.isInteger(maximum) || maximum < 1 || maximum > 2147483647)) {
67
+ throw new RangeError("Timeout maxTimerMilliseconds must be an integer from 1 through 2147483647 or Infinity");
59
68
  }
60
69
  let replace = false;
61
70
  if (includeReplace) {
@@ -64,9 +73,11 @@ function settings(value, includeReplace) {
64
73
  throw new TypeError("Timeout replace must be a boolean");
65
74
  replace = configured ?? false;
66
75
  }
67
- return { invoke, killAfterPolicy: killAfterPolicy, scheduler: binding, maxTimerMilliseconds: maximum ?? 2147483647, replace };
76
+ return { limits, invoke, killAfterPolicy: killAfterPolicy, scheduler: binding, maxTimerMilliseconds: maximum ?? 2147483647, replace };
68
77
  }
69
- async function status(context, bytes, exitCode, stdout = false) {
78
+ async function status(configuration, context, bytes, exitCode, stdout = false) {
79
+ if (bytes.length > configuration.limits.maxOutputBytes)
80
+ throw new FsError("EFBIG", { message: "timeout output limit exceeded" });
70
81
  await writeBytes(stdout ? context.stdout : context.stderr, bytes, context.signal);
71
82
  return { exitCode };
72
83
  }
@@ -82,7 +93,18 @@ function definition(configuration) {
82
93
  name: "timeout",
83
94
  description: "Run a virtual command with a cooperative time limit",
84
95
  async execute(context) {
96
+ context.signal.throwIfAborted();
85
97
  const originalArgs = context.args;
98
+ if (originalArgs.length > configuration.limits.maxArguments)
99
+ throw new FsError("EFBIG", { message: "timeout argument count limit exceeded" });
100
+ const suppliedValues = "argumentValues" in context ? context.argumentValues : undefined;
101
+ const ownedArguments = suppliedValues === undefined ? undefined : getCommandArguments(context);
102
+ let argumentBytes = 0;
103
+ for (const argument of ownedArguments?.values ?? originalArgs) {
104
+ argumentBytes += shellValueByteLength(argument);
105
+ if (argumentBytes > configuration.limits.maxArgumentBytes)
106
+ throw new FsError("EFBIG", { message: "timeout argument limit exceeded" });
107
+ }
86
108
  let offset = 0;
87
109
  let preserveStatus = false;
88
110
  let verbose = false;
@@ -96,9 +118,9 @@ function definition(configuration) {
96
118
  break;
97
119
  }
98
120
  if (token === "--help")
99
- return status(context, records.help, 0, true);
121
+ return status(configuration, context, records.help, 0, true);
100
122
  if (token === "--version")
101
- return status(context, records.version, 0, true);
123
+ return status(configuration, context, records.version, 0, true);
102
124
  if (token === "-" || !token.startsWith("-"))
103
125
  break;
104
126
  if (token === "--verbose") {
@@ -119,12 +141,12 @@ function definition(configuration) {
119
141
  if (token === "--kill-after" || token.startsWith("--kill-after=")) {
120
142
  const duration = token === "--kill-after" ? originalArgs[++offset] : token.slice(13);
121
143
  if (duration === undefined)
122
- return status(context, records.missingDuration, 125);
144
+ return status(configuration, context, records.missingDuration, 125);
123
145
  const killAfter = parseDuration(duration);
124
146
  if (killAfter.kind === "invalid")
125
- return status(context, records.invalidDuration, 125);
147
+ return status(configuration, context, records.invalidDuration, 125);
126
148
  if (killAfter.kind === "overflow")
127
- return status(context, records.durationOverflow, 125);
149
+ return status(configuration, context, records.durationOverflow, 125);
128
150
  killAfterMilliseconds = killAfter.milliseconds;
129
151
  offset++;
130
152
  continue;
@@ -132,10 +154,10 @@ function definition(configuration) {
132
154
  if (token === "--signal" || token.startsWith("--signal=")) {
133
155
  const signalToken = token === "--signal" ? originalArgs[++offset] : token.slice(9);
134
156
  if (signalToken === undefined)
135
- return status(context, records.invalidSignal, 125);
157
+ return status(configuration, context, records.invalidSignal, 125);
136
158
  const parsedSignal = parseSignal(signalToken);
137
159
  if (parsedSignal === undefined)
138
- return status(context, records.invalidSignal, 125);
160
+ return status(configuration, context, records.invalidSignal, 125);
139
161
  signalNumber = parsedSignal;
140
162
  offset++;
141
163
  continue;
@@ -162,46 +184,44 @@ function definition(configuration) {
162
184
  if (flag === "k") {
163
185
  const duration = token.slice(position + 1) || originalArgs[++offset];
164
186
  if (duration === undefined)
165
- return status(context, records.missingDuration, 125);
187
+ return status(configuration, context, records.missingDuration, 125);
166
188
  const killAfter = parseDuration(duration);
167
189
  if (killAfter.kind === "invalid")
168
- return status(context, records.invalidDuration, 125);
190
+ return status(configuration, context, records.invalidDuration, 125);
169
191
  if (killAfter.kind === "overflow")
170
- return status(context, records.durationOverflow, 125);
192
+ return status(configuration, context, records.durationOverflow, 125);
171
193
  killAfterMilliseconds = killAfter.milliseconds;
172
194
  break;
173
195
  }
174
196
  if (flag === "s") {
175
197
  const signalToken = token.slice(position + 1) || originalArgs[++offset];
176
198
  if (signalToken === undefined)
177
- return status(context, records.invalidSignal, 125);
199
+ return status(configuration, context, records.invalidSignal, 125);
178
200
  const parsedSignal = parseSignal(signalToken);
179
201
  if (parsedSignal === undefined)
180
- return status(context, records.invalidSignal, 125);
202
+ return status(configuration, context, records.invalidSignal, 125);
181
203
  signalNumber = parsedSignal;
182
204
  break;
183
205
  }
184
- return status(context, records.invalidOption, 125);
206
+ return status(configuration, context, records.invalidOption, 125);
185
207
  }
186
208
  offset++;
187
209
  continue;
188
210
  }
189
- return status(context, records.invalidOption, 125);
211
+ return status(configuration, context, records.invalidOption, 125);
190
212
  }
191
213
  const durationToken = originalArgs[offset];
192
214
  if (durationToken === undefined)
193
- return status(context, records.missingDuration, 125);
215
+ return status(configuration, context, records.missingDuration, 125);
194
216
  const parsed = parseDuration(durationToken);
195
217
  if (parsed.kind === "invalid")
196
- return status(context, records.invalidDuration, 125);
218
+ return status(configuration, context, records.invalidDuration, 125);
197
219
  if (parsed.kind === "overflow")
198
- return status(context, records.durationOverflow, 125);
220
+ return status(configuration, context, records.durationOverflow, 125);
199
221
  const command = originalArgs[offset + 1];
200
222
  if (command === undefined)
201
- return status(context, records.missingCommand, 125);
202
- const suppliedValues = "argumentValues" in context ? context.argumentValues : undefined;
203
- const argumentValues = suppliedValues === undefined ? undefined
204
- : getCommandArguments({ args: originalArgs, argumentValues: suppliedValues }).slice(offset + 2);
223
+ return status(configuration, context, records.missingCommand, 125);
224
+ const argumentValues = ownedArguments?.slice(offset + 2);
205
225
  const args = argumentValues?.args ?? Object.freeze(originalArgs.slice(offset + 2));
206
226
  const streams = {
207
227
  ...(argumentValues === undefined ? {} : { argumentValues }),
@@ -214,7 +234,7 @@ function definition(configuration) {
214
234
  if (killAfterMilliseconds !== undefined && killAfterMilliseconds !== 0 && parsed.milliseconds !== 0 && parsed.milliseconds !== Infinity) {
215
235
  context.signal.throwIfAborted();
216
236
  if (typeof killAfterPolicy !== "function")
217
- return status(context, records.escalationUnavailable, 125);
237
+ return status(configuration, context, records.escalationUnavailable, 125);
218
238
  let result;
219
239
  try {
220
240
  result = await killAfterPolicy(context, command, args, { signal: context.signal, ...streams }, Object.freeze({
@@ -233,7 +253,7 @@ function definition(configuration) {
233
253
  const selected = childInvoker(context, configuration.invoke);
234
254
  if (parsed.milliseconds === 0 || parsed.milliseconds === Infinity) {
235
255
  if (selected === undefined)
236
- return status(context, records.invokeUnavailable, 125);
256
+ return status(configuration, context, records.invokeUnavailable, 125);
237
257
  context.signal.throwIfAborted();
238
258
  let result;
239
259
  try {
@@ -247,7 +267,7 @@ function definition(configuration) {
247
267
  return result;
248
268
  }
249
269
  if (selected === undefined)
250
- return status(context, records.invokeUnavailable, 125);
270
+ return status(configuration, context, records.invokeUnavailable, 125);
251
271
  context.signal.throwIfAborted();
252
272
  const deadline = createDeadline(configuration.scheduler, parsed.milliseconds, configuration.maxTimerMilliseconds, signalNumber !== 0);
253
273
  context.registerCleanup?.(deadline.retire);
@@ -267,7 +287,7 @@ function definition(configuration) {
267
287
  context.signal.throwIfAborted();
268
288
  if (retirementFailed)
269
289
  throw retirementFailure;
270
- return status(context, records.timerSetupFailed, 125);
290
+ return status(configuration, context, records.timerSetupFailed, 125);
271
291
  }
272
292
  let returned = false;
273
293
  let result;
@@ -294,12 +314,12 @@ function definition(configuration) {
294
314
  if (retirementFailed)
295
315
  throw retirementFailure;
296
316
  if (verbose && deadline.expired) {
297
- await writeBytes(context.stderr, encoder.encode(`timeout: cooperative deadline expired for command ‘${command}’\n`), context.signal);
317
+ await status(configuration, context, encoder.encode(`timeout: cooperative deadline expired for command ‘${command}’\n`), 0);
298
318
  }
299
319
  if (!returned && invocationFailure === deadline.deadlineReason)
300
320
  return { exitCode: signalNumber === 9 || preserveStatus ? 128 + signalNumber : 124 };
301
321
  if (deadline.signal.aborted && deadline.signal.reason === deadline.timerFailureReason)
302
- return status(context, records.timerSetupFailed, 125);
322
+ return status(configuration, context, records.timerSetupFailed, 125);
303
323
  if (deadline.expired && (!preserveStatus || signalNumber === 9))
304
324
  return { exitCode: signalNumber === 9 ? 137 : 124 };
305
325
  return result;
@@ -1,8 +1,8 @@
1
1
  export const defaultSchedulerBinding = Object.freeze({
2
2
  receiver: globalThis,
3
- now: globalThis.performance.now.bind(globalThis.performance),
4
- setTimeout: globalThis.setTimeout.bind(globalThis),
5
- clearTimeout: globalThis.clearTimeout.bind(globalThis),
3
+ now: () => globalThis.performance.now(),
4
+ setTimeout: (callback, milliseconds) => globalThis.setTimeout(callback, milliseconds),
5
+ clearTimeout: (handle) => globalThis.clearTimeout(handle),
6
6
  });
7
7
  function clock(binding, previous) {
8
8
  const sample = Reflect.apply(binding.now, binding.receiver, []);
@@ -20,6 +20,7 @@ export function createDeadline(binding, duration, maximumChunk, cancel = true) {
20
20
  let expired = false;
21
21
  let remaining = duration;
22
22
  let previous = 0;
23
+ let step = 0;
23
24
  let handle;
24
25
  let handleLive = false;
25
26
  let cleanupFailed = false;
@@ -50,8 +51,8 @@ export function createDeadline(binding, duration, maximumChunk, cancel = true) {
50
51
  controller.abort(timerFailureReason);
51
52
  };
52
53
  const arm = () => {
53
- const milliseconds = Math.min(maximumChunk, Math.max(1, Math.ceil(remaining)));
54
- handle = Reflect.apply(binding.setTimeout, binding.receiver, [wake, milliseconds]);
54
+ step = Math.min(2147483647, maximumChunk, Math.max(1, Math.ceil(remaining)));
55
+ handle = Reflect.apply(binding.setTimeout, binding.receiver, [wake, step]);
55
56
  handleLive = true;
56
57
  };
57
58
  const wake = () => {
@@ -65,7 +66,7 @@ export function createDeadline(binding, duration, maximumChunk, cancel = true) {
65
66
  failTimer();
66
67
  return;
67
68
  }
68
- remaining -= sample - previous;
69
+ remaining -= Math.max(step, sample - previous);
69
70
  previous = sample;
70
71
  if (remaining <= 0) {
71
72
  admissionOpen = false;