@poe-platform/safe-bash 0.1.630 → 0.1.631
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/README.md +1 -0
- package/dist/office-package/compression.d.ts +19 -0
- package/dist/office-package/compression.js +186 -0
- package/dist/office-package/runtime.d.ts +11 -0
- package/dist/office-package/runtime.js +28 -0
- package/dist/safe-bash/chunks/{bz2-B2RXT4EX.js → bz2-ACU4EOVE.js} +2 -2
- package/dist/safe-bash/chunks/chunk-2E4KBFO6.js +239 -0
- package/dist/safe-bash/chunks/chunk-2E4KBFO6.js.map +7 -0
- package/dist/safe-bash/chunks/chunk-3E5M2WEL.js +33 -0
- package/dist/safe-bash/chunks/chunk-3E5M2WEL.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-KQ6J7JHQ.js → chunk-3LD67SER.js} +12 -10
- package/dist/safe-bash/chunks/{chunk-KQ6J7JHQ.js.map → chunk-3LD67SER.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-6LSSEOG7.js → chunk-6HNK7Q66.js} +11 -7
- package/dist/safe-bash/chunks/{chunk-6LSSEOG7.js.map → chunk-6HNK7Q66.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-AD7UAP5B.js → chunk-7EEWZY5S.js} +10 -8
- package/dist/safe-bash/chunks/{chunk-AD7UAP5B.js.map → chunk-7EEWZY5S.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-RNKTWFEM.js → chunk-CDBQXRWL.js} +3 -3
- package/dist/safe-bash/chunks/{chunk-UYX72YHY.js → chunk-CHHAB5SS.js} +10 -74
- package/dist/safe-bash/chunks/{chunk-UYX72YHY.js.map → chunk-CHHAB5SS.js.map} +3 -3
- package/dist/safe-bash/chunks/{chunk-6YSBXTTJ.js → chunk-EI4X5ETX.js} +12 -10
- package/dist/safe-bash/chunks/{chunk-6YSBXTTJ.js.map → chunk-EI4X5ETX.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-KZ7QCYNT.js → chunk-GIBLUW2U.js} +13 -9
- package/dist/safe-bash/chunks/{chunk-KZ7QCYNT.js.map → chunk-GIBLUW2U.js.map} +1 -1
- package/dist/safe-bash/chunks/chunk-GPV7ZFYS.js +1528 -0
- package/dist/safe-bash/chunks/chunk-GPV7ZFYS.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-N5CYTGBN.js → chunk-H3E67KTZ.js} +13 -9
- package/dist/safe-bash/chunks/{chunk-N5CYTGBN.js.map → chunk-H3E67KTZ.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-AFH57PPL.js → chunk-IEKESMO4.js} +3 -3
- package/dist/safe-bash/chunks/{chunk-DPJWEGIX.js → chunk-JLBIUYT3.js} +2 -2
- package/dist/safe-bash/chunks/chunk-JZDO5MC3.js +77 -0
- package/dist/safe-bash/chunks/chunk-JZDO5MC3.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-GFZKQIPL.js → chunk-LA6BX562.js} +8 -6
- package/dist/safe-bash/chunks/{chunk-GFZKQIPL.js.map → chunk-LA6BX562.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-PVJJFCGA.js → chunk-M3GPCYSP.js} +2 -2
- package/dist/safe-bash/chunks/{chunk-FWDMJTQO.js → chunk-NDCZ3UBP.js} +13 -9
- package/dist/safe-bash/chunks/{chunk-FWDMJTQO.js.map → chunk-NDCZ3UBP.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-2KL6W6NB.js → chunk-O2EVBRKF.js} +1 -2
- package/dist/safe-bash/chunks/chunk-OFT77XIB.js +389 -0
- package/dist/safe-bash/chunks/chunk-OFT77XIB.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-PCLOK32J.js → chunk-PC6BEIAF.js} +13 -9
- package/dist/safe-bash/chunks/{chunk-PCLOK32J.js.map → chunk-PC6BEIAF.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-3M5F7LD5.js → chunk-QWKP7ZU7.js} +13 -9
- package/dist/safe-bash/chunks/{chunk-3M5F7LD5.js.map → chunk-QWKP7ZU7.js.map} +1 -1
- package/dist/safe-bash/chunks/chunk-THXJ6CLF.js +53 -0
- package/dist/safe-bash/chunks/chunk-THXJ6CLF.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-K7W6Q6FN.js → chunk-V73A742N.js} +58 -16
- package/dist/safe-bash/chunks/chunk-V73A742N.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-4BYUV5PA.js → chunk-VN3ZGME3.js} +12 -8
- package/dist/safe-bash/chunks/{chunk-4BYUV5PA.js.map → chunk-VN3ZGME3.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-3BZ5NQ73.js → chunk-WECHUK3Y.js} +369 -36
- package/dist/safe-bash/chunks/chunk-WECHUK3Y.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-X55AMTNM.js → chunk-WIOEUR7D.js} +14 -10
- package/dist/safe-bash/chunks/{chunk-X55AMTNM.js.map → chunk-WIOEUR7D.js.map} +1 -1
- package/dist/safe-bash/chunks/chunk-WPIOO4NX.js +443 -0
- package/dist/safe-bash/chunks/chunk-WPIOO4NX.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-C7QNJ3D4.js → chunk-WYTI64IU.js} +2 -2
- package/dist/safe-bash/chunks/{xz-AVXFDXW4.js → xz-U4QDTPZL.js} +2 -2
- package/dist/safe-bash/chunks/{zstd-R6RPHJYO.js → zstd-OG5RPYXO.js} +2 -2
- package/dist/safe-bash/commands/archive/zip/crc.d.ts +2 -0
- package/dist/safe-bash/commands/archive/zip/crc.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/crc.js +7 -0
- package/dist/safe-bash/commands/archive/zip/crc.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/crypto.d.ts +4 -0
- package/dist/safe-bash/commands/archive/zip/crypto.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/crypto.js +34 -0
- package/dist/safe-bash/commands/archive/zip/crypto.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/help.d.ts +3 -0
- package/dist/safe-bash/commands/archive/zip/help.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/help.js +87 -0
- package/dist/safe-bash/commands/archive/zip/help.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/move.d.ts +12 -0
- package/dist/safe-bash/commands/archive/zip/move.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/move.js +60 -0
- package/dist/safe-bash/commands/archive/zip/move.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/options.js +5 -5
- package/dist/safe-bash/commands/archive/zip/options.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip-format.d.ts +1 -1
- package/dist/safe-bash/commands/archive/zip-format.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip-format.js +14 -17
- package/dist/safe-bash/commands/archive/zip-format.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip.js +45 -15
- package/dist/safe-bash/commands/archive/zip.js.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/bounded-codec.d.ts +2 -0
- package/dist/safe-bash/commands/bytes/compression/bounded-codec.d.ts.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/bounded-codec.js +4 -0
- package/dist/safe-bash/commands/bytes/compression/bounded-codec.js.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/codec.d.ts +4 -24
- package/dist/safe-bash/commands/bytes/compression/codec.d.ts.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/codec.js +12 -168
- package/dist/safe-bash/commands/bytes/compression/codec.js.map +1 -1
- package/dist/safe-bash/commands/cmp.js +1 -1
- package/dist/safe-bash/commands/cmp.js.map +1 -1
- package/dist/safe-bash/commands/csplit/index.browser.js +10 -7
- package/dist/safe-bash/commands/docx/index.browser.js +54 -0
- package/dist/safe-bash/commands/docx/index.browser.js.map +7 -0
- package/dist/safe-bash/commands/docx/index.d.ts +25 -0
- package/dist/safe-bash/commands/docx/index.d.ts.map +1 -0
- package/dist/safe-bash/commands/docx/index.js +42 -0
- package/dist/safe-bash/commands/docx/index.js.map +1 -0
- package/dist/safe-bash/commands/docx/io.d.ts +15 -0
- package/dist/safe-bash/commands/docx/io.d.ts.map +1 -0
- package/dist/safe-bash/commands/docx/io.js +16 -0
- package/dist/safe-bash/commands/docx/io.js.map +1 -0
- package/dist/safe-bash/commands/factor/index.browser.js +7 -5
- package/dist/safe-bash/commands/filesystem.d.ts.map +1 -1
- package/dist/safe-bash/commands/filesystem.js +9 -1
- package/dist/safe-bash/commands/filesystem.js.map +1 -1
- package/dist/safe-bash/commands/getopt/index.browser.js +7 -5
- package/dist/safe-bash/commands/hexdump/index.browser.js +8 -6
- package/dist/safe-bash/commands/iconv/index.browser.js +8 -6
- package/dist/safe-bash/commands/line-endings/index.browser.js +10 -7
- package/dist/safe-bash/commands/llm/index.browser.js +10 -8
- package/dist/safe-bash/commands/llm/providers/index.browser.js +3 -3
- package/dist/safe-bash/commands/network/index.browser.js +11 -8
- package/dist/safe-bash/commands/node/index.browser.js +8 -6
- package/dist/safe-bash/commands/op/index.browser.js +3 -3
- package/dist/safe-bash/commands/op/index.js +0 -1
- package/dist/safe-bash/commands/op/index.js.map +2 -2
- package/dist/safe-bash/commands/pptx/index.d.ts +29 -0
- package/dist/safe-bash/commands/pptx/index.d.ts.map +1 -0
- package/dist/safe-bash/commands/pptx/index.js +153 -0
- package/dist/safe-bash/commands/pptx/index.js.map +1 -0
- package/dist/safe-bash/commands/pr/index.browser.js +8 -6
- package/dist/safe-bash/commands/predicates.js +1 -1
- package/dist/safe-bash/commands/python/execution.d.ts +3 -0
- package/dist/safe-bash/commands/python/execution.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/execution.js +193 -0
- package/dist/safe-bash/commands/python/execution.js.map +1 -0
- package/dist/safe-bash/commands/python/index.browser.js +21 -0
- package/dist/safe-bash/commands/python/index.browser.js.map +7 -0
- package/dist/safe-bash/commands/python/index.d.ts +49 -0
- package/dist/safe-bash/commands/python/index.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/index.js +317 -0
- package/dist/safe-bash/commands/python/index.js.map +1 -0
- package/dist/safe-bash/commands/python/installation.d.ts +9 -0
- package/dist/safe-bash/commands/python/installation.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/installation.js +80 -0
- package/dist/safe-bash/commands/python/installation.js.map +1 -0
- package/dist/safe-bash/commands/python/invocation.d.ts +10 -0
- package/dist/safe-bash/commands/python/invocation.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/invocation.js +81 -0
- package/dist/safe-bash/commands/python/invocation.js.map +1 -0
- package/dist/safe-bash/commands/python/node.d.ts +12 -0
- package/dist/safe-bash/commands/python/node.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/node.js +33 -0
- package/dist/safe-bash/commands/python/node.js.map +1 -0
- package/dist/safe-bash/commands/python/provisioning-runtime.d.ts +5 -0
- package/dist/safe-bash/commands/python/provisioning-runtime.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/provisioning-runtime.js +179 -0
- package/dist/safe-bash/commands/python/provisioning-runtime.js.map +1 -0
- package/dist/safe-bash/commands/python/provisioning.d.ts +49 -0
- package/dist/safe-bash/commands/python/provisioning.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/provisioning.js +320 -0
- package/dist/safe-bash/commands/python/provisioning.js.map +1 -0
- package/dist/safe-bash/commands/python/reply.d.ts +3 -0
- package/dist/safe-bash/commands/python/reply.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/reply.js +103 -0
- package/dist/safe-bash/commands/python/reply.js.map +1 -0
- package/dist/safe-bash/commands/python/worker.browser.js +736 -0
- package/dist/safe-bash/commands/python/worker.browser.js.map +7 -0
- package/dist/safe-bash/commands/python/worker.d.ts +47 -0
- package/dist/safe-bash/commands/python/worker.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/worker.js +373 -0
- package/dist/safe-bash/commands/python/worker.js.map +1 -0
- package/dist/safe-bash/commands/tsort/index.browser.js +8 -6
- package/dist/safe-bash/commands/xml/index.browser.js +9 -7
- package/dist/safe-bash/commands/yq/index.browser.js +10 -8
- package/dist/safe-bash/commands/yq/index.d.ts.map +1 -1
- package/dist/safe-bash/commands/yq/index.js +32 -4
- package/dist/safe-bash/commands/yq/index.js.map +1 -1
- package/dist/safe-bash/commands/yq/parser.d.ts.map +1 -1
- package/dist/safe-bash/commands/yq/parser.js +21 -5
- package/dist/safe-bash/commands/yq/parser.js.map +1 -1
- package/dist/safe-bash/contracts/command.d.ts +2 -0
- package/dist/safe-bash/contracts/command.d.ts.map +1 -1
- package/dist/safe-bash/contracts/command.js +6 -2
- package/dist/safe-bash/contracts/command.js.map +1 -1
- package/dist/safe-bash/contracts/filesystem-descriptor.d.ts +13 -0
- package/dist/safe-bash/contracts/filesystem-descriptor.d.ts.map +1 -0
- package/dist/safe-bash/contracts/filesystem-descriptor.js +189 -0
- package/dist/safe-bash/contracts/filesystem-descriptor.js.map +1 -0
- package/dist/safe-bash/contracts/filesystem-output-budget.d.ts +15 -0
- package/dist/safe-bash/contracts/filesystem-output-budget.d.ts.map +1 -0
- package/dist/safe-bash/contracts/filesystem-output-budget.js +69 -0
- package/dist/safe-bash/contracts/filesystem-output-budget.js.map +1 -0
- package/dist/safe-bash/contracts/filesystem-output.d.ts +11 -5
- package/dist/safe-bash/contracts/filesystem-output.d.ts.map +1 -1
- package/dist/safe-bash/contracts/filesystem-output.js +229 -20
- package/dist/safe-bash/contracts/filesystem-output.js.map +1 -1
- package/dist/safe-bash/contracts/io.d.ts +29 -0
- package/dist/safe-bash/contracts/io.d.ts.map +1 -1
- package/dist/safe-bash/contracts/io.js +317 -37
- package/dist/safe-bash/contracts/io.js.map +1 -1
- package/dist/safe-bash/core.browser.js +5502 -2210
- package/dist/safe-bash/core.browser.js.map +4 -4
- package/dist/safe-bash/core.d.ts +1 -0
- package/dist/safe-bash/core.d.ts.map +1 -1
- package/dist/safe-bash/core.js +1 -0
- package/dist/safe-bash/core.js.map +1 -1
- package/dist/safe-bash/opt-in/commands/cmp/compare.js +95 -0
- package/dist/safe-bash/opt-in/commands/cmp/index.d.ts +6 -0
- package/dist/safe-bash/opt-in/commands/cmp/index.js +99 -0
- package/dist/safe-bash/opt-in/commands/cmp/io.js +218 -0
- package/dist/safe-bash/opt-in/commands/cmp/options.d.ts +24 -0
- package/dist/safe-bash/opt-in/commands/cmp/options.js +195 -0
- package/dist/safe-bash/opt-in/commands/dd/conversions.js +152 -0
- package/dist/safe-bash/opt-in/commands/dd/index.d.ts +18 -0
- package/dist/safe-bash/opt-in/commands/dd/index.js +561 -0
- package/dist/safe-bash/opt-in/commands/dd/io.d.ts +31 -0
- package/dist/safe-bash/opt-in/commands/dd/io.js +244 -0
- package/dist/safe-bash/opt-in/commands/dd/options.js +184 -0
- package/dist/safe-bash/opt-in/commands/dd/report.js +69 -0
- package/dist/safe-bash/opt-in/commands/install/arguments.js +224 -0
- package/dist/safe-bash/opt-in/commands/install/index.d.ts +6 -0
- package/dist/safe-bash/opt-in/commands/install/index.js +678 -0
- package/dist/safe-bash/opt-in/commands/install/mode.js +71 -0
- package/dist/safe-bash/opt-in/commands/install/options.d.ts +36 -0
- package/dist/safe-bash/opt-in/commands/install/options.js +27 -0
- package/dist/safe-bash/opt-in/commands/shuf/args.js +179 -0
- package/dist/safe-bash/opt-in/commands/shuf/index.d.ts +6 -0
- package/dist/safe-bash/opt-in/commands/shuf/index.js +20 -0
- package/dist/safe-bash/opt-in/commands/shuf/input.js +93 -0
- package/dist/safe-bash/opt-in/commands/shuf/options.d.ts +9 -0
- package/dist/safe-bash/opt-in/commands/shuf/options.js +8 -0
- package/dist/safe-bash/opt-in/commands/shuf/random.js +114 -0
- package/dist/safe-bash/opt-in/commands/shuf/shuf.d.ts +3 -0
- package/dist/safe-bash/opt-in/commands/shuf/shuf.js +230 -0
- package/dist/safe-bash/opt-in/commands/shuf/usage.js +24 -0
- package/dist/safe-bash/opt-in/commands/truncate/arguments.js +186 -0
- package/dist/safe-bash/opt-in/commands/truncate/index.d.ts +9 -0
- package/dist/safe-bash/opt-in/commands/truncate/index.js +244 -0
- package/dist/safe-bash/opt-in/commands/yes/index.d.ts +11 -0
- package/dist/safe-bash/opt-in/commands/yes/index.js +167 -0
- package/dist/safe-bash/opt-in/commands/yq/arguments.js +177 -0
- package/dist/safe-bash/opt-in/commands/yq/evaluate.js +564 -0
- package/dist/safe-bash/opt-in/commands/yq/expression.js +183 -0
- package/dist/safe-bash/opt-in/commands/yq/inplace.js +65 -0
- package/dist/safe-bash/opt-in/commands/yq/mike.d.ts +9 -0
- package/dist/safe-bash/opt-in/commands/yq/mike.js +192 -0
- package/dist/safe-bash/opt-in/commands/yq/native-encoder.js +165 -0
- package/dist/safe-bash/opt-in/commands/yq/native-work.d.ts +44 -0
- package/dist/safe-bash/opt-in/commands/yq/native-work.js +188 -0
- package/dist/safe-bash/opt-in/commands/yq/nodes.js +621 -0
- package/dist/safe-bash/opt-in/entrypoints/arrays.d.ts +1 -0
- package/dist/safe-bash/opt-in/entrypoints/arrays.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/cmp.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/cmp.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/dd.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/dd.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/devices.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/devices.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/install.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/install.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/jobs.d.ts +1 -0
- package/dist/safe-bash/opt-in/entrypoints/jobs.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/mapfile.d.ts +1 -0
- package/dist/safe-bash/opt-in/entrypoints/mapfile.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/read.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/read.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/shuf.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/shuf.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/trap.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/trap.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/truncate.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/truncate.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/yes.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/yes.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/yq.d.ts +3 -0
- package/dist/safe-bash/opt-in/entrypoints/yq.js +1 -0
- package/dist/safe-bash/opt-in/fs/devices/index.d.ts +8 -0
- package/dist/safe-bash/opt-in/fs/devices/index.js +292 -0
- package/dist/safe-bash/opt-in/optional.d.ts +28 -0
- package/dist/safe-bash/opt-in/optional.js +14 -0
- package/dist/safe-bash/opt-in/shell/extensions/arrays/index.d.ts +2 -0
- package/dist/safe-bash/opt-in/shell/extensions/arrays/index.js +4 -0
- package/dist/safe-bash/opt-in/shell/extensions/jobs/index.d.ts +2 -0
- package/dist/safe-bash/opt-in/shell/extensions/jobs/index.js +291 -0
- package/dist/safe-bash/opt-in/shell/extensions/jobs/state.js +374 -0
- package/dist/safe-bash/opt-in/shell/extensions/mapfile/index.d.ts +24 -0
- package/dist/safe-bash/opt-in/shell/extensions/mapfile/index.js +338 -0
- package/dist/safe-bash/opt-in/shell/extensions/read/index.d.ts +5 -0
- package/dist/safe-bash/opt-in/shell/extensions/read/index.js +351 -0
- package/dist/safe-bash/opt-in/shell/extensions/trap/index.d.ts +9 -0
- package/dist/safe-bash/opt-in/shell/extensions/trap/index.js +210 -0
- package/dist/safe-bash/optional-host.d.ts +5 -0
- package/dist/safe-bash/optional-host.d.ts.map +1 -0
- package/dist/safe-bash/optional-host.js +3 -0
- package/dist/safe-bash/optional-host.js.map +1 -0
- package/dist/safe-bash/shell/arrays/bindings.d.ts +15 -5
- package/dist/safe-bash/shell/arrays/bindings.d.ts.map +1 -1
- package/dist/safe-bash/shell/arrays/bindings.js +131 -23
- package/dist/safe-bash/shell/arrays/bindings.js.map +1 -1
- package/dist/safe-bash/shell/arrays/ledger.d.ts +1 -0
- package/dist/safe-bash/shell/arrays/ledger.d.ts.map +1 -1
- package/dist/safe-bash/shell/arrays/ledger.js +19 -0
- package/dist/safe-bash/shell/arrays/ledger.js.map +1 -1
- package/dist/safe-bash/shell/arrays/state.d.ts.map +1 -1
- package/dist/safe-bash/shell/arrays/state.js +3 -1
- package/dist/safe-bash/shell/arrays/state.js.map +1 -1
- package/dist/safe-bash/shell/arrays/syntax.d.ts +4 -1
- package/dist/safe-bash/shell/arrays/syntax.d.ts.map +1 -1
- package/dist/safe-bash/shell/arrays/syntax.js +3 -3
- package/dist/safe-bash/shell/arrays/syntax.js.map +1 -1
- package/dist/safe-bash/shell/conditional.js +1 -1
- package/dist/safe-bash/shell/descriptors.d.ts +31 -0
- package/dist/safe-bash/shell/descriptors.d.ts.map +1 -0
- package/dist/safe-bash/shell/descriptors.js +234 -0
- package/dist/safe-bash/shell/descriptors.js.map +1 -0
- package/dist/safe-bash/shell/diagnostic-name-ranges.d.ts +2 -0
- package/dist/safe-bash/shell/diagnostic-name-ranges.d.ts.map +1 -0
- package/dist/safe-bash/shell/diagnostic-name-ranges.js +717 -0
- package/dist/safe-bash/shell/diagnostic-name-ranges.js.map +1 -0
- package/dist/safe-bash/shell/diagnostic-name.d.ts +3 -0
- package/dist/safe-bash/shell/diagnostic-name.d.ts.map +1 -0
- package/dist/safe-bash/shell/diagnostic-name.js +89 -0
- package/dist/safe-bash/shell/diagnostic-name.js.map +1 -0
- package/dist/safe-bash/shell/display.js +1 -1
- package/dist/safe-bash/shell/display.js.map +1 -1
- package/dist/safe-bash/shell/extensions.d.ts +188 -0
- package/dist/safe-bash/shell/extensions.d.ts.map +1 -0
- package/dist/safe-bash/shell/extensions.js +141 -0
- package/dist/safe-bash/shell/extensions.js.map +1 -0
- package/dist/safe-bash/shell/input.d.ts +66 -14
- package/dist/safe-bash/shell/input.d.ts.map +1 -1
- package/dist/safe-bash/shell/input.js +947 -173
- package/dist/safe-bash/shell/input.js.map +1 -1
- package/dist/safe-bash/shell/parser.d.ts +38 -6
- package/dist/safe-bash/shell/parser.d.ts.map +1 -1
- package/dist/safe-bash/shell/parser.js +339 -52
- package/dist/safe-bash/shell/parser.js.map +1 -1
- package/dist/safe-bash/shell/runtime.d.ts +92 -20
- package/dist/safe-bash/shell/runtime.d.ts.map +1 -1
- package/dist/safe-bash/shell/runtime.js +2547 -689
- package/dist/safe-bash/shell/runtime.js.map +1 -1
- package/dist/safe-bash/shell/shell.d.ts.map +1 -1
- package/dist/safe-bash/shell/shell.js +52 -21
- package/dist/safe-bash/shell/shell.js.map +1 -1
- package/dist/safe-bash/shell/types.d.ts +4 -0
- package/dist/safe-bash/shell/types.d.ts.map +1 -1
- package/dist/safe-bash/shell/types.js.map +1 -1
- package/package.json +92 -4
- package/dist/safe-bash/chunks/chunk-3BZ5NQ73.js.map +0 -7
- package/dist/safe-bash/chunks/chunk-4DBWBEU7.js +0 -194
- package/dist/safe-bash/chunks/chunk-4DBWBEU7.js.map +0 -7
- package/dist/safe-bash/chunks/chunk-CXMIIXJA.js +0 -178
- package/dist/safe-bash/chunks/chunk-CXMIIXJA.js.map +0 -7
- package/dist/safe-bash/chunks/chunk-K7W6Q6FN.js.map +0 -7
- /package/dist/safe-bash/chunks/{bz2-B2RXT4EX.js.map → bz2-ACU4EOVE.js.map} +0 -0
- /package/dist/safe-bash/chunks/{chunk-RNKTWFEM.js.map → chunk-CDBQXRWL.js.map} +0 -0
- /package/dist/safe-bash/chunks/{chunk-AFH57PPL.js.map → chunk-IEKESMO4.js.map} +0 -0
- /package/dist/safe-bash/chunks/{chunk-DPJWEGIX.js.map → chunk-JLBIUYT3.js.map} +0 -0
- /package/dist/safe-bash/chunks/{chunk-PVJJFCGA.js.map → chunk-M3GPCYSP.js.map} +0 -0
- /package/dist/safe-bash/chunks/{chunk-2KL6W6NB.js.map → chunk-O2EVBRKF.js.map} +0 -0
- /package/dist/safe-bash/chunks/{chunk-C7QNJ3D4.js.map → chunk-WYTI64IU.js.map} +0 -0
- /package/dist/safe-bash/chunks/{xz-AVXFDXW4.js.map → xz-U4QDTPZL.js.map} +0 -0
- /package/dist/safe-bash/chunks/{zstd-R6RPHJYO.js.map → zstd-OG5RPYXO.js.map} +0 -0
|
@@ -1,8 +1,290 @@
|
|
|
1
|
-
import { PublicDiagnostic } from "../diagnostics.js";
|
|
2
1
|
import { FsError, toByteSource } from "../contracts/index.js";
|
|
3
|
-
import { yieldTurn } from "../contracts/yield.js";
|
|
2
|
+
import { monotonicNow, yieldTurn } from "../contracts/yield.js";
|
|
4
3
|
import { Budget, interruptible } from "./runtime.js";
|
|
5
|
-
import { concatShellValues, shellValueFromBytes } from "../contracts/value.js";
|
|
4
|
+
import { concatShellValues, shellValueBytes, shellValueFromBytes, shellValueText } from "../contracts/value.js";
|
|
5
|
+
import { openCommandFile } from "../contracts/filesystem-descriptor.js";
|
|
6
|
+
const inputBuffers = new WeakMap();
|
|
7
|
+
const inputByteLength = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Uint8Array.prototype), "byteLength").get;
|
|
8
|
+
export function inputBufferUsage(budget) {
|
|
9
|
+
let bytes = 0;
|
|
10
|
+
const leases = inputBuffers.get(budget);
|
|
11
|
+
if (leases)
|
|
12
|
+
for (const lease of leases)
|
|
13
|
+
bytes += lease.capacity;
|
|
14
|
+
return Object.freeze({ bytes, buffers: leases?.size ?? 0 });
|
|
15
|
+
}
|
|
16
|
+
class InputBufferLease {
|
|
17
|
+
budget;
|
|
18
|
+
capacity;
|
|
19
|
+
bytes;
|
|
20
|
+
constructor(budget, capacity, allocate) {
|
|
21
|
+
this.budget = budget;
|
|
22
|
+
this.capacity = capacity;
|
|
23
|
+
budget.signal.throwIfAborted();
|
|
24
|
+
if (!Number.isSafeInteger(capacity) || capacity <= 0 || capacity > Math.max(1, budget.limits.maxInputBytes)) {
|
|
25
|
+
throw new FsError("EFBIG", { syscall: "read" });
|
|
26
|
+
}
|
|
27
|
+
let leases = inputBuffers.get(budget);
|
|
28
|
+
if (!leases) {
|
|
29
|
+
leases = new Set();
|
|
30
|
+
inputBuffers.set(budget, leases);
|
|
31
|
+
}
|
|
32
|
+
leases.add(this);
|
|
33
|
+
try {
|
|
34
|
+
this.bytes = allocate();
|
|
35
|
+
budget.signal.throwIfAborted();
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
this.release();
|
|
39
|
+
budget.signal.throwIfAborted();
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
release() {
|
|
44
|
+
this.bytes = undefined;
|
|
45
|
+
const leases = inputBuffers.get(this.budget);
|
|
46
|
+
leases?.delete(this);
|
|
47
|
+
if (!leases?.size)
|
|
48
|
+
inputBuffers.delete(this.budget);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function prepareBytesInput(value, budget) {
|
|
52
|
+
budget.signal.throwIfAborted();
|
|
53
|
+
if (typeof value !== "string" && !(value instanceof Uint8Array))
|
|
54
|
+
throw new TypeError("Shell input must be a string or Uint8Array");
|
|
55
|
+
const length = typeof value === "string" ? Buffer.byteLength(value) : inputByteLength.call(value);
|
|
56
|
+
if (length > budget.limits.maxInputBytes)
|
|
57
|
+
throw new FsError("EFBIG", { syscall: "read" });
|
|
58
|
+
let buffer;
|
|
59
|
+
let sent = false;
|
|
60
|
+
let closed = false;
|
|
61
|
+
let closing;
|
|
62
|
+
const close = () => {
|
|
63
|
+
closed = true;
|
|
64
|
+
buffer?.release();
|
|
65
|
+
buffer = undefined;
|
|
66
|
+
budget.signal.removeEventListener("abort", aborted);
|
|
67
|
+
return closing ??= Promise.resolve();
|
|
68
|
+
};
|
|
69
|
+
const aborted = () => { void close(); };
|
|
70
|
+
try {
|
|
71
|
+
if (length) {
|
|
72
|
+
buffer = new InputBufferLease(budget, length, () => typeof value === "string" ? new TextEncoder().encode(value) : new Uint8Array(value));
|
|
73
|
+
}
|
|
74
|
+
budget.signal.addEventListener("abort", aborted, { once: true });
|
|
75
|
+
const source = {
|
|
76
|
+
[Symbol.asyncIterator]() { return this; },
|
|
77
|
+
async next() {
|
|
78
|
+
budget.signal.throwIfAborted();
|
|
79
|
+
const bytes = buffer?.bytes;
|
|
80
|
+
if (closed || sent || !bytes?.byteLength)
|
|
81
|
+
return { done: true, value: undefined };
|
|
82
|
+
sent = true;
|
|
83
|
+
return { done: false, value: bytes };
|
|
84
|
+
},
|
|
85
|
+
async return() { await close(); return { done: true, value: undefined }; },
|
|
86
|
+
};
|
|
87
|
+
return Object.freeze({ source, close, options: Object.freeze({ provenance: "stream", poll: () => {
|
|
88
|
+
budget.signal.throwIfAborted();
|
|
89
|
+
if (closed)
|
|
90
|
+
throw new Error("Prepared input is closed");
|
|
91
|
+
return !sent && length ? "ready" : "eof";
|
|
92
|
+
} }) });
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
void close();
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export async function prepareFileInput(context, path, budget, inputProfile = context.fs, admittedStat) {
|
|
100
|
+
const { fs, signal: parent, registerCleanup: register, cleanupFailurePrioritySignal } = context;
|
|
101
|
+
const registerCleanup = register.bind(context);
|
|
102
|
+
const signal = AbortSignal.any([parent, budget.signal]);
|
|
103
|
+
const readerController = new AbortController();
|
|
104
|
+
const readSignal = AbortSignal.any([signal, readerController.signal]);
|
|
105
|
+
let descriptor;
|
|
106
|
+
let legacy;
|
|
107
|
+
let legacySource;
|
|
108
|
+
let stat;
|
|
109
|
+
let accepting = true;
|
|
110
|
+
let ended = false;
|
|
111
|
+
let buffer;
|
|
112
|
+
let size = 0;
|
|
113
|
+
let pendingReads = 0;
|
|
114
|
+
let work = Promise.resolve();
|
|
115
|
+
let admitted;
|
|
116
|
+
const acquisition = new Promise(resolve => { admitted = resolve; });
|
|
117
|
+
let closing;
|
|
118
|
+
let teardownFailed = false;
|
|
119
|
+
const close = () => {
|
|
120
|
+
accepting = false;
|
|
121
|
+
if (pendingReads && !readerController.signal.aborted)
|
|
122
|
+
readerController.abort(new FsError("EBADF", { syscall: "read", path }));
|
|
123
|
+
closing ??= (async () => {
|
|
124
|
+
await acquisition;
|
|
125
|
+
await work;
|
|
126
|
+
try {
|
|
127
|
+
if (descriptor)
|
|
128
|
+
await descriptor.close();
|
|
129
|
+
else
|
|
130
|
+
await legacy?.return?.();
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
teardownFailed = true;
|
|
134
|
+
(cleanupFailurePrioritySignal ?? signal).throwIfAborted();
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
descriptor = undefined;
|
|
139
|
+
legacy = undefined;
|
|
140
|
+
legacySource = undefined;
|
|
141
|
+
buffer?.release();
|
|
142
|
+
buffer = undefined;
|
|
143
|
+
signal.removeEventListener("abort", aborted);
|
|
144
|
+
}
|
|
145
|
+
signal.throwIfAborted();
|
|
146
|
+
})();
|
|
147
|
+
void closing.catch(() => { });
|
|
148
|
+
return closing;
|
|
149
|
+
};
|
|
150
|
+
const aborted = () => { void close().catch(() => { }); };
|
|
151
|
+
const check = () => {
|
|
152
|
+
signal.throwIfAborted();
|
|
153
|
+
if (!accepting)
|
|
154
|
+
throw new FsError("EBADF", { syscall: "read", path });
|
|
155
|
+
};
|
|
156
|
+
try {
|
|
157
|
+
registerCleanup(cleanupFailurePrioritySignal === undefined ? close : async () => {
|
|
158
|
+
try {
|
|
159
|
+
await close();
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
if (teardownFailed)
|
|
163
|
+
cleanupFailurePrioritySignal.throwIfAborted();
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
168
|
+
check();
|
|
169
|
+
const capabilities = await fs.capabilitiesFor?.(path, { signal }) ?? fs.capabilities;
|
|
170
|
+
check();
|
|
171
|
+
let provenance = "unknown";
|
|
172
|
+
if (capabilities.open === true || capabilities.open !== false && typeof fs.open === "function") {
|
|
173
|
+
descriptor = await openCommandFile({ fs, signal, registerCleanup, cleanupFailurePrioritySignal }, path, { access: "read", signal });
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
stat = admittedStat ?? await fs.stat(path, { signal });
|
|
177
|
+
check();
|
|
178
|
+
legacySource = await fileInput(fs, path, budget.limits.maxInputBytes, readSignal, inputProfile, { stat, registerCleanup });
|
|
179
|
+
legacy = legacySource[Symbol.asyncIterator]();
|
|
180
|
+
stat = legacySource.stat ?? stat;
|
|
181
|
+
}
|
|
182
|
+
check();
|
|
183
|
+
if (descriptor) {
|
|
184
|
+
stat = await descriptor.stat({ signal });
|
|
185
|
+
check();
|
|
186
|
+
}
|
|
187
|
+
if (stat) {
|
|
188
|
+
if (stat.type === "directory")
|
|
189
|
+
throw new FsError("EISDIR", { syscall: "read", path });
|
|
190
|
+
if (descriptor || legacySource?.stat)
|
|
191
|
+
provenance = stat.type === "file" ? "regular" : stat.type === "character" ? "stream" : "unknown";
|
|
192
|
+
}
|
|
193
|
+
const source = {
|
|
194
|
+
[Symbol.asyncIterator]() { return this; },
|
|
195
|
+
next(maximum = 64 * 1024) {
|
|
196
|
+
try {
|
|
197
|
+
signal.throwIfAborted();
|
|
198
|
+
if (!Number.isSafeInteger(maximum) || maximum <= 0)
|
|
199
|
+
throw new RangeError("Input chunk size must be a positive safe integer");
|
|
200
|
+
if (ended)
|
|
201
|
+
return Promise.resolve({ done: true, value: undefined });
|
|
202
|
+
check();
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
return Promise.reject(error);
|
|
206
|
+
}
|
|
207
|
+
pendingReads++;
|
|
208
|
+
const operation = work.then(async () => {
|
|
209
|
+
signal.throwIfAborted();
|
|
210
|
+
if (ended)
|
|
211
|
+
return { done: true, value: undefined };
|
|
212
|
+
check();
|
|
213
|
+
if (legacy) {
|
|
214
|
+
const result = await (legacySource?.readChunk ? legacySource.readChunk(maximum) : legacy.next());
|
|
215
|
+
check();
|
|
216
|
+
ended = result.done === true && provenance !== "regular";
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
if (!buffer) {
|
|
220
|
+
const capacity = Math.min(64 * 1024, budget.limits.maxInputBytes) || 1;
|
|
221
|
+
buffer = new InputBufferLease(budget, capacity, () => new Uint8Array(capacity));
|
|
222
|
+
}
|
|
223
|
+
const bytes = buffer.bytes;
|
|
224
|
+
const remaining = budget.limits.maxInputBytes - size;
|
|
225
|
+
const chunk = bytes.subarray(0, Math.min(maximum, remaining >= bytes.length ? bytes.length : remaining + 1));
|
|
226
|
+
const length = await descriptor.read(chunk, null, { signal: readSignal });
|
|
227
|
+
check();
|
|
228
|
+
if (!Number.isSafeInteger(length) || length < 0 || length > chunk.length)
|
|
229
|
+
throw new FsError("EIO", { syscall: "read", path });
|
|
230
|
+
if (length > budget.limits.maxInputBytes - size)
|
|
231
|
+
throw new FsError("EFBIG", { syscall: "read", path });
|
|
232
|
+
size += length;
|
|
233
|
+
const done = length === 0;
|
|
234
|
+
ended = done && provenance !== "regular";
|
|
235
|
+
return done ? { done: true, value: undefined } : { done: false, value: chunk.subarray(0, length) };
|
|
236
|
+
});
|
|
237
|
+
work = operation.then(() => { pendingReads--; }, () => { pendingReads--; });
|
|
238
|
+
return operation.then(async (result) => {
|
|
239
|
+
if (result.done && provenance !== "regular") {
|
|
240
|
+
if (legacy)
|
|
241
|
+
await close();
|
|
242
|
+
else {
|
|
243
|
+
buffer?.release();
|
|
244
|
+
buffer = undefined;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
signal.throwIfAborted();
|
|
248
|
+
return result;
|
|
249
|
+
}, async (error) => {
|
|
250
|
+
try {
|
|
251
|
+
await close();
|
|
252
|
+
}
|
|
253
|
+
catch { }
|
|
254
|
+
signal.throwIfAborted();
|
|
255
|
+
throw error;
|
|
256
|
+
});
|
|
257
|
+
},
|
|
258
|
+
async return() { await close(); return { done: true, value: undefined }; },
|
|
259
|
+
};
|
|
260
|
+
admitted();
|
|
261
|
+
const seek = legacySource?.seek;
|
|
262
|
+
return Object.freeze({ source, close, options: Object.freeze({
|
|
263
|
+
provenance, eof: provenance === "regular" ? "retryable" : "terminal",
|
|
264
|
+
...(descriptor ? { descriptor } : {}), ...(stat ? { stat } : {}),
|
|
265
|
+
readChunk: source.next.bind(source),
|
|
266
|
+
...(seek ? { async seek(position, callerSignal) {
|
|
267
|
+
callerSignal.throwIfAborted();
|
|
268
|
+
check();
|
|
269
|
+
await work;
|
|
270
|
+
check();
|
|
271
|
+
callerSignal.throwIfAborted();
|
|
272
|
+
await seek.call(legacySource, position, callerSignal);
|
|
273
|
+
check();
|
|
274
|
+
ended = false;
|
|
275
|
+
} } : {}),
|
|
276
|
+
}) });
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
admitted();
|
|
280
|
+
try {
|
|
281
|
+
await close();
|
|
282
|
+
}
|
|
283
|
+
catch { }
|
|
284
|
+
signal.throwIfAborted();
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
6
288
|
export async function fileInput(fs, path, maxBytes, signal, inputProfile = fs, ownership) {
|
|
7
289
|
let admittedCapabilities;
|
|
8
290
|
if (ownership?.stat.type === "file") {
|
|
@@ -18,7 +300,6 @@ export async function fileInput(fs, path, maxBytes, signal, inputProfile = fs, o
|
|
|
18
300
|
})();
|
|
19
301
|
return closing;
|
|
20
302
|
};
|
|
21
|
-
ownership.registerCleanup(close);
|
|
22
303
|
const work = async (operation) => {
|
|
23
304
|
signal.throwIfAborted();
|
|
24
305
|
if (closed)
|
|
@@ -31,6 +312,7 @@ export async function fileInput(fs, path, maxBytes, signal, inputProfile = fs, o
|
|
|
31
312
|
try {
|
|
32
313
|
const capabilities = admittedCapabilities = await work(() => Promise.resolve(fs.capabilitiesFor?.(path, { signal }) ?? fs.capabilities));
|
|
33
314
|
if (capabilities.retainedRead === true && fs.openReadFile) {
|
|
315
|
+
ownership.registerCleanup(close);
|
|
34
316
|
try {
|
|
35
317
|
await work(async () => { handle = await fs.openReadFile(path, { signal }); });
|
|
36
318
|
}
|
|
@@ -161,8 +443,80 @@ export async function fileInput(fs, path, maxBytes, signal, inputProfile = fs, o
|
|
|
161
443
|
}),
|
|
162
444
|
};
|
|
163
445
|
}
|
|
446
|
+
const inputClock = {
|
|
447
|
+
now: monotonicNow,
|
|
448
|
+
schedule(delayMs, expire) {
|
|
449
|
+
const timer = setTimeout(expire, Math.min(delayMs, 2_147_483_647));
|
|
450
|
+
return () => clearTimeout(timer);
|
|
451
|
+
},
|
|
452
|
+
};
|
|
453
|
+
class InputDeadline {
|
|
454
|
+
clock;
|
|
455
|
+
parent;
|
|
456
|
+
#controller = new AbortController();
|
|
457
|
+
#expiresAt;
|
|
458
|
+
#cancel;
|
|
459
|
+
#closed = false;
|
|
460
|
+
signal;
|
|
461
|
+
constructor(clock, timeoutMs, parent) {
|
|
462
|
+
this.clock = clock;
|
|
463
|
+
this.parent = parent;
|
|
464
|
+
const now = clock.now();
|
|
465
|
+
this.#expiresAt = now + timeoutMs;
|
|
466
|
+
if (!Number.isFinite(now) || !Number.isFinite(this.#expiresAt))
|
|
467
|
+
throw new RangeError("Invalid input clock deadline");
|
|
468
|
+
this.signal = AbortSignal.any([parent, this.#controller.signal]);
|
|
469
|
+
parent.throwIfAborted();
|
|
470
|
+
parent.addEventListener("abort", this.close, { once: true });
|
|
471
|
+
try {
|
|
472
|
+
this.#schedule();
|
|
473
|
+
}
|
|
474
|
+
catch (error) {
|
|
475
|
+
this.close();
|
|
476
|
+
throw error;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
expired() {
|
|
480
|
+
this.parent.throwIfAborted();
|
|
481
|
+
if (!this.#closed && this.clock.now() >= this.#expiresAt) {
|
|
482
|
+
this.#controller.abort();
|
|
483
|
+
this.close();
|
|
484
|
+
}
|
|
485
|
+
return this.#controller.signal.aborted;
|
|
486
|
+
}
|
|
487
|
+
#schedule() {
|
|
488
|
+
const cancel = this.clock.schedule(Math.max(0, this.#expiresAt - this.clock.now()), () => {
|
|
489
|
+
this.#cancel = undefined;
|
|
490
|
+
if (this.#closed || this.parent.aborted)
|
|
491
|
+
return;
|
|
492
|
+
if (!this.expired())
|
|
493
|
+
this.#schedule();
|
|
494
|
+
});
|
|
495
|
+
if (this.#closed)
|
|
496
|
+
cancel();
|
|
497
|
+
else
|
|
498
|
+
this.#cancel = cancel;
|
|
499
|
+
}
|
|
500
|
+
close = () => {
|
|
501
|
+
this.#closed = true;
|
|
502
|
+
const cancel = this.#cancel;
|
|
503
|
+
this.#cancel = undefined;
|
|
504
|
+
this.parent.removeEventListener("abort", this.close);
|
|
505
|
+
try {
|
|
506
|
+
cancel?.();
|
|
507
|
+
}
|
|
508
|
+
catch (error) {
|
|
509
|
+
if (!this.parent.aborted)
|
|
510
|
+
throw error;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
}
|
|
164
514
|
class InputCursor {
|
|
165
515
|
#iterator;
|
|
516
|
+
#provenance;
|
|
517
|
+
#eof;
|
|
518
|
+
#poll;
|
|
519
|
+
#clock;
|
|
166
520
|
#readChunk;
|
|
167
521
|
#budget;
|
|
168
522
|
stat;
|
|
@@ -171,28 +525,49 @@ class InputCursor {
|
|
|
171
525
|
remainder;
|
|
172
526
|
#unread = [];
|
|
173
527
|
#read;
|
|
528
|
+
#readResult;
|
|
529
|
+
#readError;
|
|
530
|
+
#readSettled = false;
|
|
174
531
|
#readFailed = false;
|
|
175
532
|
#turn = Promise.resolve();
|
|
176
533
|
#returned;
|
|
177
534
|
#ended = false;
|
|
178
535
|
#closed = false;
|
|
536
|
+
#active = false;
|
|
179
537
|
#produced = 0;
|
|
180
538
|
#boundedReads = false;
|
|
181
|
-
constructor(source,
|
|
539
|
+
constructor(source, options, budget) {
|
|
540
|
+
const { provenance = options.stat?.type === "file" ? "regular" : options.stat?.type === "character" ? "stream" : "unknown", eof = "terminal", poll, clock = inputClock } = options;
|
|
541
|
+
if (provenance !== "unknown" && provenance !== "regular" && provenance !== "stream")
|
|
542
|
+
throw new TypeError("Invalid input provenance");
|
|
543
|
+
if (eof !== "terminal" && eof !== "retryable" || eof === "retryable" && provenance !== "regular")
|
|
544
|
+
throw new TypeError("Retryable input EOF requires regular provenance");
|
|
545
|
+
if (poll !== undefined && typeof poll !== "function")
|
|
546
|
+
throw new TypeError("Invalid input polling capability");
|
|
547
|
+
const { now, schedule } = clock;
|
|
548
|
+
if (typeof now !== "function" || typeof schedule !== "function")
|
|
549
|
+
throw new TypeError("Invalid input clock");
|
|
550
|
+
this.#provenance = provenance;
|
|
551
|
+
this.#eof = eof;
|
|
552
|
+
this.#poll = poll?.bind(options);
|
|
553
|
+
this.#clock = Object.freeze({ now: now.bind(clock), schedule: schedule.bind(clock) });
|
|
182
554
|
this.#iterator = source[Symbol.asyncIterator]();
|
|
183
|
-
this.#readChunk =
|
|
555
|
+
this.#readChunk = options.readChunk?.bind(options);
|
|
184
556
|
this.#budget = budget;
|
|
185
|
-
if (
|
|
186
|
-
this.stat =
|
|
187
|
-
if (
|
|
557
|
+
if (options.stat)
|
|
558
|
+
this.stat = options.stat;
|
|
559
|
+
if (options.seek)
|
|
188
560
|
this.seek = async (position, signal) => {
|
|
189
561
|
if (this.#read)
|
|
190
562
|
await interruptible(this.#read, signal);
|
|
191
563
|
signal.throwIfAborted();
|
|
192
|
-
await
|
|
564
|
+
await options.seek(position, signal);
|
|
193
565
|
this.remainder = undefined;
|
|
194
566
|
this.#unread.length = 0;
|
|
195
567
|
this.#read = undefined;
|
|
568
|
+
this.#readResult = undefined;
|
|
569
|
+
this.#readError = undefined;
|
|
570
|
+
this.#readSettled = false;
|
|
196
571
|
this.#ended = false;
|
|
197
572
|
this.position = position;
|
|
198
573
|
};
|
|
@@ -209,16 +584,78 @@ class InputCursor {
|
|
|
209
584
|
if (this.#produced > this.#budget.limits.maxInputBytes)
|
|
210
585
|
this.#budget.fail("maxInputBytes");
|
|
211
586
|
}
|
|
212
|
-
|
|
587
|
+
get bufferedBytes() {
|
|
588
|
+
if (this.#active)
|
|
589
|
+
return 0;
|
|
590
|
+
return (this.remainder?.length ?? 0) + this.#unread.reduce((length, chunk) => length + chunk.length, 0)
|
|
591
|
+
+ (this.#readResult && !this.#readResult.done ? this.#readResult.value.length : 0);
|
|
592
|
+
}
|
|
593
|
+
readiness() {
|
|
594
|
+
if (this.#active)
|
|
595
|
+
return "blocked";
|
|
596
|
+
if (this.remainder?.length || this.#unread.some(chunk => chunk.length))
|
|
597
|
+
return "ready";
|
|
598
|
+
if (this.#readError)
|
|
599
|
+
throw this.#readError.reason;
|
|
600
|
+
if (this.#ended || this.#readResult?.done)
|
|
601
|
+
return "eof";
|
|
602
|
+
if (this.#readResult && this.#readResult.value.length)
|
|
603
|
+
return "ready";
|
|
604
|
+
if (this.#closed)
|
|
605
|
+
throw new Error("Shell input cursor is closed");
|
|
606
|
+
if (this.#provenance === "regular")
|
|
607
|
+
return "ready";
|
|
608
|
+
const readiness = this.#poll?.() ?? "unknown";
|
|
609
|
+
if (!["ready", "eof", "blocked", "unknown"].includes(readiness))
|
|
610
|
+
throw new TypeError("Invalid input readiness");
|
|
611
|
+
if (readiness === "eof" && this.#read && !this.#readSettled)
|
|
612
|
+
return "unknown";
|
|
613
|
+
return readiness;
|
|
614
|
+
}
|
|
615
|
+
probeRead() {
|
|
616
|
+
const readiness = this.readiness();
|
|
617
|
+
return { readiness: readiness === "eof" ? "ready" : readiness,
|
|
618
|
+
timeout: this.#provenance === "regular" ? "ignore" : this.#provenance === "stream" ? "honor" : "unknown" };
|
|
619
|
+
}
|
|
620
|
+
deadline(timeoutMs, signal, scope) {
|
|
621
|
+
if (timeoutMs === undefined)
|
|
622
|
+
return undefined;
|
|
623
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
|
|
624
|
+
throw new RangeError("Read timeout must be positive and finite; use readiness for zero timeout");
|
|
625
|
+
if (this.#provenance === "unknown")
|
|
626
|
+
throw new TypeError("Read timeout requires explicit input provenance");
|
|
627
|
+
if (this.#provenance === "regular")
|
|
628
|
+
return undefined;
|
|
629
|
+
scope.reserve(192, 3);
|
|
630
|
+
return new InputDeadline(this.#clock, timeoutMs, signal);
|
|
631
|
+
}
|
|
632
|
+
async consume(signal, operation, interrupted) {
|
|
633
|
+
if (signal.aborted && interrupted)
|
|
634
|
+
return interrupted(signal.reason);
|
|
213
635
|
signal.throwIfAborted();
|
|
214
636
|
const previous = this.#turn;
|
|
215
637
|
let release;
|
|
216
638
|
const completed = new Promise((resolve) => { release = resolve; });
|
|
217
639
|
this.#turn = previous.then(() => completed);
|
|
218
640
|
try {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
641
|
+
try {
|
|
642
|
+
await interruptible(previous, signal);
|
|
643
|
+
signal.throwIfAborted();
|
|
644
|
+
}
|
|
645
|
+
catch (error) {
|
|
646
|
+
if (signal.aborted && interrupted)
|
|
647
|
+
return await interrupted(error);
|
|
648
|
+
throw error;
|
|
649
|
+
}
|
|
650
|
+
if (this.#eof === "retryable")
|
|
651
|
+
this.#ended = false;
|
|
652
|
+
this.#active = true;
|
|
653
|
+
try {
|
|
654
|
+
return await operation();
|
|
655
|
+
}
|
|
656
|
+
finally {
|
|
657
|
+
this.#active = false;
|
|
658
|
+
}
|
|
222
659
|
}
|
|
223
660
|
finally {
|
|
224
661
|
release();
|
|
@@ -237,6 +674,8 @@ class InputCursor {
|
|
|
237
674
|
if (this.#ended || this.#closed)
|
|
238
675
|
return { value: undefined, done: true };
|
|
239
676
|
if (!this.#read) {
|
|
677
|
+
this.#readSettled = false;
|
|
678
|
+
this.#readResult = undefined;
|
|
240
679
|
this.#read = Promise.resolve().then(() => this.#closed ? { value: undefined, done: true } : this.#readChunk ? this.#readChunk(maxBytes) : this.#iterator.next()).then((result) => {
|
|
241
680
|
if (result.done)
|
|
242
681
|
return { value: undefined, done: true };
|
|
@@ -245,13 +684,15 @@ class InputCursor {
|
|
|
245
684
|
if (this.#boundedReads && result.value.byteLength > this.#budget.limits.maxInputBytes - this.#produced)
|
|
246
685
|
this.#budget.fail("maxInputBytes");
|
|
247
686
|
this.#produced += result.value.byteLength;
|
|
248
|
-
return { value: new Uint8Array(result.value), done: false };
|
|
687
|
+
return { value: this.#boundedReads ? new Uint8Array(result.value) : result.value, done: false };
|
|
249
688
|
});
|
|
689
|
+
void this.#read.then(result => { this.#readSettled = true; this.#readResult = result; }, reason => { this.#readSettled = true; this.#readError = { reason }; });
|
|
250
690
|
}
|
|
251
691
|
try {
|
|
252
692
|
const result = await interruptible(this.#read, signal);
|
|
253
693
|
signal.throwIfAborted();
|
|
254
694
|
this.#read = undefined;
|
|
695
|
+
this.#readResult = undefined;
|
|
255
696
|
if (result.done)
|
|
256
697
|
this.#ended = true;
|
|
257
698
|
return result;
|
|
@@ -266,7 +707,7 @@ class InputCursor {
|
|
|
266
707
|
}
|
|
267
708
|
}
|
|
268
709
|
async close(signal) {
|
|
269
|
-
if (this.#ended && !this.seek) {
|
|
710
|
+
if (this.#ended && this.#eof === "terminal" && !this.seek) {
|
|
270
711
|
signal.throwIfAborted();
|
|
271
712
|
return;
|
|
272
713
|
}
|
|
@@ -285,36 +726,75 @@ class InputCursor {
|
|
|
285
726
|
signal.throwIfAborted();
|
|
286
727
|
}
|
|
287
728
|
}
|
|
288
|
-
class
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
729
|
+
class ReadBuffer {
|
|
730
|
+
scope;
|
|
731
|
+
maximum;
|
|
732
|
+
#buffer = new Uint8Array();
|
|
733
|
+
#reservation;
|
|
734
|
+
length = 0;
|
|
735
|
+
constructor(scope, maximum) {
|
|
736
|
+
this.scope = scope;
|
|
737
|
+
this.maximum = maximum;
|
|
738
|
+
}
|
|
739
|
+
append(byte) {
|
|
740
|
+
if (this.length === this.#buffer.length) {
|
|
741
|
+
const capacity = Math.min(this.maximum, Math.max(64, this.#buffer.length * 2));
|
|
742
|
+
const reservation = this.scope.reserve(capacity + 64, 1);
|
|
743
|
+
try {
|
|
744
|
+
const buffer = new Uint8Array(capacity);
|
|
745
|
+
buffer.set(this.#buffer);
|
|
746
|
+
reservation.commit(buffer);
|
|
747
|
+
this.#reservation?.release();
|
|
748
|
+
this.#reservation = reservation;
|
|
749
|
+
this.#buffer = buffer;
|
|
750
|
+
}
|
|
751
|
+
catch (error) {
|
|
752
|
+
reservation.release();
|
|
753
|
+
throw error;
|
|
754
|
+
}
|
|
298
755
|
}
|
|
756
|
+
this.#buffer[this.length++] = byte;
|
|
299
757
|
}
|
|
300
|
-
|
|
758
|
+
bytes() { return this.#buffer.subarray(0, this.length); }
|
|
759
|
+
}
|
|
760
|
+
function utf8Length(first) {
|
|
761
|
+
return first >= 0xc2 && first <= 0xdf ? 2 : first >= 0xe0 && first <= 0xef ? 3 : first >= 0xf0 && first <= 0xf4 ? 4 : 1;
|
|
762
|
+
}
|
|
763
|
+
function utf8Continuation(first, position, byte) {
|
|
764
|
+
if (byte < 0x80 || byte > 0xbf)
|
|
765
|
+
return false;
|
|
766
|
+
return position !== 1 || (first !== 0xe0 || byte >= 0xa0) && (first !== 0xed || byte < 0xa0)
|
|
767
|
+
&& (first !== 0xf0 || byte >= 0x90) && (first !== 0xf4 || byte < 0x90);
|
|
768
|
+
}
|
|
769
|
+
function displayWidth(bytes, offset) {
|
|
770
|
+
const first = bytes[offset];
|
|
771
|
+
const width = utf8Length(first);
|
|
772
|
+
let consumed = 1;
|
|
773
|
+
while (consumed < width && offset + consumed < bytes.length && utf8Continuation(first, consumed, bytes[offset + consumed]))
|
|
774
|
+
consumed++;
|
|
775
|
+
return consumed;
|
|
301
776
|
}
|
|
302
777
|
export class ShellInput {
|
|
303
778
|
budget;
|
|
779
|
+
descriptor;
|
|
304
780
|
#cursor;
|
|
305
781
|
#owned;
|
|
306
782
|
#lifetime = new AbortController();
|
|
307
783
|
#cleanupSignal;
|
|
308
784
|
signal;
|
|
785
|
+
#reads = new Set();
|
|
309
786
|
stat;
|
|
310
787
|
seek;
|
|
311
788
|
#closing;
|
|
312
|
-
constructor(source, budget, signal = budget.signal,
|
|
789
|
+
constructor(source, budget, signal = budget.signal, options) {
|
|
313
790
|
this.budget = budget;
|
|
314
791
|
this.#owned = !(source instanceof ShellInput);
|
|
315
|
-
this.#
|
|
792
|
+
if (!this.#owned && options !== undefined)
|
|
793
|
+
throw new TypeError("Borrowed input cannot replace cursor capabilities");
|
|
794
|
+
this.#cursor = source instanceof ShellInput ? source.#cursor : new InputCursor(source, options ?? {}, budget);
|
|
795
|
+
this.descriptor = source instanceof ShellInput ? source.descriptor : options?.descriptor;
|
|
316
796
|
this.#cleanupSignal = signal;
|
|
317
|
-
this.signal = AbortSignal.any([signal, this.#lifetime.signal]);
|
|
797
|
+
this.signal = AbortSignal.any([budget.signal, signal, this.#lifetime.signal]);
|
|
318
798
|
if (this.#cursor.stat)
|
|
319
799
|
this.stat = this.#cursor.stat;
|
|
320
800
|
if (this.#cursor.seek)
|
|
@@ -327,6 +807,22 @@ export class ShellInput {
|
|
|
327
807
|
});
|
|
328
808
|
};
|
|
329
809
|
}
|
|
810
|
+
get bufferedBytes() {
|
|
811
|
+
this.signal.throwIfAborted();
|
|
812
|
+
return this.#cursor.bufferedBytes;
|
|
813
|
+
}
|
|
814
|
+
readiness() {
|
|
815
|
+
this.signal.throwIfAborted();
|
|
816
|
+
const readiness = this.#cursor.readiness();
|
|
817
|
+
this.signal.throwIfAborted();
|
|
818
|
+
return readiness;
|
|
819
|
+
}
|
|
820
|
+
probeRead() {
|
|
821
|
+
this.signal.throwIfAborted();
|
|
822
|
+
const result = this.#cursor.probeRead();
|
|
823
|
+
this.signal.throwIfAborted();
|
|
824
|
+
return result;
|
|
825
|
+
}
|
|
330
826
|
get position() { return this.#cursor.position; }
|
|
331
827
|
read(maxBytes, callerSignal) {
|
|
332
828
|
const signal = AbortSignal.any([this.signal, callerSignal]);
|
|
@@ -388,9 +884,10 @@ export class ShellInput {
|
|
|
388
884
|
next() {
|
|
389
885
|
return this.#cursor.consume(this.signal, async () => {
|
|
390
886
|
const result = await this.#cursor.take(this.signal);
|
|
391
|
-
if (
|
|
392
|
-
|
|
393
|
-
|
|
887
|
+
if (result.done)
|
|
888
|
+
return result;
|
|
889
|
+
this.#cursor.position += result.value.byteLength;
|
|
890
|
+
return { done: false, value: new Uint8Array(result.value) };
|
|
394
891
|
});
|
|
395
892
|
}
|
|
396
893
|
[Symbol.asyncIterator]() {
|
|
@@ -417,7 +914,7 @@ export class ShellInput {
|
|
|
417
914
|
this.#cursor.position += end;
|
|
418
915
|
this.budget.source(end);
|
|
419
916
|
if (end)
|
|
420
|
-
chunks.push(result.value.subarray(0, end));
|
|
917
|
+
chunks.push(new Uint8Array(result.value.subarray(0, end)));
|
|
421
918
|
length += end;
|
|
422
919
|
if (newline >= 0)
|
|
423
920
|
break;
|
|
@@ -431,8 +928,86 @@ export class ShellInput {
|
|
|
431
928
|
return bytes;
|
|
432
929
|
});
|
|
433
930
|
}
|
|
434
|
-
|
|
435
|
-
|
|
931
|
+
async record(options = {}) {
|
|
932
|
+
this.signal.throwIfAborted();
|
|
933
|
+
const { delimiter = 10 } = options;
|
|
934
|
+
this.signal.throwIfAborted();
|
|
935
|
+
if (!Number.isInteger(delimiter) || delimiter < 0 || delimiter > 255)
|
|
936
|
+
throw new RangeError("Invalid raw record delimiter");
|
|
937
|
+
const scope = this.budget.values.scope();
|
|
938
|
+
let active = true;
|
|
939
|
+
let completion;
|
|
940
|
+
let resolve;
|
|
941
|
+
const finish = () => {
|
|
942
|
+
if (!completion || active)
|
|
943
|
+
return;
|
|
944
|
+
this.#reads.delete(release);
|
|
945
|
+
scope.close();
|
|
946
|
+
resolve();
|
|
947
|
+
};
|
|
948
|
+
const release = () => {
|
|
949
|
+
if (!completion) {
|
|
950
|
+
completion = new Promise(done => { resolve = done; });
|
|
951
|
+
finish();
|
|
952
|
+
}
|
|
953
|
+
return completion;
|
|
954
|
+
};
|
|
955
|
+
try {
|
|
956
|
+
scope.reserve(128, 2);
|
|
957
|
+
this.#reads.add(release);
|
|
958
|
+
const result = await this.#cursor.consume(this.signal, async () => {
|
|
959
|
+
const buffer = new ReadBuffer(scope, this.budget.limits.maxOutputBytes);
|
|
960
|
+
let chunk = new Uint8Array();
|
|
961
|
+
let offset = 0;
|
|
962
|
+
let pulls = 0;
|
|
963
|
+
let reason = "eof";
|
|
964
|
+
try {
|
|
965
|
+
while (true) {
|
|
966
|
+
this.signal.throwIfAborted();
|
|
967
|
+
if (offset === chunk.length) {
|
|
968
|
+
if (++pulls % 128 === 0)
|
|
969
|
+
await yieldTurn(this.signal);
|
|
970
|
+
const next = await this.#cursor.take(this.signal);
|
|
971
|
+
if (next.done)
|
|
972
|
+
break;
|
|
973
|
+
chunk = next.value;
|
|
974
|
+
offset = 0;
|
|
975
|
+
if (!chunk.length)
|
|
976
|
+
continue;
|
|
977
|
+
}
|
|
978
|
+
if (buffer.length >= this.budget.limits.maxOutputBytes)
|
|
979
|
+
this.budget.fail("maxOutputBytes");
|
|
980
|
+
const byte = chunk[offset++];
|
|
981
|
+
this.#cursor.position++;
|
|
982
|
+
buffer.append(byte);
|
|
983
|
+
if (byte === delimiter) {
|
|
984
|
+
reason = "delimiter";
|
|
985
|
+
break;
|
|
986
|
+
}
|
|
987
|
+
if (buffer.length % 1024 === 0)
|
|
988
|
+
await yieldTurn(this.signal);
|
|
989
|
+
}
|
|
990
|
+
const shellValue = shellValueFromBytes(buffer.bytes(), scope);
|
|
991
|
+
this.signal.throwIfAborted();
|
|
992
|
+
return Object.freeze({ shellValue, reason, release });
|
|
993
|
+
}
|
|
994
|
+
finally {
|
|
995
|
+
if (offset < chunk.length)
|
|
996
|
+
this.#cursor.remainder = chunk.subarray(offset);
|
|
997
|
+
}
|
|
998
|
+
});
|
|
999
|
+
this.signal.throwIfAborted();
|
|
1000
|
+
return result;
|
|
1001
|
+
}
|
|
1002
|
+
catch (error) {
|
|
1003
|
+
void release();
|
|
1004
|
+
this.signal.throwIfAborted();
|
|
1005
|
+
throw error;
|
|
1006
|
+
}
|
|
1007
|
+
finally {
|
|
1008
|
+
active = false;
|
|
1009
|
+
finish();
|
|
1010
|
+
}
|
|
436
1011
|
}
|
|
437
1012
|
/** Select uses read's escape rules without projecting raw REPLY bytes to text. */
|
|
438
1013
|
selectLine(allocation) {
|
|
@@ -543,160 +1118,359 @@ export class ShellInput {
|
|
|
543
1118
|
}
|
|
544
1119
|
});
|
|
545
1120
|
}
|
|
546
|
-
async
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
1121
|
+
async line(raw, options = {}) {
|
|
1122
|
+
const { count, delimiter = 10, byteCount = false, exact = false, timeoutMs } = options;
|
|
1123
|
+
this.signal.throwIfAborted();
|
|
1124
|
+
if (count !== undefined && (!Number.isSafeInteger(count) || count < 0))
|
|
1125
|
+
throw new RangeError("Invalid read count");
|
|
1126
|
+
if (!Number.isInteger(delimiter) || delimiter < 0 || delimiter > 255)
|
|
1127
|
+
throw new RangeError("Invalid read delimiter");
|
|
1128
|
+
const scope = this.budget.values.scope();
|
|
1129
|
+
let active = 1;
|
|
1130
|
+
let closed = false;
|
|
1131
|
+
let completion;
|
|
1132
|
+
let resolve;
|
|
1133
|
+
const finish = () => {
|
|
1134
|
+
if (!closed || active)
|
|
1135
|
+
return;
|
|
1136
|
+
this.#reads.delete(release);
|
|
1137
|
+
scope.close();
|
|
1138
|
+
resolve();
|
|
1139
|
+
};
|
|
1140
|
+
const release = () => {
|
|
1141
|
+
if (!completion) {
|
|
1142
|
+
completion = new Promise(done => { resolve = done; });
|
|
1143
|
+
closed = true;
|
|
1144
|
+
finish();
|
|
1145
|
+
}
|
|
1146
|
+
return completion;
|
|
1147
|
+
};
|
|
1148
|
+
const assertOpen = () => {
|
|
1149
|
+
this.signal.throwIfAborted();
|
|
1150
|
+
if (closed)
|
|
1151
|
+
throw new Error("Read result is closed");
|
|
1152
|
+
scope.assertOpen();
|
|
1153
|
+
};
|
|
552
1154
|
let chunk = new Uint8Array();
|
|
553
1155
|
let offset = 0;
|
|
554
|
-
let
|
|
555
|
-
let escaping = false;
|
|
556
|
-
let escapedCharacter = false;
|
|
557
|
-
let units = 0;
|
|
558
|
-
let pulls = 0;
|
|
559
|
-
let terminated = options.count === 0;
|
|
1156
|
+
let deadline;
|
|
560
1157
|
try {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
1158
|
+
scope.reserve(256, 3);
|
|
1159
|
+
this.#reads.add(release);
|
|
1160
|
+
deadline = this.#cursor.deadline(timeoutMs, this.signal, scope);
|
|
1161
|
+
const read = async () => {
|
|
1162
|
+
try {
|
|
1163
|
+
const buffer = new ReadBuffer(scope, this.budget.limits.maxOutputBytes);
|
|
1164
|
+
const escapedByteOffsets = [];
|
|
1165
|
+
let escaping = false;
|
|
1166
|
+
let visible = true;
|
|
1167
|
+
let units = 0;
|
|
1168
|
+
let consumed = 0;
|
|
1169
|
+
let checkpoint = 0;
|
|
1170
|
+
let pulls = 0;
|
|
1171
|
+
let terminated = count === 0;
|
|
1172
|
+
const outcome = { reason: terminated ? "count" : "eof" };
|
|
1173
|
+
const nextByte = async () => {
|
|
1174
|
+
this.signal.throwIfAborted();
|
|
1175
|
+
if (deadline?.expired()) {
|
|
1176
|
+
outcome.reason = "timeout";
|
|
1177
|
+
return undefined;
|
|
1178
|
+
}
|
|
1179
|
+
while (offset === chunk.length) {
|
|
1180
|
+
if (++pulls % 128 === 0)
|
|
1181
|
+
await yieldTurn(this.signal);
|
|
1182
|
+
let result;
|
|
1183
|
+
try {
|
|
1184
|
+
result = await this.#cursor.take(deadline?.signal ?? this.signal);
|
|
1185
|
+
}
|
|
1186
|
+
catch (error) {
|
|
1187
|
+
this.signal.throwIfAborted();
|
|
1188
|
+
if (deadline?.expired()) {
|
|
1189
|
+
outcome.reason = "timeout";
|
|
1190
|
+
return undefined;
|
|
1191
|
+
}
|
|
1192
|
+
throw error;
|
|
1193
|
+
}
|
|
1194
|
+
if (result.done) {
|
|
1195
|
+
if (deadline?.expired())
|
|
1196
|
+
outcome.reason = "timeout";
|
|
1197
|
+
return undefined;
|
|
1198
|
+
}
|
|
1199
|
+
chunk = result.value;
|
|
1200
|
+
offset = 0;
|
|
1201
|
+
if (deadline?.expired()) {
|
|
1202
|
+
outcome.reason = "timeout";
|
|
1203
|
+
return undefined;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
this.signal.throwIfAborted();
|
|
1207
|
+
this.#cursor.position++;
|
|
1208
|
+
return chunk[offset++];
|
|
1209
|
+
};
|
|
1210
|
+
const account = () => {
|
|
1211
|
+
if (++consumed > this.budget.limits.maxOutputBytes)
|
|
1212
|
+
this.budget.fail("maxOutputBytes");
|
|
1213
|
+
};
|
|
1214
|
+
while (!terminated) {
|
|
1215
|
+
if (consumed - checkpoint >= 1024) {
|
|
1216
|
+
checkpoint = consumed;
|
|
1217
|
+
await yieldTurn(this.signal);
|
|
1218
|
+
}
|
|
1219
|
+
const first = await nextByte();
|
|
1220
|
+
if (first === undefined) {
|
|
1221
|
+
if (escaping && visible && !buffer.length && outcome.reason === "eof")
|
|
1222
|
+
buffer.append(1);
|
|
1223
|
+
break;
|
|
1224
|
+
}
|
|
1225
|
+
const quoted = escaping;
|
|
1226
|
+
escaping = false;
|
|
1227
|
+
if (quoted && first === 10) {
|
|
1228
|
+
account();
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
if (!quoted && !raw && first === 92) {
|
|
1232
|
+
account();
|
|
1233
|
+
escaping = true;
|
|
1234
|
+
continue;
|
|
1235
|
+
}
|
|
1236
|
+
if (!quoted && !exact && first === delimiter) {
|
|
1237
|
+
terminated = true;
|
|
1238
|
+
outcome.reason = "delimiter";
|
|
1239
|
+
break;
|
|
1240
|
+
}
|
|
1241
|
+
account();
|
|
1242
|
+
if (!quoted && first === 0)
|
|
1243
|
+
continue;
|
|
1244
|
+
if (quoted && visible && first !== 0) {
|
|
1245
|
+
scope.reserve(64, 2);
|
|
1246
|
+
escapedByteOffsets.push(buffer.length);
|
|
1247
|
+
}
|
|
1248
|
+
if (visible && first === 0) {
|
|
1249
|
+
if (quoted && !buffer.length)
|
|
1250
|
+
buffer.append(1);
|
|
1251
|
+
visible = false;
|
|
1252
|
+
}
|
|
1253
|
+
else if (visible)
|
|
1254
|
+
buffer.append(first);
|
|
1255
|
+
const width = byteCount ? 1 : utf8Length(first);
|
|
1256
|
+
const committedLength = buffer.length;
|
|
1257
|
+
for (let position = 1; position < width; position++) {
|
|
1258
|
+
const next = await nextByte();
|
|
1259
|
+
if (next === undefined)
|
|
1260
|
+
break;
|
|
1261
|
+
account();
|
|
1262
|
+
if (next === 0)
|
|
1263
|
+
visible = false;
|
|
1264
|
+
else if (visible)
|
|
1265
|
+
buffer.append(next);
|
|
1266
|
+
if (!utf8Continuation(first, position, next))
|
|
1267
|
+
break;
|
|
1268
|
+
}
|
|
1269
|
+
if (outcome.reason === "timeout") {
|
|
1270
|
+
buffer.length = committedLength;
|
|
1271
|
+
break;
|
|
1272
|
+
}
|
|
1273
|
+
units++;
|
|
1274
|
+
if (units === count) {
|
|
1275
|
+
terminated = true;
|
|
1276
|
+
outcome.reason = "count";
|
|
1277
|
+
}
|
|
1278
|
+
if (units % 1024 === 0)
|
|
1279
|
+
await yieldTurn(this.signal);
|
|
568
1280
|
}
|
|
569
|
-
chunk = result.value;
|
|
570
|
-
offset = 0;
|
|
571
|
-
if (!chunk.length)
|
|
572
|
-
continue;
|
|
573
|
-
}
|
|
574
|
-
const byte = chunk[offset++];
|
|
575
|
-
this.#cursor.position++;
|
|
576
|
-
if (!options.exact && !escaping && byte === delimiter) {
|
|
577
|
-
terminated = true;
|
|
578
|
-
break;
|
|
579
|
-
}
|
|
580
|
-
if (++length > this.budget.limits.maxOutputBytes)
|
|
581
|
-
this.budget.fail("maxOutputBytes");
|
|
582
|
-
if (length % 1024 === 0) {
|
|
583
|
-
await yieldTurn(this.signal);
|
|
584
1281
|
this.signal.throwIfAborted();
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
1282
|
+
deadline?.close();
|
|
1283
|
+
const reason = outcome.reason;
|
|
1284
|
+
let bytes = buffer.bytes();
|
|
1285
|
+
if (reason === "timeout" && (escapedByteOffsets.length || escaping && visible)) {
|
|
1286
|
+
const projected = new ReadBuffer(scope, this.budget.limits.maxOutputBytes);
|
|
1287
|
+
let escape = 0;
|
|
1288
|
+
for (let position = 0; position < bytes.length; position++) {
|
|
1289
|
+
if (escapedByteOffsets[escape] === position) {
|
|
1290
|
+
projected.append(1);
|
|
1291
|
+
escapedByteOffsets[escape++] = projected.length;
|
|
1292
|
+
}
|
|
1293
|
+
projected.append(bytes[position]);
|
|
1294
|
+
if (position % 1024 === 0)
|
|
1295
|
+
await yieldTurn(this.signal);
|
|
1296
|
+
}
|
|
1297
|
+
if (escaping && visible) {
|
|
1298
|
+
scope.reserve(64, 2);
|
|
1299
|
+
escapedByteOffsets.push(projected.length);
|
|
1300
|
+
projected.append(1);
|
|
1301
|
+
}
|
|
1302
|
+
bytes = projected.bytes();
|
|
604
1303
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
1304
|
+
const shellValue = shellValueFromBytes(bytes, scope);
|
|
1305
|
+
const escaped = new Set();
|
|
1306
|
+
let escapeIndex = 0;
|
|
1307
|
+
let characters = 0;
|
|
1308
|
+
for (let start = 0; start < bytes.length; start += displayWidth(bytes, start)) {
|
|
1309
|
+
if (escapedByteOffsets[escapeIndex] === start) {
|
|
1310
|
+
escaped.add(characters);
|
|
1311
|
+
escapeIndex++;
|
|
1312
|
+
}
|
|
1313
|
+
if (++characters % 1024 === 0)
|
|
1314
|
+
await yieldTurn(this.signal);
|
|
1315
|
+
}
|
|
1316
|
+
Object.freeze(escapedByteOffsets);
|
|
1317
|
+
const result = {
|
|
1318
|
+
value: shellValueText(shellValue), shellValue, escaped, escapedByteOffsets, terminated, reason, release,
|
|
1319
|
+
fields: async (ifs, maximum) => {
|
|
1320
|
+
assertOpen();
|
|
1321
|
+
active++;
|
|
1322
|
+
try {
|
|
1323
|
+
if (maximum !== undefined && (!Number.isSafeInteger(maximum) || maximum < 0))
|
|
1324
|
+
throw new RangeError("Invalid read field count");
|
|
1325
|
+
const separators = shellValueBytes(ifs, scope);
|
|
1326
|
+
scope.reserve(128, 2);
|
|
1327
|
+
const keys = new Set();
|
|
1328
|
+
const escapedStart = (start) => {
|
|
1329
|
+
let lower = 0;
|
|
1330
|
+
let upper = escapedByteOffsets.length;
|
|
1331
|
+
while (lower < upper) {
|
|
1332
|
+
const middle = lower + Math.floor((upper - lower) / 2);
|
|
1333
|
+
const offset = escapedByteOffsets[middle];
|
|
1334
|
+
if (offset === start)
|
|
1335
|
+
return true;
|
|
1336
|
+
if (offset < start)
|
|
1337
|
+
lower = middle + 1;
|
|
1338
|
+
else
|
|
1339
|
+
upper = middle;
|
|
1340
|
+
}
|
|
1341
|
+
return false;
|
|
1342
|
+
};
|
|
1343
|
+
const width = (input, start) => {
|
|
1344
|
+
if (input === bytes && reason === "timeout" && bytes[start] === 1 && escapedStart(start + 1))
|
|
1345
|
+
return 2;
|
|
1346
|
+
if (byteCount || input === bytes && escapedStart(start))
|
|
1347
|
+
return 1;
|
|
1348
|
+
const length = displayWidth(input, start);
|
|
1349
|
+
return length === utf8Length(input[start]) ? length : 1;
|
|
1350
|
+
};
|
|
1351
|
+
const key = (input, start) => {
|
|
1352
|
+
let value = 1;
|
|
1353
|
+
for (let position = start, end = start + width(input, start); position < end; position++)
|
|
1354
|
+
value = value * 257 + input[position];
|
|
1355
|
+
return value;
|
|
1356
|
+
};
|
|
1357
|
+
let steps = 0;
|
|
1358
|
+
for (let start = 0; start < separators.length; start += width(separators, start)) {
|
|
1359
|
+
const value = key(separators, start);
|
|
1360
|
+
if (!keys.has(value)) {
|
|
1361
|
+
scope.reserve(32, 1);
|
|
1362
|
+
keys.add(value);
|
|
1363
|
+
}
|
|
1364
|
+
for (let position = start, end = start + width(separators, start); position < end; position++) {
|
|
1365
|
+
const byteKey = 257 + separators[position];
|
|
1366
|
+
if (!keys.has(byteKey)) {
|
|
1367
|
+
scope.reserve(32, 1);
|
|
1368
|
+
keys.add(byteKey);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
if (++steps % 1024 === 0) {
|
|
1372
|
+
await yieldTurn(this.signal);
|
|
1373
|
+
assertOpen();
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
const separator = (start) => !escapedStart(start)
|
|
1377
|
+
&& !(reason === "timeout" && bytes[start] === 1 && escapedStart(start + 1)) && keys.has(key(bytes, start));
|
|
1378
|
+
const whitespace = (start) => (bytes[start] === 32 || bytes[start] === 9 || bytes[start] === 10) && separator(start);
|
|
1379
|
+
let end = 0;
|
|
1380
|
+
for (let start = 0; start < bytes.length; start += width(bytes, start)) {
|
|
1381
|
+
if (!whitespace(start))
|
|
1382
|
+
end = start + width(bytes, start);
|
|
1383
|
+
if (++steps % 1024 === 0) {
|
|
1384
|
+
await yieldTurn(this.signal);
|
|
1385
|
+
assertOpen();
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
let position = 0;
|
|
1389
|
+
const advance = () => {
|
|
1390
|
+
assertOpen();
|
|
1391
|
+
position += width(bytes, position);
|
|
1392
|
+
if (++steps % 1024 === 0)
|
|
1393
|
+
return yieldTurn(this.signal);
|
|
1394
|
+
return undefined;
|
|
1395
|
+
};
|
|
1396
|
+
const fields = [];
|
|
1397
|
+
while (position < end && whitespace(position)) {
|
|
1398
|
+
const pending = advance();
|
|
1399
|
+
if (pending)
|
|
1400
|
+
await pending;
|
|
1401
|
+
}
|
|
1402
|
+
while (position < end && fields.length < (maximum ?? Number.MAX_SAFE_INTEGER)) {
|
|
1403
|
+
const start = position;
|
|
1404
|
+
while (position < end && !separator(position)) {
|
|
1405
|
+
const pending = advance();
|
|
1406
|
+
if (pending)
|
|
1407
|
+
await pending;
|
|
1408
|
+
}
|
|
1409
|
+
let fieldEnd = position;
|
|
1410
|
+
while (position < end && whitespace(position)) {
|
|
1411
|
+
const pending = advance();
|
|
1412
|
+
if (pending)
|
|
1413
|
+
await pending;
|
|
1414
|
+
}
|
|
1415
|
+
if (position < end && separator(position)) {
|
|
1416
|
+
const pending = advance();
|
|
1417
|
+
if (pending)
|
|
1418
|
+
await pending;
|
|
1419
|
+
}
|
|
1420
|
+
while (position < end && whitespace(position)) {
|
|
1421
|
+
const pending = advance();
|
|
1422
|
+
if (pending)
|
|
1423
|
+
await pending;
|
|
1424
|
+
}
|
|
1425
|
+
if (maximum !== undefined && fields.length === maximum - 1 && position < end)
|
|
1426
|
+
fieldEnd = end;
|
|
1427
|
+
scope.reserve(64, 1);
|
|
1428
|
+
fields.push(Object.freeze({ start, end: fieldEnd, value: shellValueFromBytes(bytes.subarray(start, fieldEnd), scope) }));
|
|
1429
|
+
}
|
|
1430
|
+
assertOpen();
|
|
1431
|
+
return Object.freeze(fields);
|
|
1432
|
+
}
|
|
1433
|
+
finally {
|
|
1434
|
+
active--;
|
|
1435
|
+
finish();
|
|
1436
|
+
}
|
|
1437
|
+
},
|
|
1438
|
+
};
|
|
1439
|
+
assertOpen();
|
|
1440
|
+
return Object.freeze(result);
|
|
608
1441
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
1442
|
+
finally {
|
|
1443
|
+
if (offset < chunk.length)
|
|
1444
|
+
this.#cursor.remainder = chunk.subarray(offset);
|
|
1445
|
+
}
|
|
1446
|
+
};
|
|
1447
|
+
const result = await this.#cursor.consume(deadline?.signal ?? this.signal, read, async (error) => {
|
|
1448
|
+
this.signal.throwIfAborted();
|
|
1449
|
+
if (deadline?.expired())
|
|
1450
|
+
return read();
|
|
1451
|
+
throw error;
|
|
1452
|
+
});
|
|
1453
|
+
this.signal.throwIfAborted();
|
|
1454
|
+
return result;
|
|
615
1455
|
}
|
|
616
1456
|
catch (error) {
|
|
617
|
-
|
|
618
|
-
|
|
1457
|
+
void release();
|
|
1458
|
+
this.signal.throwIfAborted();
|
|
619
1459
|
throw error;
|
|
620
1460
|
}
|
|
621
1461
|
finally {
|
|
622
|
-
|
|
623
|
-
|
|
1462
|
+
deadline?.close();
|
|
1463
|
+
active--;
|
|
1464
|
+
finish();
|
|
624
1465
|
}
|
|
625
1466
|
}
|
|
626
|
-
async readLine(raw) {
|
|
627
|
-
const chunks = [];
|
|
628
|
-
let length = 0;
|
|
629
|
-
let terminated = false;
|
|
630
|
-
while (true) {
|
|
631
|
-
const result = await this.#cursor.take(this.signal);
|
|
632
|
-
if (result.done)
|
|
633
|
-
break;
|
|
634
|
-
const newline = result.value.indexOf(10);
|
|
635
|
-
this.#cursor.position += newline < 0 ? result.value.byteLength : newline + 1;
|
|
636
|
-
const chunk = newline < 0 ? result.value : result.value.subarray(0, newline);
|
|
637
|
-
if (chunk.byteLength > this.budget.limits.maxOutputBytes - length)
|
|
638
|
-
this.budget.fail("maxOutputBytes");
|
|
639
|
-
chunks.push(new Uint8Array(chunk));
|
|
640
|
-
length += chunk.byteLength;
|
|
641
|
-
if (newline >= 0) {
|
|
642
|
-
if (newline + 1 < result.value.length)
|
|
643
|
-
this.#cursor.remainder = result.value.subarray(newline + 1);
|
|
644
|
-
let slashes = 0;
|
|
645
|
-
if (!raw) {
|
|
646
|
-
for (let chunkIndex = chunks.length - 1; chunkIndex >= 0; chunkIndex--) {
|
|
647
|
-
const bytes = chunks[chunkIndex];
|
|
648
|
-
let offset = bytes.length - 1;
|
|
649
|
-
while (offset >= 0 && bytes[offset] === 92) {
|
|
650
|
-
slashes++;
|
|
651
|
-
offset--;
|
|
652
|
-
}
|
|
653
|
-
if (offset >= 0)
|
|
654
|
-
break;
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
if (slashes % 2 === 1) {
|
|
658
|
-
while (chunks.at(-1)?.length === 0)
|
|
659
|
-
chunks.pop();
|
|
660
|
-
chunks[chunks.length - 1] = chunks.at(-1).subarray(0, -1);
|
|
661
|
-
length--;
|
|
662
|
-
}
|
|
663
|
-
else {
|
|
664
|
-
terminated = true;
|
|
665
|
-
break;
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
const bytes = new Uint8Array(length);
|
|
670
|
-
let offset = 0;
|
|
671
|
-
for (const chunk of chunks) {
|
|
672
|
-
bytes.set(chunk, offset);
|
|
673
|
-
offset += chunk.length;
|
|
674
|
-
}
|
|
675
|
-
const value = new TextDecoder().decode(bytes).replace(/\0/gu, "");
|
|
676
|
-
const escaped = new Set();
|
|
677
|
-
if (raw || !value.includes("\\"))
|
|
678
|
-
return { value, escaped, terminated };
|
|
679
|
-
const text = new ReadText();
|
|
680
|
-
let characters = 0;
|
|
681
|
-
for (let index = 0; index < value.length;) {
|
|
682
|
-
const slash = value.indexOf("\\", index);
|
|
683
|
-
const span = value.slice(index, slash < 0 ? value.length : slash);
|
|
684
|
-
text.append(span);
|
|
685
|
-
for (let pointOffset = 0; pointOffset < span.length; pointOffset += span.codePointAt(pointOffset) > 0xffff ? 2 : 1)
|
|
686
|
-
characters++;
|
|
687
|
-
if (slash < 0 || slash + 1 === value.length)
|
|
688
|
-
break;
|
|
689
|
-
const character = String.fromCodePoint(value.codePointAt(slash + 1));
|
|
690
|
-
escaped.add(characters++);
|
|
691
|
-
text.append(character);
|
|
692
|
-
index = slash + 1 + character.length;
|
|
693
|
-
}
|
|
694
|
-
return { value: text.finish(), escaped, terminated };
|
|
695
|
-
}
|
|
696
1467
|
close() {
|
|
697
1468
|
if (!this.#closing) {
|
|
698
1469
|
this.#lifetime.abort(new Error("Shell input view closed"));
|
|
699
|
-
|
|
1470
|
+
const pending = [...this.#reads].map(release => release());
|
|
1471
|
+
if (this.#owned)
|
|
1472
|
+
pending.push(this.#cursor.close(this.#cleanupSignal));
|
|
1473
|
+
this.#closing = Promise.all(pending).then(() => undefined);
|
|
700
1474
|
}
|
|
701
1475
|
return this.#closing;
|
|
702
1476
|
}
|