@poe-platform/safe-bash 0.1.525 → 0.1.526

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.
@@ -38650,10 +38650,10 @@ var Parser4 = class {
38650
38650
  const body = this.nonemptyScript(/* @__PURE__ */ new Set(["done"]));
38651
38651
  this.expect("done", true);
38652
38652
  command7 = { kind, condition, body, redirects: [] };
38653
- } else if (this.is("for")) {
38654
- this.advance();
38653
+ } else if (this.is("for") || this.is("select")) {
38654
+ const kind = this.advance().value;
38655
38655
  const name2 = this.advance().value;
38656
- if (!/^[a-zA-Z_][a-zA-Z_0-9]*$/u.test(name2)) this.error("Invalid for variable");
38656
+ if (!/^[a-zA-Z_][a-zA-Z_0-9]*$/u.test(name2)) this.error(`Invalid ${kind} variable`);
38657
38657
  this.newlines();
38658
38658
  let words;
38659
38659
  if (this.is("in")) {
@@ -38662,12 +38662,12 @@ var Parser4 = class {
38662
38662
  while (this.current.kind === "word") words.push(this.advance().word);
38663
38663
  }
38664
38664
  if (this.is(";") || this.is("\n")) this.advance();
38665
- else if (words) this.error("Expected for separator", true);
38665
+ else if (words) this.error(`Expected ${kind} separator`, true);
38666
38666
  this.newlines();
38667
38667
  this.expect("do", true);
38668
38668
  const body = this.nonemptyScript(/* @__PURE__ */ new Set(["done"]));
38669
38669
  this.expect("done", true);
38670
- command7 = { kind: "for", name: name2, ...words ? { words } : {}, body, redirects: [] };
38670
+ command7 = { kind, name: name2, ...words ? { words } : {}, body, redirects: [] };
38671
38671
  } else if (this.is("function")) {
38672
38672
  this.advance();
38673
38673
  if (this.current.kind !== "word" || !/^[a-zA-Z_][a-zA-Z_0-9]*$/u.test(this.current.value)) this.error("Invalid function name");
@@ -38685,7 +38685,7 @@ var Parser4 = class {
38685
38685
  this.expect("(");
38686
38686
  this.expect(")");
38687
38687
  this.newlines();
38688
- if (!["{", "(", "if", "case", "while", "until", "for", "[["].includes(this.current.value)) this.error("Expected function body");
38688
+ if (!["{", "(", "if", "case", "while", "until", "for", "select", "[["].includes(this.current.value)) this.error("Expected function body");
38689
38689
  command7 = { kind: "function", name: name2, body: this.command(), redirects: [] };
38690
38690
  } else {
38691
38691
  if (["!", "then", "else", "elif", "fi", "do", "done", "}", "case", "esac", "select", "function", "[[", "]]"].includes(this.current.value)) this.error(`Unexpected or unsupported keyword ${this.current.value}`);
@@ -39540,6 +39540,94 @@ function evaluatePositionalArithmetic(program, options3, evaluate) {
39540
39540
  }
39541
39541
  }
39542
39542
 
39543
+ // packages/safe-bash/src/shell/select-menu.ts
39544
+ init_platform();
39545
+ async function selectMenu(values, columns, byteLocale2, sink, work, allocation) {
39546
+ allocation.reserve(64 + values.length * 32, 0);
39547
+ const items = [];
39548
+ let maximum = 0;
39549
+ for (const value2 of values) {
39550
+ const bytes3 = shellValueBytes(value2, allocation);
39551
+ allocation.reserve(bytes3.length * 2 + 64, 0);
39552
+ const decoder2 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
39553
+ let text3 = "";
39554
+ for (let offset = 0; offset < bytes3.length; offset += 1024) {
39555
+ const end = Math.min(offset + 1024, bytes3.length);
39556
+ const pending = stringCheckpoint(work, end - offset);
39557
+ if (pending) await pending;
39558
+ if (text3 !== void 0) try {
39559
+ text3 += decoder2.decode(bytes3.subarray(offset, end), { stream: end < bytes3.length });
39560
+ } catch (error) {
39561
+ if (!(error instanceof TypeError) || "code" in error && error.code !== "ERR_ENCODING_INVALID_ENCODED_DATA") throw error;
39562
+ text3 = void 0;
39563
+ }
39564
+ }
39565
+ let width = 0;
39566
+ if (text3 !== void 0) for (const character of text3) {
39567
+ const pending = stringCheckpoint(work);
39568
+ if (pending) await pending;
39569
+ const point = character.codePointAt(0);
39570
+ if (byteLocale2 && point >= 128) {
39571
+ width = 0;
39572
+ break;
39573
+ }
39574
+ if (point < 32 || point === 127) {
39575
+ width = bytes3.length;
39576
+ break;
39577
+ }
39578
+ if (/[\p{Mn}\p{Me}\p{Cf}]/u.test(character)) continue;
39579
+ width += point >= 4352 && (point <= 4447 || point === 9001 || point === 9002 || point >= 11904 && point <= 42191 && point !== 12351 || point >= 44032 && point <= 55203 || point >= 63744 && point <= 64255 || point >= 65040 && point <= 65049 || point >= 65072 && point <= 65135 || point >= 65280 && point <= 65376 || point >= 65504 && point <= 65510 || point >= 127744 && point <= 129791 || point >= 131072 && point <= 262141) ? 2 : 1;
39580
+ }
39581
+ maximum = Math.max(maximum, byteLocale2 ? bytes3.length : width);
39582
+ items.push({ bytes: bytes3, width });
39583
+ }
39584
+ let screen = 0;
39585
+ if (columns) {
39586
+ let started = false;
39587
+ for (const character of columns) {
39588
+ const pending = stringCheckpoint(work);
39589
+ if (pending) await pending;
39590
+ if (!started && " \r\n\v\f".includes(character)) continue;
39591
+ if (!started && character === "+") {
39592
+ started = true;
39593
+ continue;
39594
+ }
39595
+ if (character < "0" || character > "9") break;
39596
+ started = true;
39597
+ screen = Math.min(2147483647, screen * 10 + Number(character));
39598
+ }
39599
+ }
39600
+ screen ||= 80;
39601
+ const digits = String(values.length).length;
39602
+ const cell2 = maximum + digits + 4;
39603
+ let rows2 = Math.ceil(values.length / Math.max(1, Math.floor(screen / cell2)));
39604
+ if (rows2 === 1) rows2 = values.length;
39605
+ for (let row = 0; row < rows2; row++) {
39606
+ let position = 0;
39607
+ for (let index = row; index < items.length; index += rows2) {
39608
+ const pending = stringCheckpoint(work);
39609
+ if (pending) await pending;
39610
+ const item = items[index];
39611
+ const indexWidth = position ? digits : String(rows2).length;
39612
+ await writeText(sink, `${String(index + 1).padStart(indexWidth)}) `);
39613
+ await sink.write(item.bytes);
39614
+ if (index + rows2 < items.length) {
39615
+ let from = position + indexWidth + 2 + item.width;
39616
+ const to = position + cell2;
39617
+ while (from < to) {
39618
+ const pending2 = stringCheckpoint(work);
39619
+ if (pending2) await pending2;
39620
+ const tab = Math.floor(to / 8) > Math.floor(from / 8);
39621
+ await writeText(sink, tab ? " " : " ");
39622
+ from += tab ? 8 - from % 8 : 1;
39623
+ }
39624
+ position += cell2;
39625
+ }
39626
+ }
39627
+ await writeText(sink, "\n");
39628
+ }
39629
+ }
39630
+
39543
39631
  // packages/safe-bash/src/shell/locale.ts
39544
39632
  init_platform();
39545
39633
  function byteLocale(variables) {
@@ -39598,7 +39686,8 @@ ${nested}${scriptText(command7.body, nested)};
39598
39686
  ${indent}done`;
39599
39687
  break;
39600
39688
  case "for":
39601
- text3 = `for ${command7.name}${command7.words ? ` in ${command7.words.map(spelling).join(" ")}` : ""};
39689
+ case "select":
39690
+ text3 = `${command7.kind} ${command7.name}${command7.words ? ` in ${command7.words.map(spelling).join(" ")}` : ""};
39602
39691
  do
39603
39692
  ${nested}${scriptText(command7.body, nested)};
39604
39693
  ${indent}done`;
@@ -43857,6 +43946,64 @@ var Runtime = class _Runtime {
43857
43946
  status3 = result2.status;
43858
43947
  if (result2.stop) break;
43859
43948
  }
43949
+ } else if (command7.kind === "select") {
43950
+ const values = command7.words ? await this.valueWords(command7.words, state, io) : this.positionalValues(state);
43951
+ if (!values.length) return 0;
43952
+ const input3 = io.stdin instanceof ShellInput ? io.stdin : new ShellInput(io.stdin, this.budget, this.signal);
43953
+ const work = { remaining: this.budget.limits.maxExpansionBytes * 8, signal: this.signal, exhausted: () => this.budget.fail("maxExpansionBytes") };
43954
+ let showMenu = true;
43955
+ while (true) {
43956
+ this.budget.loop();
43957
+ if (showMenu) await selectMenu(values, state.variables.COLUMNS, byteLocale(state.variables), io.stderr, work, allocation);
43958
+ const prompt = stateMonitor(state)?.values.get("PS3", state.variables.PS3 ?? "#? ") ?? state.variables.PS3 ?? "#? ";
43959
+ await io.stderr.write(shellValueBytes(prompt, allocation));
43960
+ const line = await input3.selectLine(allocation);
43961
+ if (state.readonlyVariables?.has("REPLY")) {
43962
+ await this.diagnostic(io, "REPLY: readonly variable");
43963
+ await writeText(io.stdout, "\n");
43964
+ return 1;
43965
+ }
43966
+ this.writeVariable(state, "REPLY", line.value);
43967
+ if (!line.terminated) {
43968
+ await writeText(io.stdout, "\n");
43969
+ return 1;
43970
+ }
43971
+ const bytes3 = shellValueBytes(line.value, allocation);
43972
+ if (!bytes3.length) {
43973
+ showMenu = true;
43974
+ continue;
43975
+ }
43976
+ let choice = 0;
43977
+ let phase = "start";
43978
+ let valid = true;
43979
+ let negative = false;
43980
+ let digits = false;
43981
+ for (const byte of bytes3) {
43982
+ const pending = stringCheckpoint(work);
43983
+ if (pending) await pending;
43984
+ if (byte === 32 || byte >= 9 && byte <= 13) {
43985
+ if (phase === "digits") phase = "tail";
43986
+ continue;
43987
+ }
43988
+ if (phase === "start" && (byte === 43 || byte === 45)) {
43989
+ negative = byte === 45;
43990
+ phase = "digits";
43991
+ continue;
43992
+ }
43993
+ if (phase === "tail" || byte < 48 || byte > 57) {
43994
+ valid = false;
43995
+ continue;
43996
+ }
43997
+ phase = "digits";
43998
+ digits = true;
43999
+ choice = Math.min(values.length + 1, choice * 10 + byte - 48);
44000
+ }
44001
+ await this.assignVariable(state, command7.name, valid && digits && !negative && choice >= 1 && choice <= values.length ? values[choice - 1] : "");
44002
+ const result2 = await this.loopBody(command7.body, state, io);
44003
+ status3 = result2.status;
44004
+ if (result2.stop) break;
44005
+ showMenu = shellValueByteLength(stateMonitor(state)?.values.get("REPLY", state.variables.REPLY ?? "") ?? state.variables.REPLY ?? "") === 0;
44006
+ }
43860
44007
  } else {
43861
44008
  while (true) {
43862
44009
  this.budget.loop();
@@ -47972,6 +48119,52 @@ var ShellInput = class _ShellInput {
47972
48119
  line(raw, options3) {
47973
48120
  return this.#cursor.consume(this.signal, () => options3 ? this.readBounded(raw, options3) : this.readLine(raw));
47974
48121
  }
48122
+ /** Select uses read's escape rules without projecting raw REPLY bytes to text. */
48123
+ selectLine(allocation) {
48124
+ return this.#cursor.consume(this.signal, async () => {
48125
+ allocation.reserve(64, 0);
48126
+ const parts = [];
48127
+ let escaping = false;
48128
+ let length = 0;
48129
+ let pulls = 0;
48130
+ while (true) {
48131
+ if (++pulls % 128 === 0) await yieldTurn(this.signal);
48132
+ const result2 = await this.#cursor.take(this.signal);
48133
+ if (result2.done) return { value: concatShellValues(parts, allocation), terminated: false };
48134
+ const chunk = result2.value;
48135
+ for (let offset = 0; offset < chunk.length; ) {
48136
+ await yieldTurn(this.signal);
48137
+ const end = Math.min(offset + 1024, chunk.length);
48138
+ allocation.reserve(end - offset + 64, 0);
48139
+ const output2 = new Uint8Array(end - offset);
48140
+ let used = 0;
48141
+ let terminated = false;
48142
+ while (offset < end) {
48143
+ const byte = chunk[offset++];
48144
+ this.#cursor.position++;
48145
+ if (++length > this.budget.limits.maxOutputBytes) this.budget.fail("maxOutputBytes");
48146
+ if (byte === 0) continue;
48147
+ if (escaping) {
48148
+ escaping = false;
48149
+ if (byte !== 10) output2[used++] = byte;
48150
+ } else if (byte === 92) escaping = true;
48151
+ else if (byte === 10) {
48152
+ terminated = true;
48153
+ break;
48154
+ } else output2[used++] = byte;
48155
+ }
48156
+ if (used) {
48157
+ allocation.reserve(32, 0);
48158
+ parts.push(shellValueFromBytes(output2.subarray(0, used), allocation));
48159
+ }
48160
+ if (terminated) {
48161
+ if (offset < chunk.length) this.#cursor.remainder = chunk.subarray(offset);
48162
+ return { value: concatShellValues(parts, allocation), terminated: true };
48163
+ }
48164
+ }
48165
+ }
48166
+ });
48167
+ }
47975
48168
  async readBounded(raw, options3) {
47976
48169
  const text3 = new ReadText();
47977
48170
  let characters = 0;