@ykdz/template 0.0.36 → 0.0.38

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 (687) hide show
  1. package/dist/application.d.ts +129 -13
  2. package/dist/application.js +286 -174
  3. package/dist/command-result.d.ts +9 -0
  4. package/dist/command-result.js +231 -0
  5. package/dist/main.d.ts +4 -1
  6. package/dist/main.js +319 -51
  7. package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.d.ts +122 -22
  8. package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.js +2081 -410
  9. package/node_modules/@ykdz/template-builtin-presets/dist/src/generation-record.d.ts +18 -3
  10. package/node_modules/@ykdz/template-builtin-presets/dist/src/generation-record.js +29 -5
  11. package/node_modules/@ykdz/template-builtin-presets/dist/src/index.d.ts +2 -2
  12. package/node_modules/@ykdz/template-builtin-presets/dist/src/index.js +1 -1
  13. package/node_modules/@ykdz/template-builtin-presets/dist/src/rust-bin/definition.d.ts +1 -1
  14. package/node_modules/@ykdz/template-builtin-presets/dist/src/rust-bin/definition.js +16 -15
  15. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.d.ts +1 -1
  16. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.js +1 -1
  17. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/typescript.d.ts +8 -6
  18. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/typescript.js +3 -3
  19. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/vue.d.ts +6 -4
  20. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/vue.js +8 -6
  21. package/node_modules/@ykdz/template-builtin-presets/dist/src/template-sources.d.ts +1 -1
  22. package/node_modules/@ykdz/template-builtin-presets/dist/src/template-sources.js +1 -1
  23. package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-cli/definition.d.ts +1 -1
  24. package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-cli/definition.js +34 -8
  25. package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-lib/definition.d.ts +1 -1
  26. package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-lib/definition.js +7 -7
  27. package/node_modules/@ykdz/template-builtin-presets/dist/src/vike-app/definition.d.ts +28 -2
  28. package/node_modules/@ykdz/template-builtin-presets/dist/src/vike-app/definition.js +164 -148
  29. package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-app/definition.d.ts +2 -2
  30. package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-app/definition.js +3 -3
  31. package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-hono-app/definition.d.ts +2 -2
  32. package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-hono-app/definition.js +22 -23
  33. package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/bin/semver.js +0 -0
  34. package/node_modules/{valibot → @ykdz/template-builtin-presets/node_modules/valibot}/README.md +1 -1
  35. package/node_modules/{valibot → @ykdz/template-builtin-presets/node_modules/valibot}/dist/index.cjs +427 -504
  36. package/node_modules/{valibot → @ykdz/template-builtin-presets/node_modules/valibot}/dist/index.d.cts +427 -32
  37. package/node_modules/{valibot → @ykdz/template-builtin-presets/node_modules/valibot}/dist/index.d.mts +427 -32
  38. package/node_modules/@ykdz/template-builtin-presets/node_modules/valibot/dist/index.min.cjs +2 -0
  39. package/node_modules/@ykdz/template-builtin-presets/node_modules/valibot/dist/index.min.mjs +2 -0
  40. package/node_modules/{valibot → @ykdz/template-builtin-presets/node_modules/valibot}/dist/index.mjs +418 -504
  41. package/node_modules/{valibot → @ykdz/template-builtin-presets/node_modules/valibot}/package.json +7 -2
  42. package/node_modules/@ykdz/template-builtin-presets/package.json +23 -10
  43. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment-diagnostics.yml +5 -5
  44. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment.yml +4 -4
  45. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-diagnostics.yml +4 -4
  46. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check.yml +3 -3
  47. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/AGENTS.md.template +17 -0
  48. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/devcontainer.json +4 -2
  49. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/pnpm-workspace.dynamic.txt +1 -0
  50. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/pnpm-workspace.yaml +5 -5
  51. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/scripts/check-toolchain-versions.ts +556 -0
  52. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/tsconfig.json +4 -6
  53. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/tsconfig.packing-hook.json +2 -3
  54. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/turbo.json +1 -1
  55. package/node_modules/@ykdz/template-builtin-presets/templates/rust-bin/turbo.json +19 -1
  56. package/node_modules/@ykdz/template-builtin-presets/templates/shared/devcontainer/node-pnpm.Dockerfile +4 -6
  57. package/node_modules/@ykdz/template-builtin-presets/templates/shared/oxc/tsconfig.config.json +2 -1
  58. package/node_modules/@ykdz/template-builtin-presets/templates/shared/vue/tsconfig.app.json +1 -1
  59. package/node_modules/@ykdz/template-builtin-presets/templates/shared/vue/tsconfig.node.json +2 -2
  60. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/readiness.ts +26 -5
  61. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/release.yml +1 -1
  62. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/root-tsconfig.json +2 -7
  63. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/tsconfig.json +2 -2
  64. package/node_modules/@ykdz/template-builtin-presets/templates/ts-lib/tsconfig.build.json +2 -1
  65. package/node_modules/@ykdz/template-builtin-presets/templates/ts-lib/tsconfig.json +2 -2
  66. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/scripts/reset.ts +3 -0
  67. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/src/db.ts +3 -4
  68. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/src/readiness.ts +6 -5
  69. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/src/storage.ts +93 -0
  70. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/tsconfig.build.json +8 -2
  71. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/tsconfig.json +3 -7
  72. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/turbo.json +1 -1
  73. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/drizzle.config.ts +5 -5
  74. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/tsconfig.build.json +2 -1
  75. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/tsconfig.json +2 -2
  76. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/.env.example +2 -1
  77. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/Dockerfile +25 -17
  78. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/Dockerfile.dockerignore +4 -4
  79. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/playwright.config.ts +5 -25
  80. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/scripts/check-standalone-deployment.ts +17 -15
  81. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/test/playwright-teardown.ts +6 -7
  82. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/tsconfig.app.json +1 -2
  83. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/tsconfig.json +0 -1
  84. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/tsconfig.node.json +2 -3
  85. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/tsconfig.test.json +0 -1
  86. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/turbo.json +0 -8
  87. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/vite.config.ts +3 -39
  88. package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/api/tsconfig.json +7 -2
  89. package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/root/playwright.config.ts +34 -0
  90. package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/{web → root}/test/e2e/app.spec.ts +3 -3
  91. package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/root/test/e2e/fixtures.ts +15 -0
  92. package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/root/tsconfig.json +3 -0
  93. package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/src/api.ts +1 -3
  94. package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/turbo.json +0 -4
  95. package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/vite.config.ts +2 -0
  96. package/node_modules/@ykdz/template-core/dist/ci-diagnostic-artifact.d.ts +2 -2
  97. package/node_modules/@ykdz/template-core/dist/ci-diagnostic-artifact.js +10 -5
  98. package/node_modules/@ykdz/template-core/dist/module-graph.d.ts +1 -1
  99. package/node_modules/@ykdz/template-core/dist/module-graph.js +7 -5
  100. package/node_modules/@ykdz/template-core/dist/package-contribution.d.ts +33 -10
  101. package/node_modules/@ykdz/template-core/dist/package-contribution.js +1 -1
  102. package/node_modules/@ykdz/template-core/dist/pnpm-workspace.yaml +10 -9
  103. package/node_modules/@ykdz/template-core/dist/preset-definition.d.ts +11 -0
  104. package/node_modules/@ykdz/template-core/dist/project-github.d.ts +7 -5
  105. package/node_modules/@ykdz/template-core/dist/project-github.js +4 -4
  106. package/node_modules/@ykdz/template-core/dist/project-linking-v2.d.ts +2 -2
  107. package/node_modules/@ykdz/template-core/dist/project-linking-v2.js +12 -6
  108. package/node_modules/@ykdz/template-core/dist/project-projection.d.ts +27 -0
  109. package/node_modules/@ykdz/template-core/dist/project-projection.js +394 -9
  110. package/node_modules/@ykdz/template-core/dist/release-toolchain-snapshot.d.ts +10 -0
  111. package/node_modules/@ykdz/template-core/dist/release-toolchain-snapshot.js +9 -0
  112. package/node_modules/@ykdz/template-core/dist/template-boundary-check.js +15 -9
  113. package/node_modules/@ykdz/template-core/node_modules/valibot/LICENSE.md +9 -0
  114. package/node_modules/@ykdz/template-core/node_modules/valibot/README.md +94 -0
  115. package/node_modules/@ykdz/template-core/node_modules/valibot/dist/index.cjs +7897 -0
  116. package/node_modules/@ykdz/template-core/node_modules/valibot/dist/index.d.cts +16579 -0
  117. package/node_modules/@ykdz/template-core/node_modules/valibot/dist/index.d.mts +16579 -0
  118. package/node_modules/@ykdz/template-core/node_modules/valibot/dist/index.min.cjs +2 -0
  119. package/node_modules/@ykdz/template-core/node_modules/valibot/dist/index.min.mjs +2 -0
  120. package/node_modules/@ykdz/template-core/node_modules/valibot/dist/index.mjs +7577 -0
  121. package/node_modules/@ykdz/template-core/node_modules/valibot/package.json +84 -0
  122. package/node_modules/@ykdz/template-core/package.json +0 -2
  123. package/node_modules/hosted-git-info/LICENSE +13 -0
  124. package/node_modules/hosted-git-info/README.md +138 -0
  125. package/node_modules/hosted-git-info/lib/from-url.js +122 -0
  126. package/node_modules/hosted-git-info/lib/hosts.js +229 -0
  127. package/node_modules/hosted-git-info/lib/index.js +227 -0
  128. package/node_modules/hosted-git-info/lib/parse-url.js +79 -0
  129. package/node_modules/hosted-git-info/package.json +57 -0
  130. package/node_modules/jsonc-parser/CHANGELOG.md +76 -0
  131. package/node_modules/jsonc-parser/LICENSE.md +21 -0
  132. package/node_modules/jsonc-parser/README.md +364 -0
  133. package/node_modules/jsonc-parser/SECURITY.md +41 -0
  134. package/node_modules/jsonc-parser/lib/esm/impl/edit.js +185 -0
  135. package/node_modules/jsonc-parser/lib/esm/impl/format.js +261 -0
  136. package/node_modules/jsonc-parser/lib/esm/impl/parser.js +659 -0
  137. package/node_modules/jsonc-parser/lib/esm/impl/scanner.js +443 -0
  138. package/node_modules/jsonc-parser/lib/esm/impl/string-intern.js +29 -0
  139. package/node_modules/jsonc-parser/lib/esm/main.d.ts +351 -0
  140. package/node_modules/jsonc-parser/lib/esm/main.js +178 -0
  141. package/node_modules/jsonc-parser/lib/umd/impl/edit.js +201 -0
  142. package/node_modules/jsonc-parser/lib/umd/impl/format.js +275 -0
  143. package/node_modules/jsonc-parser/lib/umd/impl/parser.js +682 -0
  144. package/node_modules/jsonc-parser/lib/umd/impl/scanner.js +456 -0
  145. package/node_modules/jsonc-parser/lib/umd/impl/string-intern.js +42 -0
  146. package/node_modules/jsonc-parser/lib/umd/main.d.ts +351 -0
  147. package/node_modules/jsonc-parser/lib/umd/main.js +194 -0
  148. package/node_modules/jsonc-parser/package.json +37 -0
  149. package/node_modules/lru-cache/LICENSE.md +55 -0
  150. package/node_modules/lru-cache/README.md +469 -0
  151. package/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.d.ts.map +1 -0
  152. package/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.js.map +1 -0
  153. package/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.d.ts +5 -0
  154. package/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.js +7 -0
  155. package/node_modules/lru-cache/dist/commonjs/browser/index.d.ts +1400 -0
  156. package/node_modules/lru-cache/dist/commonjs/browser/index.d.ts.map +1 -0
  157. package/node_modules/lru-cache/dist/commonjs/browser/index.js +1733 -0
  158. package/node_modules/lru-cache/dist/commonjs/browser/index.js.map +1 -0
  159. package/node_modules/lru-cache/dist/commonjs/browser/index.min.js +2 -0
  160. package/node_modules/lru-cache/dist/commonjs/browser/index.min.js.map +7 -0
  161. package/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts +12 -0
  162. package/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts.map +1 -0
  163. package/node_modules/lru-cache/dist/commonjs/browser/perf.js +10 -0
  164. package/node_modules/lru-cache/dist/commonjs/browser/perf.js.map +1 -0
  165. package/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.cjs.map +1 -0
  166. package/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.d.cts.map +1 -0
  167. package/node_modules/lru-cache/dist/commonjs/diagnostics-channel.d.ts +5 -0
  168. package/node_modules/lru-cache/dist/commonjs/diagnostics-channel.js +7 -0
  169. package/node_modules/lru-cache/dist/commonjs/index.d.ts +1400 -0
  170. package/node_modules/lru-cache/dist/commonjs/index.d.ts.map +1 -0
  171. package/node_modules/lru-cache/dist/commonjs/index.js +1733 -0
  172. package/node_modules/lru-cache/dist/commonjs/index.js.map +1 -0
  173. package/node_modules/lru-cache/dist/commonjs/index.min.js +2 -0
  174. package/node_modules/lru-cache/dist/commonjs/index.min.js.map +7 -0
  175. package/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.d.ts.map +1 -0
  176. package/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.js.map +1 -0
  177. package/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.d.ts +5 -0
  178. package/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.js +9 -0
  179. package/node_modules/lru-cache/dist/commonjs/node/index.d.ts +1400 -0
  180. package/node_modules/lru-cache/dist/commonjs/node/index.d.ts.map +1 -0
  181. package/node_modules/lru-cache/dist/commonjs/node/index.js +1733 -0
  182. package/node_modules/lru-cache/dist/commonjs/node/index.js.map +1 -0
  183. package/node_modules/lru-cache/dist/commonjs/node/index.min.js +2 -0
  184. package/node_modules/lru-cache/dist/commonjs/node/index.min.js.map +7 -0
  185. package/node_modules/lru-cache/dist/commonjs/node/perf.d.ts +12 -0
  186. package/node_modules/lru-cache/dist/commonjs/node/perf.d.ts.map +1 -0
  187. package/node_modules/lru-cache/dist/commonjs/node/perf.js +10 -0
  188. package/node_modules/lru-cache/dist/commonjs/node/perf.js.map +1 -0
  189. package/node_modules/lru-cache/dist/commonjs/package.json +3 -0
  190. package/node_modules/lru-cache/dist/commonjs/perf.d.ts +12 -0
  191. package/node_modules/lru-cache/dist/commonjs/perf.d.ts.map +1 -0
  192. package/node_modules/lru-cache/dist/commonjs/perf.js +10 -0
  193. package/node_modules/lru-cache/dist/commonjs/perf.js.map +1 -0
  194. package/node_modules/lru-cache/dist/commonjs/react-native/diagnostics-channel-react-native.d.ts.map +1 -0
  195. package/node_modules/lru-cache/dist/commonjs/react-native/diagnostics-channel-react-native.js.map +1 -0
  196. package/node_modules/lru-cache/dist/commonjs/react-native/diagnostics-channel.d.ts +5 -0
  197. package/node_modules/lru-cache/dist/commonjs/react-native/diagnostics-channel.js +7 -0
  198. package/node_modules/lru-cache/dist/commonjs/react-native/index.d.ts +1400 -0
  199. package/node_modules/lru-cache/dist/commonjs/react-native/index.d.ts.map +1 -0
  200. package/node_modules/lru-cache/dist/commonjs/react-native/index.js +1733 -0
  201. package/node_modules/lru-cache/dist/commonjs/react-native/index.js.map +1 -0
  202. package/node_modules/lru-cache/dist/commonjs/react-native/perf.d.ts +12 -0
  203. package/node_modules/lru-cache/dist/commonjs/react-native/perf.d.ts.map +1 -0
  204. package/node_modules/lru-cache/dist/commonjs/react-native/perf.js +10 -0
  205. package/node_modules/lru-cache/dist/commonjs/react-native/perf.js.map +1 -0
  206. package/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.d.ts.map +1 -0
  207. package/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.js.map +1 -0
  208. package/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.d.ts +5 -0
  209. package/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.js +4 -0
  210. package/node_modules/lru-cache/dist/esm/browser/index.d.ts +1400 -0
  211. package/node_modules/lru-cache/dist/esm/browser/index.d.ts.map +1 -0
  212. package/node_modules/lru-cache/dist/esm/browser/index.js +1729 -0
  213. package/node_modules/lru-cache/dist/esm/browser/index.js.map +1 -0
  214. package/node_modules/lru-cache/dist/esm/browser/index.min.js +2 -0
  215. package/node_modules/lru-cache/dist/esm/browser/index.min.js.map +7 -0
  216. package/node_modules/lru-cache/dist/esm/browser/perf.d.ts +12 -0
  217. package/node_modules/lru-cache/dist/esm/browser/perf.d.ts.map +1 -0
  218. package/node_modules/lru-cache/dist/esm/browser/perf.js +7 -0
  219. package/node_modules/lru-cache/dist/esm/browser/perf.js.map +1 -0
  220. package/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.d.mts.map +1 -0
  221. package/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.mjs.map +1 -0
  222. package/node_modules/lru-cache/dist/esm/diagnostics-channel.d.ts +5 -0
  223. package/node_modules/lru-cache/dist/esm/diagnostics-channel.js +19 -0
  224. package/node_modules/lru-cache/dist/esm/index.d.ts +1400 -0
  225. package/node_modules/lru-cache/dist/esm/index.d.ts.map +1 -0
  226. package/node_modules/lru-cache/dist/esm/index.js +1729 -0
  227. package/node_modules/lru-cache/dist/esm/index.js.map +1 -0
  228. package/node_modules/lru-cache/dist/esm/index.min.js +2 -0
  229. package/node_modules/lru-cache/dist/esm/index.min.js.map +7 -0
  230. package/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.d.ts.map +1 -0
  231. package/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.js.map +1 -0
  232. package/node_modules/lru-cache/dist/esm/node/diagnostics-channel.d.ts +5 -0
  233. package/node_modules/lru-cache/dist/esm/node/diagnostics-channel.js +6 -0
  234. package/node_modules/lru-cache/dist/esm/node/index.d.ts +1400 -0
  235. package/node_modules/lru-cache/dist/esm/node/index.d.ts.map +1 -0
  236. package/node_modules/lru-cache/dist/esm/node/index.js +1729 -0
  237. package/node_modules/lru-cache/dist/esm/node/index.js.map +1 -0
  238. package/node_modules/lru-cache/dist/esm/node/index.min.js +2 -0
  239. package/node_modules/lru-cache/dist/esm/node/index.min.js.map +7 -0
  240. package/node_modules/lru-cache/dist/esm/node/perf.d.ts +12 -0
  241. package/node_modules/lru-cache/dist/esm/node/perf.d.ts.map +1 -0
  242. package/node_modules/lru-cache/dist/esm/node/perf.js +7 -0
  243. package/node_modules/lru-cache/dist/esm/node/perf.js.map +1 -0
  244. package/node_modules/lru-cache/dist/esm/package.json +3 -0
  245. package/node_modules/lru-cache/dist/esm/perf.d.ts +12 -0
  246. package/node_modules/lru-cache/dist/esm/perf.d.ts.map +1 -0
  247. package/node_modules/lru-cache/dist/esm/perf.js +7 -0
  248. package/node_modules/lru-cache/dist/esm/perf.js.map +1 -0
  249. package/node_modules/lru-cache/dist/esm/react-native/diagnostics-channel-react-native.d.ts.map +1 -0
  250. package/node_modules/lru-cache/dist/esm/react-native/diagnostics-channel-react-native.js.map +1 -0
  251. package/node_modules/lru-cache/dist/esm/react-native/diagnostics-channel.d.ts +5 -0
  252. package/node_modules/lru-cache/dist/esm/react-native/diagnostics-channel.js +4 -0
  253. package/node_modules/lru-cache/dist/esm/react-native/index.d.ts +1400 -0
  254. package/node_modules/lru-cache/dist/esm/react-native/index.d.ts.map +1 -0
  255. package/node_modules/lru-cache/dist/esm/react-native/index.js +1729 -0
  256. package/node_modules/lru-cache/dist/esm/react-native/index.js.map +1 -0
  257. package/node_modules/lru-cache/dist/esm/react-native/perf.d.ts +12 -0
  258. package/node_modules/lru-cache/dist/esm/react-native/perf.d.ts.map +1 -0
  259. package/node_modules/lru-cache/dist/esm/react-native/perf.js +7 -0
  260. package/node_modules/lru-cache/dist/esm/react-native/perf.js.map +1 -0
  261. package/node_modules/lru-cache/package.json +180 -0
  262. package/node_modules/npm-package-arg/LICENSE +15 -0
  263. package/node_modules/npm-package-arg/README.md +104 -0
  264. package/node_modules/npm-package-arg/lib/npa.js +479 -0
  265. package/node_modules/npm-package-arg/node_modules/semver/LICENSE +15 -0
  266. package/node_modules/npm-package-arg/node_modules/semver/README.md +680 -0
  267. package/node_modules/npm-package-arg/node_modules/semver/bin/semver.js +195 -0
  268. package/node_modules/npm-package-arg/node_modules/semver/classes/comparator.js +143 -0
  269. package/node_modules/npm-package-arg/node_modules/semver/classes/index.js +7 -0
  270. package/node_modules/npm-package-arg/node_modules/semver/classes/range.js +577 -0
  271. package/node_modules/npm-package-arg/node_modules/semver/classes/semver.js +350 -0
  272. package/node_modules/npm-package-arg/node_modules/semver/functions/clean.js +8 -0
  273. package/node_modules/npm-package-arg/node_modules/semver/functions/cmp.js +54 -0
  274. package/node_modules/npm-package-arg/node_modules/semver/functions/coerce.js +62 -0
  275. package/node_modules/npm-package-arg/node_modules/semver/functions/compare-build.js +9 -0
  276. package/node_modules/npm-package-arg/node_modules/semver/functions/compare-loose.js +5 -0
  277. package/node_modules/npm-package-arg/node_modules/semver/functions/compare.js +7 -0
  278. package/node_modules/npm-package-arg/node_modules/semver/functions/diff.js +60 -0
  279. package/node_modules/npm-package-arg/node_modules/semver/functions/eq.js +5 -0
  280. package/node_modules/npm-package-arg/node_modules/semver/functions/gt.js +5 -0
  281. package/node_modules/npm-package-arg/node_modules/semver/functions/gte.js +5 -0
  282. package/node_modules/npm-package-arg/node_modules/semver/functions/inc.js +21 -0
  283. package/node_modules/npm-package-arg/node_modules/semver/functions/lt.js +5 -0
  284. package/node_modules/npm-package-arg/node_modules/semver/functions/lte.js +5 -0
  285. package/node_modules/npm-package-arg/node_modules/semver/functions/major.js +5 -0
  286. package/node_modules/npm-package-arg/node_modules/semver/functions/minor.js +5 -0
  287. package/node_modules/npm-package-arg/node_modules/semver/functions/neq.js +5 -0
  288. package/node_modules/npm-package-arg/node_modules/semver/functions/parse.js +18 -0
  289. package/node_modules/npm-package-arg/node_modules/semver/functions/patch.js +5 -0
  290. package/node_modules/npm-package-arg/node_modules/semver/functions/prerelease.js +8 -0
  291. package/node_modules/npm-package-arg/node_modules/semver/functions/rcompare.js +5 -0
  292. package/node_modules/npm-package-arg/node_modules/semver/functions/rsort.js +5 -0
  293. package/node_modules/npm-package-arg/node_modules/semver/functions/satisfies.js +12 -0
  294. package/node_modules/npm-package-arg/node_modules/semver/functions/sort.js +5 -0
  295. package/node_modules/npm-package-arg/node_modules/semver/functions/truncate.js +48 -0
  296. package/node_modules/npm-package-arg/node_modules/semver/functions/valid.js +8 -0
  297. package/node_modules/npm-package-arg/node_modules/semver/index.js +93 -0
  298. package/node_modules/npm-package-arg/node_modules/semver/internal/constants.js +37 -0
  299. package/node_modules/npm-package-arg/node_modules/semver/internal/debug.js +11 -0
  300. package/node_modules/npm-package-arg/node_modules/semver/internal/identifiers.js +29 -0
  301. package/node_modules/npm-package-arg/node_modules/semver/internal/lrucache.js +42 -0
  302. package/node_modules/npm-package-arg/node_modules/semver/internal/parse-options.js +17 -0
  303. package/node_modules/npm-package-arg/node_modules/semver/internal/re.js +223 -0
  304. package/node_modules/npm-package-arg/node_modules/semver/package.json +78 -0
  305. package/node_modules/npm-package-arg/node_modules/semver/preload.js +4 -0
  306. package/node_modules/npm-package-arg/node_modules/semver/range.bnf +17 -0
  307. package/node_modules/npm-package-arg/node_modules/semver/ranges/gtr.js +6 -0
  308. package/node_modules/npm-package-arg/node_modules/semver/ranges/intersects.js +9 -0
  309. package/node_modules/npm-package-arg/node_modules/semver/ranges/ltr.js +6 -0
  310. package/node_modules/npm-package-arg/node_modules/semver/ranges/max-satisfying.js +27 -0
  311. package/node_modules/npm-package-arg/node_modules/semver/ranges/min-satisfying.js +26 -0
  312. package/node_modules/npm-package-arg/node_modules/semver/ranges/min-version.js +63 -0
  313. package/node_modules/npm-package-arg/node_modules/semver/ranges/outside.js +82 -0
  314. package/node_modules/npm-package-arg/node_modules/semver/ranges/simplify.js +49 -0
  315. package/node_modules/npm-package-arg/node_modules/semver/ranges/subset.js +249 -0
  316. package/node_modules/npm-package-arg/node_modules/semver/ranges/to-comparators.js +10 -0
  317. package/node_modules/npm-package-arg/node_modules/semver/ranges/valid.js +13 -0
  318. package/node_modules/npm-package-arg/package.json +60 -0
  319. package/node_modules/picomatch/LICENSE +21 -0
  320. package/node_modules/picomatch/README.md +743 -0
  321. package/node_modules/picomatch/index.js +17 -0
  322. package/node_modules/picomatch/lib/constants.js +184 -0
  323. package/node_modules/picomatch/lib/parse.js +1425 -0
  324. package/node_modules/picomatch/lib/picomatch.js +361 -0
  325. package/node_modules/picomatch/lib/scan.js +403 -0
  326. package/node_modules/picomatch/lib/utils.js +72 -0
  327. package/node_modules/picomatch/package.json +83 -0
  328. package/node_modules/picomatch/posix.js +3 -0
  329. package/node_modules/proc-log/LICENSE +15 -0
  330. package/node_modules/proc-log/README.md +226 -0
  331. package/node_modules/proc-log/lib/index.js +158 -0
  332. package/node_modules/proc-log/package.json +46 -0
  333. package/node_modules/smol-toml/LICENSE +24 -0
  334. package/node_modules/smol-toml/README.md +605 -0
  335. package/node_modules/smol-toml/dist/date.d.ts +41 -0
  336. package/node_modules/smol-toml/dist/date.js +156 -0
  337. package/node_modules/smol-toml/dist/error.d.ts +37 -0
  338. package/node_modules/smol-toml/dist/error.js +67 -0
  339. package/node_modules/smol-toml/dist/extract.js +289 -0
  340. package/node_modules/smol-toml/dist/index.cjs +770 -0
  341. package/node_modules/smol-toml/dist/index.d.ts +44 -0
  342. package/node_modules/smol-toml/dist/index.js +34 -0
  343. package/node_modules/smol-toml/dist/parse.d.ts +73 -0
  344. package/node_modules/smol-toml/dist/parse.js +164 -0
  345. package/node_modules/smol-toml/dist/primitive.js +151 -0
  346. package/node_modules/smol-toml/dist/stringify.d.ts +32 -0
  347. package/node_modules/smol-toml/dist/stringify.js +219 -0
  348. package/node_modules/smol-toml/dist/struct.js +156 -0
  349. package/node_modules/smol-toml/dist/util.d.ts +43 -0
  350. package/node_modules/smol-toml/dist/util.js +57 -0
  351. package/node_modules/smol-toml/package.json +68 -0
  352. package/node_modules/typescript/bin/tsc +0 -0
  353. package/node_modules/typescript/bin/tsserver +0 -0
  354. package/node_modules/unbash/LICENSE +15 -0
  355. package/node_modules/unbash/README.md +289 -0
  356. package/node_modules/unbash/dist/ansi-c.d.ts +5 -0
  357. package/node_modules/unbash/dist/ansi-c.js +130 -0
  358. package/node_modules/unbash/dist/arithmetic.d.ts +11 -0
  359. package/node_modules/unbash/dist/arithmetic.js +530 -0
  360. package/node_modules/unbash/dist/assignment.d.ts +15 -0
  361. package/node_modules/unbash/dist/assignment.js +118 -0
  362. package/node_modules/unbash/dist/chars.d.ts +41 -0
  363. package/node_modules/unbash/dist/chars.js +41 -0
  364. package/node_modules/unbash/dist/command.d.ts +14 -0
  365. package/node_modules/unbash/dist/command.js +41 -0
  366. package/node_modules/unbash/dist/heredoc.d.ts +18 -0
  367. package/node_modules/unbash/dist/heredoc.js +25 -0
  368. package/node_modules/unbash/dist/internal-types.d.ts +87 -0
  369. package/node_modules/unbash/dist/internal-types.js +1 -0
  370. package/node_modules/unbash/dist/lexer.d.ts +202 -0
  371. package/node_modules/unbash/dist/lexer.js +3930 -0
  372. package/node_modules/unbash/dist/parser.d.ts +4 -0
  373. package/node_modules/unbash/dist/parser.js +1295 -0
  374. package/node_modules/unbash/dist/parts.d.ts +17 -0
  375. package/node_modules/unbash/dist/parts.js +82 -0
  376. package/node_modules/unbash/dist/printer.d.ts +2 -0
  377. package/node_modules/unbash/dist/printer.js +536 -0
  378. package/node_modules/unbash/dist/types.d.ts +494 -0
  379. package/node_modules/unbash/dist/types.js +1 -0
  380. package/node_modules/unbash/dist/word.d.ts +22 -0
  381. package/node_modules/unbash/dist/word.js +102 -0
  382. package/node_modules/unbash/package.json +80 -0
  383. package/node_modules/validate-npm-package-name/LICENSE +6 -0
  384. package/node_modules/validate-npm-package-name/README.md +120 -0
  385. package/node_modules/validate-npm-package-name/lib/builtin-modules.json +1 -0
  386. package/node_modules/validate-npm-package-name/lib/index.js +114 -0
  387. package/node_modules/validate-npm-package-name/package.json +59 -0
  388. package/node_modules/yaml/LICENSE +13 -0
  389. package/node_modules/yaml/README.md +172 -0
  390. package/node_modules/yaml/bin.mjs +11 -0
  391. package/node_modules/yaml/browser/dist/compose/compose-collection.js +88 -0
  392. package/node_modules/yaml/browser/dist/compose/compose-doc.js +43 -0
  393. package/node_modules/yaml/browser/dist/compose/compose-node.js +109 -0
  394. package/node_modules/yaml/browser/dist/compose/compose-scalar.js +86 -0
  395. package/node_modules/yaml/browser/dist/compose/composer.js +219 -0
  396. package/node_modules/yaml/browser/dist/compose/resolve-block-map.js +115 -0
  397. package/node_modules/yaml/browser/dist/compose/resolve-block-scalar.js +198 -0
  398. package/node_modules/yaml/browser/dist/compose/resolve-block-seq.js +49 -0
  399. package/node_modules/yaml/browser/dist/compose/resolve-end.js +37 -0
  400. package/node_modules/yaml/browser/dist/compose/resolve-flow-collection.js +207 -0
  401. package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +226 -0
  402. package/node_modules/yaml/browser/dist/compose/resolve-props.js +146 -0
  403. package/node_modules/yaml/browser/dist/compose/util-contains-newline.js +34 -0
  404. package/node_modules/yaml/browser/dist/compose/util-empty-scalar-position.js +26 -0
  405. package/node_modules/yaml/browser/dist/compose/util-flow-indent-check.js +15 -0
  406. package/node_modules/yaml/browser/dist/compose/util-map-includes.js +13 -0
  407. package/node_modules/yaml/browser/dist/doc/Document.js +335 -0
  408. package/node_modules/yaml/browser/dist/doc/anchors.js +71 -0
  409. package/node_modules/yaml/browser/dist/doc/applyReviver.js +55 -0
  410. package/node_modules/yaml/browser/dist/doc/createNode.js +88 -0
  411. package/node_modules/yaml/browser/dist/doc/directives.js +176 -0
  412. package/node_modules/yaml/browser/dist/errors.js +57 -0
  413. package/node_modules/yaml/browser/dist/index.js +17 -0
  414. package/node_modules/yaml/browser/dist/log.js +11 -0
  415. package/node_modules/yaml/browser/dist/nodes/Alias.js +118 -0
  416. package/node_modules/yaml/browser/dist/nodes/Collection.js +147 -0
  417. package/node_modules/yaml/browser/dist/nodes/Node.js +38 -0
  418. package/node_modules/yaml/browser/dist/nodes/Pair.js +36 -0
  419. package/node_modules/yaml/browser/dist/nodes/Scalar.js +24 -0
  420. package/node_modules/yaml/browser/dist/nodes/YAMLMap.js +144 -0
  421. package/node_modules/yaml/browser/dist/nodes/YAMLSeq.js +113 -0
  422. package/node_modules/yaml/browser/dist/nodes/addPairToJSMap.js +63 -0
  423. package/node_modules/yaml/browser/dist/nodes/identity.js +36 -0
  424. package/node_modules/yaml/browser/dist/nodes/toJS.js +37 -0
  425. package/node_modules/yaml/browser/dist/parse/cst-scalar.js +214 -0
  426. package/node_modules/yaml/browser/dist/parse/cst-stringify.js +61 -0
  427. package/node_modules/yaml/browser/dist/parse/cst-visit.js +97 -0
  428. package/node_modules/yaml/browser/dist/parse/cst.js +98 -0
  429. package/node_modules/yaml/browser/dist/parse/lexer.js +721 -0
  430. package/node_modules/yaml/browser/dist/parse/line-counter.js +39 -0
  431. package/node_modules/yaml/browser/dist/parse/parser.js +975 -0
  432. package/node_modules/yaml/browser/dist/public-api.js +102 -0
  433. package/node_modules/yaml/browser/dist/schema/Schema.js +37 -0
  434. package/node_modules/yaml/browser/dist/schema/common/map.js +17 -0
  435. package/node_modules/yaml/browser/dist/schema/common/null.js +15 -0
  436. package/node_modules/yaml/browser/dist/schema/common/seq.js +17 -0
  437. package/node_modules/yaml/browser/dist/schema/common/string.js +14 -0
  438. package/node_modules/yaml/browser/dist/schema/core/bool.js +19 -0
  439. package/node_modules/yaml/browser/dist/schema/core/float.js +43 -0
  440. package/node_modules/yaml/browser/dist/schema/core/int.js +38 -0
  441. package/node_modules/yaml/browser/dist/schema/core/schema.js +23 -0
  442. package/node_modules/yaml/browser/dist/schema/json/schema.js +62 -0
  443. package/node_modules/yaml/browser/dist/schema/tags.js +96 -0
  444. package/node_modules/yaml/browser/dist/schema/yaml-1.1/binary.js +58 -0
  445. package/node_modules/yaml/browser/dist/schema/yaml-1.1/bool.js +26 -0
  446. package/node_modules/yaml/browser/dist/schema/yaml-1.1/float.js +46 -0
  447. package/node_modules/yaml/browser/dist/schema/yaml-1.1/int.js +71 -0
  448. package/node_modules/yaml/browser/dist/schema/yaml-1.1/merge.js +67 -0
  449. package/node_modules/yaml/browser/dist/schema/yaml-1.1/omap.js +74 -0
  450. package/node_modules/yaml/browser/dist/schema/yaml-1.1/pairs.js +78 -0
  451. package/node_modules/yaml/browser/dist/schema/yaml-1.1/schema.js +39 -0
  452. package/node_modules/yaml/browser/dist/schema/yaml-1.1/set.js +93 -0
  453. package/node_modules/yaml/browser/dist/schema/yaml-1.1/timestamp.js +101 -0
  454. package/node_modules/yaml/browser/dist/stringify/foldFlowLines.js +146 -0
  455. package/node_modules/yaml/browser/dist/stringify/stringify.js +129 -0
  456. package/node_modules/yaml/browser/dist/stringify/stringifyCollection.js +153 -0
  457. package/node_modules/yaml/browser/dist/stringify/stringifyComment.js +20 -0
  458. package/node_modules/yaml/browser/dist/stringify/stringifyDocument.js +85 -0
  459. package/node_modules/yaml/browser/dist/stringify/stringifyNumber.js +25 -0
  460. package/node_modules/yaml/browser/dist/stringify/stringifyPair.js +150 -0
  461. package/node_modules/yaml/browser/dist/stringify/stringifyString.js +336 -0
  462. package/node_modules/yaml/browser/dist/util.js +11 -0
  463. package/node_modules/yaml/browser/dist/visit.js +233 -0
  464. package/node_modules/yaml/browser/index.js +5 -0
  465. package/node_modules/yaml/browser/package.json +3 -0
  466. package/node_modules/yaml/dist/cli.d.ts +8 -0
  467. package/node_modules/yaml/dist/cli.mjs +201 -0
  468. package/node_modules/yaml/dist/compose/compose-collection.d.ts +11 -0
  469. package/node_modules/yaml/dist/compose/compose-collection.js +90 -0
  470. package/node_modules/yaml/dist/compose/compose-doc.d.ts +7 -0
  471. package/node_modules/yaml/dist/compose/compose-doc.js +45 -0
  472. package/node_modules/yaml/dist/compose/compose-node.d.ts +29 -0
  473. package/node_modules/yaml/dist/compose/compose-node.js +112 -0
  474. package/node_modules/yaml/dist/compose/compose-scalar.d.ts +5 -0
  475. package/node_modules/yaml/dist/compose/compose-scalar.js +88 -0
  476. package/node_modules/yaml/dist/compose/composer.d.ts +63 -0
  477. package/node_modules/yaml/dist/compose/composer.js +224 -0
  478. package/node_modules/yaml/dist/compose/resolve-block-map.d.ts +6 -0
  479. package/node_modules/yaml/dist/compose/resolve-block-map.js +117 -0
  480. package/node_modules/yaml/dist/compose/resolve-block-scalar.d.ts +11 -0
  481. package/node_modules/yaml/dist/compose/resolve-block-scalar.js +200 -0
  482. package/node_modules/yaml/dist/compose/resolve-block-seq.d.ts +6 -0
  483. package/node_modules/yaml/dist/compose/resolve-block-seq.js +51 -0
  484. package/node_modules/yaml/dist/compose/resolve-end.d.ts +6 -0
  485. package/node_modules/yaml/dist/compose/resolve-end.js +39 -0
  486. package/node_modules/yaml/dist/compose/resolve-flow-collection.d.ts +7 -0
  487. package/node_modules/yaml/dist/compose/resolve-flow-collection.js +209 -0
  488. package/node_modules/yaml/dist/compose/resolve-flow-scalar.d.ts +10 -0
  489. package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +228 -0
  490. package/node_modules/yaml/dist/compose/resolve-props.d.ts +23 -0
  491. package/node_modules/yaml/dist/compose/resolve-props.js +148 -0
  492. package/node_modules/yaml/dist/compose/util-contains-newline.d.ts +2 -0
  493. package/node_modules/yaml/dist/compose/util-contains-newline.js +36 -0
  494. package/node_modules/yaml/dist/compose/util-empty-scalar-position.d.ts +2 -0
  495. package/node_modules/yaml/dist/compose/util-empty-scalar-position.js +28 -0
  496. package/node_modules/yaml/dist/compose/util-flow-indent-check.d.ts +3 -0
  497. package/node_modules/yaml/dist/compose/util-flow-indent-check.js +17 -0
  498. package/node_modules/yaml/dist/compose/util-map-includes.d.ts +4 -0
  499. package/node_modules/yaml/dist/compose/util-map-includes.js +15 -0
  500. package/node_modules/yaml/dist/doc/Document.d.ts +141 -0
  501. package/node_modules/yaml/dist/doc/Document.js +337 -0
  502. package/node_modules/yaml/dist/doc/anchors.d.ts +24 -0
  503. package/node_modules/yaml/dist/doc/anchors.js +76 -0
  504. package/node_modules/yaml/dist/doc/applyReviver.d.ts +9 -0
  505. package/node_modules/yaml/dist/doc/applyReviver.js +57 -0
  506. package/node_modules/yaml/dist/doc/createNode.d.ts +17 -0
  507. package/node_modules/yaml/dist/doc/createNode.js +90 -0
  508. package/node_modules/yaml/dist/doc/directives.d.ts +49 -0
  509. package/node_modules/yaml/dist/doc/directives.js +178 -0
  510. package/node_modules/yaml/dist/errors.d.ts +21 -0
  511. package/node_modules/yaml/dist/errors.js +62 -0
  512. package/node_modules/yaml/dist/index.d.ts +25 -0
  513. package/node_modules/yaml/dist/index.js +50 -0
  514. package/node_modules/yaml/dist/log.d.ts +3 -0
  515. package/node_modules/yaml/dist/log.js +19 -0
  516. package/node_modules/yaml/dist/nodes/Alias.d.ts +29 -0
  517. package/node_modules/yaml/dist/nodes/Alias.js +120 -0
  518. package/node_modules/yaml/dist/nodes/Collection.d.ts +73 -0
  519. package/node_modules/yaml/dist/nodes/Collection.js +151 -0
  520. package/node_modules/yaml/dist/nodes/Node.d.ts +53 -0
  521. package/node_modules/yaml/dist/nodes/Node.js +40 -0
  522. package/node_modules/yaml/dist/nodes/Pair.d.ts +22 -0
  523. package/node_modules/yaml/dist/nodes/Pair.js +39 -0
  524. package/node_modules/yaml/dist/nodes/Scalar.d.ts +47 -0
  525. package/node_modules/yaml/dist/nodes/Scalar.js +27 -0
  526. package/node_modules/yaml/dist/nodes/YAMLMap.d.ts +53 -0
  527. package/node_modules/yaml/dist/nodes/YAMLMap.js +147 -0
  528. package/node_modules/yaml/dist/nodes/YAMLSeq.d.ts +60 -0
  529. package/node_modules/yaml/dist/nodes/YAMLSeq.js +115 -0
  530. package/node_modules/yaml/dist/nodes/addPairToJSMap.d.ts +4 -0
  531. package/node_modules/yaml/dist/nodes/addPairToJSMap.js +65 -0
  532. package/node_modules/yaml/dist/nodes/identity.d.ts +23 -0
  533. package/node_modules/yaml/dist/nodes/identity.js +53 -0
  534. package/node_modules/yaml/dist/nodes/toJS.d.ts +29 -0
  535. package/node_modules/yaml/dist/nodes/toJS.js +39 -0
  536. package/node_modules/yaml/dist/options.d.ts +350 -0
  537. package/node_modules/yaml/dist/parse/cst-scalar.d.ts +64 -0
  538. package/node_modules/yaml/dist/parse/cst-scalar.js +218 -0
  539. package/node_modules/yaml/dist/parse/cst-stringify.d.ts +8 -0
  540. package/node_modules/yaml/dist/parse/cst-stringify.js +63 -0
  541. package/node_modules/yaml/dist/parse/cst-visit.d.ts +39 -0
  542. package/node_modules/yaml/dist/parse/cst-visit.js +99 -0
  543. package/node_modules/yaml/dist/parse/cst.d.ts +109 -0
  544. package/node_modules/yaml/dist/parse/cst.js +112 -0
  545. package/node_modules/yaml/dist/parse/lexer.d.ts +87 -0
  546. package/node_modules/yaml/dist/parse/lexer.js +723 -0
  547. package/node_modules/yaml/dist/parse/line-counter.d.ts +22 -0
  548. package/node_modules/yaml/dist/parse/line-counter.js +41 -0
  549. package/node_modules/yaml/dist/parse/parser.d.ts +84 -0
  550. package/node_modules/yaml/dist/parse/parser.js +980 -0
  551. package/node_modules/yaml/dist/public-api.d.ts +44 -0
  552. package/node_modules/yaml/dist/public-api.js +107 -0
  553. package/node_modules/yaml/dist/schema/Schema.d.ts +17 -0
  554. package/node_modules/yaml/dist/schema/Schema.js +39 -0
  555. package/node_modules/yaml/dist/schema/common/map.d.ts +2 -0
  556. package/node_modules/yaml/dist/schema/common/map.js +19 -0
  557. package/node_modules/yaml/dist/schema/common/null.d.ts +4 -0
  558. package/node_modules/yaml/dist/schema/common/null.js +17 -0
  559. package/node_modules/yaml/dist/schema/common/seq.d.ts +2 -0
  560. package/node_modules/yaml/dist/schema/common/seq.js +19 -0
  561. package/node_modules/yaml/dist/schema/common/string.d.ts +2 -0
  562. package/node_modules/yaml/dist/schema/common/string.js +16 -0
  563. package/node_modules/yaml/dist/schema/core/bool.d.ts +4 -0
  564. package/node_modules/yaml/dist/schema/core/bool.js +21 -0
  565. package/node_modules/yaml/dist/schema/core/float.d.ts +4 -0
  566. package/node_modules/yaml/dist/schema/core/float.js +47 -0
  567. package/node_modules/yaml/dist/schema/core/int.d.ts +4 -0
  568. package/node_modules/yaml/dist/schema/core/int.js +42 -0
  569. package/node_modules/yaml/dist/schema/core/schema.d.ts +1 -0
  570. package/node_modules/yaml/dist/schema/core/schema.js +25 -0
  571. package/node_modules/yaml/dist/schema/json/schema.d.ts +2 -0
  572. package/node_modules/yaml/dist/schema/json/schema.js +64 -0
  573. package/node_modules/yaml/dist/schema/json-schema.d.ts +69 -0
  574. package/node_modules/yaml/dist/schema/tags.d.ts +48 -0
  575. package/node_modules/yaml/dist/schema/tags.js +99 -0
  576. package/node_modules/yaml/dist/schema/types.d.ts +92 -0
  577. package/node_modules/yaml/dist/schema/yaml-1.1/binary.d.ts +2 -0
  578. package/node_modules/yaml/dist/schema/yaml-1.1/binary.js +70 -0
  579. package/node_modules/yaml/dist/schema/yaml-1.1/bool.d.ts +7 -0
  580. package/node_modules/yaml/dist/schema/yaml-1.1/bool.js +29 -0
  581. package/node_modules/yaml/dist/schema/yaml-1.1/float.d.ts +4 -0
  582. package/node_modules/yaml/dist/schema/yaml-1.1/float.js +50 -0
  583. package/node_modules/yaml/dist/schema/yaml-1.1/int.d.ts +5 -0
  584. package/node_modules/yaml/dist/schema/yaml-1.1/int.js +76 -0
  585. package/node_modules/yaml/dist/schema/yaml-1.1/merge.d.ts +9 -0
  586. package/node_modules/yaml/dist/schema/yaml-1.1/merge.js +71 -0
  587. package/node_modules/yaml/dist/schema/yaml-1.1/omap.d.ts +22 -0
  588. package/node_modules/yaml/dist/schema/yaml-1.1/omap.js +77 -0
  589. package/node_modules/yaml/dist/schema/yaml-1.1/pairs.d.ts +10 -0
  590. package/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +82 -0
  591. package/node_modules/yaml/dist/schema/yaml-1.1/schema.d.ts +1 -0
  592. package/node_modules/yaml/dist/schema/yaml-1.1/schema.js +41 -0
  593. package/node_modules/yaml/dist/schema/yaml-1.1/set.d.ts +28 -0
  594. package/node_modules/yaml/dist/schema/yaml-1.1/set.js +96 -0
  595. package/node_modules/yaml/dist/schema/yaml-1.1/timestamp.d.ts +6 -0
  596. package/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +105 -0
  597. package/node_modules/yaml/dist/stringify/foldFlowLines.d.ts +34 -0
  598. package/node_modules/yaml/dist/stringify/foldFlowLines.js +151 -0
  599. package/node_modules/yaml/dist/stringify/stringify.d.ts +21 -0
  600. package/node_modules/yaml/dist/stringify/stringify.js +132 -0
  601. package/node_modules/yaml/dist/stringify/stringifyCollection.d.ts +17 -0
  602. package/node_modules/yaml/dist/stringify/stringifyCollection.js +155 -0
  603. package/node_modules/yaml/dist/stringify/stringifyComment.d.ts +10 -0
  604. package/node_modules/yaml/dist/stringify/stringifyComment.js +24 -0
  605. package/node_modules/yaml/dist/stringify/stringifyDocument.d.ts +4 -0
  606. package/node_modules/yaml/dist/stringify/stringifyDocument.js +87 -0
  607. package/node_modules/yaml/dist/stringify/stringifyNumber.d.ts +2 -0
  608. package/node_modules/yaml/dist/stringify/stringifyNumber.js +27 -0
  609. package/node_modules/yaml/dist/stringify/stringifyPair.d.ts +3 -0
  610. package/node_modules/yaml/dist/stringify/stringifyPair.js +152 -0
  611. package/node_modules/yaml/dist/stringify/stringifyString.d.ts +9 -0
  612. package/node_modules/yaml/dist/stringify/stringifyString.js +338 -0
  613. package/node_modules/yaml/dist/test-events.d.ts +4 -0
  614. package/node_modules/yaml/dist/test-events.js +134 -0
  615. package/node_modules/yaml/dist/util.d.ts +16 -0
  616. package/node_modules/yaml/dist/util.js +28 -0
  617. package/node_modules/yaml/dist/visit.d.ts +102 -0
  618. package/node_modules/yaml/dist/visit.js +236 -0
  619. package/node_modules/yaml/package.json +97 -0
  620. package/node_modules/yaml/util.js +2 -0
  621. package/package.json +21 -19
  622. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/scripts/check-boundaries.ts +0 -12
  623. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/scripts/run-root-owned-task.ts +0 -69
  624. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/test/global-setup.ts +0 -21
  625. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/vitest.config.ts +0 -7
  626. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/devcontainer/docker-client.Dockerfile +0 -3
  627. package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/playwright.config.ts +0 -90
  628. package/node_modules/@ykdz/template-core/dist/toolchain-baseline-update.d.ts +0 -45
  629. package/node_modules/@ykdz/template-core/dist/toolchain-baseline-update.js +0 -27
  630. package/node_modules/@ykdz/template-core/dist/toolchain-resolution.d.ts +0 -37
  631. package/node_modules/@ykdz/template-core/dist/toolchain-resolution.js +0 -195
  632. package/node_modules/valibot/dist/index.min.cjs +0 -2
  633. package/node_modules/valibot/dist/index.min.mjs +0 -2
  634. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/LICENSE +0 -0
  635. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/README.md +0 -0
  636. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/classes/comparator.js +0 -0
  637. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/classes/index.js +0 -0
  638. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/classes/range.js +0 -0
  639. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/classes/semver.js +0 -0
  640. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/clean.js +0 -0
  641. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/cmp.js +0 -0
  642. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/coerce.js +0 -0
  643. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/compare-build.js +0 -0
  644. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/compare-loose.js +0 -0
  645. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/compare.js +0 -0
  646. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/diff.js +0 -0
  647. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/eq.js +0 -0
  648. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/gt.js +0 -0
  649. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/gte.js +0 -0
  650. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/inc.js +0 -0
  651. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/lt.js +0 -0
  652. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/lte.js +0 -0
  653. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/major.js +0 -0
  654. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/minor.js +0 -0
  655. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/neq.js +0 -0
  656. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/parse.js +0 -0
  657. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/patch.js +0 -0
  658. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/prerelease.js +0 -0
  659. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/rcompare.js +0 -0
  660. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/rsort.js +0 -0
  661. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/satisfies.js +0 -0
  662. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/sort.js +0 -0
  663. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/truncate.js +0 -0
  664. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/functions/valid.js +0 -0
  665. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/index.js +0 -0
  666. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/internal/constants.js +0 -0
  667. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/internal/debug.js +0 -0
  668. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/internal/identifiers.js +0 -0
  669. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/internal/lrucache.js +0 -0
  670. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/internal/parse-options.js +0 -0
  671. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/internal/re.js +0 -0
  672. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/package.json +0 -0
  673. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/preload.js +0 -0
  674. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/range.bnf +0 -0
  675. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/ranges/gtr.js +0 -0
  676. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/ranges/intersects.js +0 -0
  677. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/ranges/ltr.js +0 -0
  678. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/ranges/max-satisfying.js +0 -0
  679. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/ranges/min-satisfying.js +0 -0
  680. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/ranges/min-version.js +0 -0
  681. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/ranges/outside.js +0 -0
  682. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/ranges/simplify.js +0 -0
  683. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/ranges/subset.js +0 -0
  684. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/ranges/to-comparators.js +0 -0
  685. /package/node_modules/{semver → @ykdz/template-builtin-presets/node_modules/semver}/ranges/valid.js +0 -0
  686. /package/node_modules/{valibot → @ykdz/template-builtin-presets/node_modules/valibot}/LICENSE.md +0 -0
  687. /package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/src/{types.d.ts → types.ts} +0 -0
@@ -0,0 +1,3930 @@
1
+ import { decodeAnsiCQuoted } from "./ansi-c.js";
2
+ import { parseArithmeticExpression } from "./arithmetic.js";
3
+ import { WordImpl } from "./word.js";
4
+ import { CH_TAB, CH_NL, CH_SPACE, CH_BANG, CH_DQUOTE, CH_HASH, CH_DOLLAR, CH_PERCENT, CH_AMP, CH_SQUOTE, CH_LPAREN, CH_RPAREN, CH_STAR, CH_PLUS, CH_COMMA, CH_DASH, CH_SLASH, CH_0, CH_9, CH_COLON, CH_SEMI, CH_LT, CH_EQ, CH_GT, CH_QUESTION, CH_AT, CH_A, CH_Z, CH_LBRACKET, CH_BACKSLASH, CH_RBRACKET, CH_CARET, CH_UNDERSCORE, CH_BACKTICK, CH_a, CH_z, CH_LBRACE, CH_PIPE, CH_RBRACE, } from "./chars.js";
5
+ // Shared nesting budget for compound syntax and for structure materialized on demand
6
+ // (sub-field words, nested substitution scripts). 256 nested levels stay lossless; past
7
+ // that the lexer stops descending instead of overflowing the stack: deeper sub-fields
8
+ // keep their raw text without parts, and substitution scripts stay unresolved past one
9
+ // boundary script flagged with "maximum substitution nesting depth exceeded". The
10
+ // iterative scanners additionally report the depth error where their counters can see
11
+ // it (nested `${`, `$((`, and `$(`); cut-offs those counters cannot see (e.g. chains
12
+ // interleaved with double quotes) degrade to plain text without an error.
13
+ export const MAX_SYNTAX_NESTING = 256;
14
+ export const Token = {
15
+ Word: 0,
16
+ Assignment: 1,
17
+ Semi: 2,
18
+ Newline: 3,
19
+ Pipe: 4,
20
+ And: 5,
21
+ Or: 6,
22
+ Amp: 7,
23
+ LParen: 8,
24
+ RParen: 9,
25
+ LBrace: 10,
26
+ RBrace: 11,
27
+ Bang: 12,
28
+ If: 13,
29
+ Then: 14,
30
+ Else: 15,
31
+ Elif: 16,
32
+ Fi: 17,
33
+ Do: 18,
34
+ Done: 19,
35
+ For: 20,
36
+ While: 21,
37
+ Until: 22,
38
+ In: 23,
39
+ Case: 24,
40
+ Esac: 25,
41
+ Function: 26,
42
+ DoubleSemi: 27,
43
+ SemiAmp: 28,
44
+ DoubleSemiAmp: 29,
45
+ Select: 30,
46
+ DblLBracket: 31,
47
+ DblRBracket: 32,
48
+ EOF: 33,
49
+ ArithCmd: 34,
50
+ Coproc: 35,
51
+ Redirect: 36,
52
+ };
53
+ export class TokenValue {
54
+ token = Token.EOF;
55
+ // Materialized token value, or null for word tokens whose value has not been
56
+ // requested yet (computed from [pos, end) on demand via the owning lexer).
57
+ _value = "";
58
+ _owner;
59
+ pos = 0;
60
+ end = 0;
61
+ fileDescriptor = undefined;
62
+ variableName = undefined;
63
+ descriptorEnd = 0;
64
+ delimiterQuoted = false;
65
+ content = undefined;
66
+ targetPos = 0;
67
+ targetEnd = 0;
68
+ assignmentOperatorPos = -1;
69
+ // True when `value` is exactly the raw source span [pos, end) — lets consumers
70
+ // reuse the string instead of slicing the source again.
71
+ raw = false;
72
+ // True when a word contains no quoting, escaped characters, or expansions.
73
+ // Backslash-newline continuations preserve keyword eligibility.
74
+ keywordEligible = false;
75
+ constructor(owner = null) {
76
+ this._owner = owner;
77
+ }
78
+ get value() {
79
+ return (this._value ?? (this._value = this._owner === null ? "" : this._owner._tokenValue(this.pos, this.end, this.raw)));
80
+ }
81
+ set value(v) {
82
+ this._value = v;
83
+ }
84
+ reset() {
85
+ this.token = Token.EOF;
86
+ this._value = "";
87
+ this.pos = 0;
88
+ this.end = 0;
89
+ this.fileDescriptor = undefined;
90
+ this.variableName = undefined;
91
+ this.descriptorEnd = 0;
92
+ this.delimiterQuoted = false;
93
+ this.content = undefined;
94
+ this.targetPos = 0;
95
+ this.targetEnd = 0;
96
+ this.assignmentOperatorPos = -1;
97
+ this.raw = false;
98
+ this.keywordEligible = false;
99
+ }
100
+ copyFrom(other) {
101
+ this.token = other.token;
102
+ this._value = other._value;
103
+ this.pos = other.pos;
104
+ this.end = other.end;
105
+ this.fileDescriptor = other.fileDescriptor;
106
+ this.variableName = other.variableName;
107
+ this.descriptorEnd = other.descriptorEnd;
108
+ this.delimiterQuoted = other.delimiterQuoted;
109
+ this.content = other.content;
110
+ this.targetPos = other.targetPos;
111
+ this.targetEnd = other.targetEnd;
112
+ this.assignmentOperatorPos = other.assignmentOperatorPos;
113
+ this.raw = other.raw;
114
+ this.keywordEligible = other.keywordEligible;
115
+ }
116
+ }
117
+ const RESERVED_WORDS = new Map([
118
+ ["if", Token.If],
119
+ ["then", Token.Then],
120
+ ["else", Token.Else],
121
+ ["elif", Token.Elif],
122
+ ["fi", Token.Fi],
123
+ ["do", Token.Do],
124
+ ["done", Token.Done],
125
+ ["for", Token.For],
126
+ ["while", Token.While],
127
+ ["until", Token.Until],
128
+ ["in", Token.In],
129
+ ["case", Token.Case],
130
+ ["esac", Token.Esac],
131
+ ["function", Token.Function],
132
+ ["select", Token.Select],
133
+ ["coproc", Token.Coproc],
134
+ ["!", Token.Bang],
135
+ ["{", Token.LBrace],
136
+ ["}", Token.RBrace],
137
+ ]);
138
+ // `name()` is a syntax error for these; `time`, `[[` and `]]` are matched positionally
139
+ // rather than through RESERVED_WORDS, and a name spelled like an assignment reads as a prefix.
140
+ export function requiresFunctionKeyword(name) {
141
+ return RESERVED_WORDS.has(name) || name === "time" || name === "[[" || name === "]]" || name.includes("=");
142
+ }
143
+ // Combined character type table — bit 0: metachar, bit 1: word-special
144
+ const charType = new Uint8Array(128);
145
+ charType[CH_PIPE] = 1;
146
+ charType[CH_AMP] = 1;
147
+ charType[CH_SEMI] = 1;
148
+ charType[CH_LPAREN] = 1;
149
+ charType[CH_RPAREN] = 1;
150
+ charType[CH_LT] = 1;
151
+ charType[CH_GT] = 1;
152
+ charType[CH_SPACE] = 1;
153
+ charType[CH_TAB] = 1;
154
+ charType[CH_NL] = 1;
155
+ charType[CH_BACKSLASH] = 2;
156
+ charType[CH_SQUOTE] = 2;
157
+ charType[CH_DQUOTE] = 2;
158
+ charType[CH_DOLLAR] = 2;
159
+ charType[CH_BACKTICK] = 2;
160
+ charType[CH_LBRACE] = 2;
161
+ export function skipLineContinuations(source, pos, end) {
162
+ while (pos + 1 < end && source.charCodeAt(pos) === CH_BACKSLASH && source.charCodeAt(pos + 1) === CH_NL)
163
+ pos += 2;
164
+ return pos;
165
+ }
166
+ const arithmeticWordDelimiter = new Uint8Array(128);
167
+ for (const ch of [
168
+ CH_TAB,
169
+ CH_NL,
170
+ CH_SPACE,
171
+ CH_BANG,
172
+ CH_PERCENT,
173
+ CH_AMP,
174
+ CH_LPAREN,
175
+ CH_RPAREN,
176
+ CH_STAR,
177
+ CH_PLUS,
178
+ CH_COMMA,
179
+ CH_DASH,
180
+ CH_SLASH,
181
+ CH_COLON,
182
+ CH_LT,
183
+ CH_EQ,
184
+ CH_GT,
185
+ CH_QUESTION,
186
+ CH_CARET,
187
+ CH_PIPE,
188
+ ]) {
189
+ arithmeticWordDelimiter[ch] = 1;
190
+ }
191
+ export function hasEmbeddedWordStructure(source, start, end) {
192
+ for (let pos = start; pos < end; pos++) {
193
+ const ch = source.charCodeAt(pos);
194
+ if (ch === CH_BACKSLASH ||
195
+ ch === CH_SQUOTE ||
196
+ ch === CH_DQUOTE ||
197
+ ch === CH_DOLLAR ||
198
+ ch === CH_BACKTICK ||
199
+ ((ch === CH_LT || ch === CH_GT) && pos + 1 < end && source.charCodeAt(pos + 1) === CH_LPAREN)) {
200
+ return true;
201
+ }
202
+ }
203
+ return false;
204
+ }
205
+ function findUnnested(s, target, pairTernaries = false, findNestedEnd) {
206
+ let depth = 0;
207
+ let ternaryDepth = 0;
208
+ for (let i = 0; i < s.length; i++) {
209
+ const c = s.charCodeAt(i);
210
+ if (c === CH_BACKSLASH) {
211
+ i++;
212
+ continue;
213
+ }
214
+ if (c === CH_LBRACE) {
215
+ depth++;
216
+ continue;
217
+ }
218
+ if (c === CH_RBRACE) {
219
+ if (depth > 0)
220
+ depth--;
221
+ continue;
222
+ }
223
+ if (c === CH_SQUOTE) {
224
+ i++;
225
+ while (i < s.length && s.charCodeAt(i) !== CH_SQUOTE)
226
+ i++;
227
+ continue;
228
+ }
229
+ if (c === CH_DQUOTE) {
230
+ i++;
231
+ while (i < s.length) {
232
+ const quoted = s.charCodeAt(i);
233
+ if (quoted === CH_DQUOTE)
234
+ break;
235
+ if (quoted === CH_BACKSLASH) {
236
+ i += 2;
237
+ continue;
238
+ }
239
+ const nestedEnd = findNestedEnd?.(i, true) ?? i;
240
+ if (nestedEnd > i) {
241
+ i = nestedEnd;
242
+ continue;
243
+ }
244
+ i++;
245
+ }
246
+ continue;
247
+ }
248
+ const nestedEnd = findNestedEnd?.(i, false) ?? i;
249
+ if (nestedEnd > i) {
250
+ i = nestedEnd - 1;
251
+ continue;
252
+ }
253
+ if (pairTernaries && depth === 0) {
254
+ if (c === CH_QUESTION) {
255
+ ternaryDepth++;
256
+ continue;
257
+ }
258
+ if (c === CH_COLON && ternaryDepth > 0) {
259
+ ternaryDepth--;
260
+ continue;
261
+ }
262
+ }
263
+ if (c === target && depth === 0)
264
+ return i;
265
+ }
266
+ return -1;
267
+ }
268
+ // Lookup: identifier chars (a-z, A-Z, 0-9, _) — bit 0: start, bit 1: continue
269
+ const isIdChar = new Uint8Array(128);
270
+ for (let i = CH_a; i <= CH_z; i++)
271
+ isIdChar[i] = 3;
272
+ for (let i = CH_A; i <= CH_Z; i++)
273
+ isIdChar[i] = 3;
274
+ for (let i = CH_0; i <= CH_9; i++)
275
+ isIdChar[i] = 2;
276
+ isIdChar[CH_UNDERSCORE] = 3;
277
+ const extglobPrefix = new Uint8Array(128);
278
+ extglobPrefix[CH_QUESTION] = 1;
279
+ extglobPrefix[CH_AT] = 1;
280
+ extglobPrefix[CH_STAR] = 1;
281
+ extglobPrefix[CH_PLUS] = 1;
282
+ extglobPrefix[CH_BANG] = 1;
283
+ extglobPrefix[CH_EQ] = 1;
284
+ const extglobOp = {
285
+ [CH_QUESTION]: "?",
286
+ [CH_AT]: "@",
287
+ [CH_STAR]: "*",
288
+ [CH_PLUS]: "+",
289
+ [CH_BANG]: "!",
290
+ };
291
+ function isDQChild(p) {
292
+ const t = p.type;
293
+ return (t === "Literal" ||
294
+ t === "SimpleExpansion" ||
295
+ t === "ParameterExpansion" ||
296
+ t === "CommandExpansion" ||
297
+ t === "ArithmeticExpansion");
298
+ }
299
+ function isAllDigitsRange(src, start, end) {
300
+ for (let i = start; i < end; i++) {
301
+ const c = src.charCodeAt(i);
302
+ if (c < CH_0 || c > CH_9)
303
+ return false;
304
+ }
305
+ return end > start;
306
+ }
307
+ const ASSIGNMENT_INVALID = -1;
308
+ const ASSIGNMENT_NAME_START = 0;
309
+ const ASSIGNMENT_NAME = 1;
310
+ const ASSIGNMENT_AFTER_INDEX = 2;
311
+ const ASSIGNMENT_AFTER_PLUS = 3;
312
+ const ASSIGNMENT_INDEX_BASE = 4;
313
+ function isMatchedAssignment(state) {
314
+ return state < ASSIGNMENT_INVALID;
315
+ }
316
+ function assignmentOperatorPos(state) {
317
+ return -state - 2;
318
+ }
319
+ function scanAssignmentPrefix(src, start, end, initialState) {
320
+ let state = initialState;
321
+ for (let i = start; i < end && state >= 0; i++) {
322
+ const c = src.charCodeAt(i);
323
+ if (state >= ASSIGNMENT_INDEX_BASE) {
324
+ if (c === CH_LBRACKET)
325
+ state++;
326
+ else if (c === CH_RBRACKET && --state === ASSIGNMENT_INDEX_BASE)
327
+ state = ASSIGNMENT_AFTER_INDEX;
328
+ }
329
+ else if (state === ASSIGNMENT_NAME_START) {
330
+ state = c < 128 && isIdChar[c] & 1 ? ASSIGNMENT_NAME : ASSIGNMENT_INVALID;
331
+ }
332
+ else if (state === ASSIGNMENT_NAME) {
333
+ if (c < 128 && isIdChar[c] & 2)
334
+ continue;
335
+ if (c === CH_LBRACKET)
336
+ state = ASSIGNMENT_INDEX_BASE + 1;
337
+ else if (c === CH_PLUS)
338
+ state = ASSIGNMENT_AFTER_PLUS;
339
+ else
340
+ state = c === CH_EQ ? -i - 2 : ASSIGNMENT_INVALID;
341
+ }
342
+ else if (state === ASSIGNMENT_AFTER_INDEX) {
343
+ if (c === CH_PLUS)
344
+ state = ASSIGNMENT_AFTER_PLUS;
345
+ else
346
+ state = c === CH_EQ ? -i - 2 : ASSIGNMENT_INVALID;
347
+ }
348
+ else {
349
+ state = c === CH_EQ ? -i - 2 : ASSIGNMENT_INVALID;
350
+ }
351
+ }
352
+ return state;
353
+ }
354
+ const NO_EXPANSIONS = [];
355
+ function setToken(out, token, value, pos = 0, end = 0) {
356
+ out.token = token;
357
+ out._value = value;
358
+ out.pos = pos;
359
+ out.end = end;
360
+ out.fileDescriptor = undefined;
361
+ out.variableName = undefined;
362
+ out.descriptorEnd = 0;
363
+ out.delimiterQuoted = false;
364
+ out.content = undefined;
365
+ out.assignmentOperatorPos = -1;
366
+ out.raw = false;
367
+ out.keywordEligible = false;
368
+ }
369
+ // Word token over [pos, end) whose value materializes on first access.
370
+ function setSpanToken(out, token, pos, end, raw) {
371
+ out.token = token;
372
+ out._value = null;
373
+ out.pos = pos;
374
+ out.end = end;
375
+ out.fileDescriptor = undefined;
376
+ out.variableName = undefined;
377
+ out.descriptorEnd = 0;
378
+ out.delimiterQuoted = false;
379
+ out.content = undefined;
380
+ out.assignmentOperatorPos = -1;
381
+ out.raw = raw;
382
+ out.keywordEligible = false;
383
+ }
384
+ // Positional reserved-word match over src[start, start+len) — no slice. Mirrors
385
+ // RESERVED_WORDS; first-char dispatch plus length checks reject fast.
386
+ function matchReservedWord(src, start, len) {
387
+ switch (src.charCodeAt(start)) {
388
+ case CH_BANG:
389
+ return len === 1 ? Token.Bang : undefined;
390
+ case CH_LBRACE:
391
+ return len === 1 ? Token.LBrace : undefined;
392
+ case CH_RBRACE:
393
+ return len === 1 ? Token.RBrace : undefined;
394
+ case 0x69 /* i */: {
395
+ if (len !== 2)
396
+ return undefined;
397
+ const c = src.charCodeAt(start + 1);
398
+ return c === 0x66 /* f */ ? Token.If : c === 0x6e /* n */ ? Token.In : undefined;
399
+ }
400
+ case 0x66 /* f */:
401
+ if (len === 2)
402
+ return src.charCodeAt(start + 1) === 0x69 /* i */ ? Token.Fi : undefined;
403
+ if (len === 3)
404
+ return src.startsWith("for", start) ? Token.For : undefined;
405
+ if (len === 8)
406
+ return src.startsWith("function", start) ? Token.Function : undefined;
407
+ return undefined;
408
+ case 0x74 /* t */:
409
+ return len === 4 && src.startsWith("then", start) ? Token.Then : undefined;
410
+ case 0x65 /* e */:
411
+ if (len !== 4)
412
+ return undefined;
413
+ if (src.startsWith("else", start))
414
+ return Token.Else;
415
+ if (src.startsWith("elif", start))
416
+ return Token.Elif;
417
+ if (src.startsWith("esac", start))
418
+ return Token.Esac;
419
+ return undefined;
420
+ case 0x64 /* d */:
421
+ if (len === 2)
422
+ return src.charCodeAt(start + 1) === 0x6f /* o */ ? Token.Do : undefined;
423
+ if (len === 4)
424
+ return src.startsWith("done", start) ? Token.Done : undefined;
425
+ return undefined;
426
+ case 0x63 /* c */:
427
+ if (len === 4)
428
+ return src.startsWith("case", start) ? Token.Case : undefined;
429
+ if (len === 6)
430
+ return src.startsWith("coproc", start) ? Token.Coproc : undefined;
431
+ return undefined;
432
+ case 0x77 /* w */:
433
+ return len === 5 && src.startsWith("while", start) ? Token.While : undefined;
434
+ case 0x75 /* u */:
435
+ return len === 5 && src.startsWith("until", start) ? Token.Until : undefined;
436
+ case 0x73 /* s */:
437
+ return len === 6 && src.startsWith("select", start) ? Token.Select : undefined;
438
+ default:
439
+ return undefined;
440
+ }
441
+ }
442
+ export const LexContext = {
443
+ Normal: 0,
444
+ CommandStart: 1,
445
+ TestMode: 2,
446
+ // After a prefix element: assignments still recognized, reserved words not.
447
+ CommandPrefix: 3,
448
+ // Assignment arguments retain ordinary word boundaries inside subscripts.
449
+ Declaration: 4,
450
+ ArrayElement: 5,
451
+ };
452
+ function scanBraceExpansion(src, pos, len) {
453
+ const nextCh = pos + 1 < len ? src.charCodeAt(pos + 1) : 0;
454
+ if (nextCh <= CH_SPACE || nextCh === CH_RBRACE)
455
+ return -1;
456
+ let depth = 1;
457
+ let hasSep = false;
458
+ let scanPos = pos + 1;
459
+ while (scanPos < len && depth > 0) {
460
+ const bc = src.charCodeAt(scanPos);
461
+ if (bc === CH_LBRACE)
462
+ depth++;
463
+ else if (bc === CH_RBRACE) {
464
+ if (--depth === 0)
465
+ break;
466
+ }
467
+ else if (bc <= CH_SPACE || bc === CH_SEMI || bc === CH_PIPE || bc === CH_AMP)
468
+ return -1;
469
+ else if (depth === 1 &&
470
+ (bc === 0x2c /* , */ || (bc === 0x2e /* . */ && scanPos + 1 < len && src.charCodeAt(scanPos + 1) === 0x2e)))
471
+ hasSep = true;
472
+ if (bc === CH_BACKSLASH)
473
+ scanPos++;
474
+ scanPos++;
475
+ }
476
+ if (depth === 0 && hasSep)
477
+ return scanPos + 1;
478
+ return -1;
479
+ }
480
+ export class Lexer {
481
+ src;
482
+ srcEnd;
483
+ parenBoundary;
484
+ pos;
485
+ current;
486
+ nextState;
487
+ hasPeek;
488
+ pendingHereDocs;
489
+ collectedExpansions;
490
+ _errors = null;
491
+ _buildParts = false;
492
+ // Build processed text while scanning. Off on the normal token path (values
493
+ // materialize lazily); on for redirect targets, heredoc delimiters, arithmetic
494
+ // command bodies, and bounded value re-lexes. _buildParts implies it.
495
+ _buildValue = false;
496
+ // Nesting depth of the window being lexed (enclosing sub-fields plus substitution
497
+ // scripts), sharing the MAX_SYNTAX_NESTING budget across lazily created lexers.
498
+ _nestingDepth = 0;
499
+ // `start`/`end` bound the lexer to a window of `src` so substitution scripts can be
500
+ // parsed in place against the original source — every position is then absolute, with
501
+ // no slicing or re-basing. Defaults cover the whole string (the common top-level parse).
502
+ constructor(src, start = 0, end = src.length, parenBoundary = false) {
503
+ this.src = src;
504
+ this.srcEnd = end;
505
+ this.parenBoundary = parenBoundary;
506
+ this.pos = start;
507
+ this.current = new TokenValue(this);
508
+ this.nextState = new TokenValue(this);
509
+ this.hasPeek = false;
510
+ this.pendingHereDocs = null;
511
+ this.collectedExpansions = null;
512
+ if (start === 0 && src.charCodeAt(0) === CH_HASH && src.charCodeAt(1) === CH_BANG) {
513
+ const nl = src.indexOf("\n");
514
+ this.pos = nl === -1 ? this.srcEnd : nl + 1;
515
+ }
516
+ }
517
+ getSource() {
518
+ return this.src;
519
+ }
520
+ get errors() {
521
+ return this._errors ?? (this._errors = []);
522
+ }
523
+ getCollectedExpansions() {
524
+ return this.collectedExpansions ?? NO_EXPANSIONS;
525
+ }
526
+ // Collected expansions resolve after the enclosing scan unwinds, so each records the
527
+ // depth it was found at; resolveCollected charges that depth against the shared budget.
528
+ collect(part, inner, innerStart, parenBoundary = false) {
529
+ (this.collectedExpansions ?? (this.collectedExpansions = [])).push([
530
+ part,
531
+ this._nestingDepth,
532
+ inner,
533
+ innerStart,
534
+ parenBoundary,
535
+ ]);
536
+ }
537
+ getPos() {
538
+ return this.pos;
539
+ }
540
+ /** Materialize a word token's value: raw spans slice directly, others re-lex the span. */
541
+ _tokenValue(pos, end, raw) {
542
+ return raw ? this.src.slice(pos, end) : this.wordValueOf(pos, end);
543
+ }
544
+ // Re-lex [start, end) in value mode to produce the processed word text. Bounded
545
+ // to the span, so every expansion the original scan consumed closes within it.
546
+ // Errors were already reported by the original scan; suppress duplicates.
547
+ wordValueOf(start, end) {
548
+ const savedPos = this.pos;
549
+ const savedEnd = this.srcEnd;
550
+ const savedBuildValue = this._buildValue;
551
+ const savedUnbalanced = this._unbalanced;
552
+ const errorCount = this._errors === null ? 0 : this._errors.length;
553
+ this.pos = start;
554
+ this.srcEnd = end;
555
+ this._buildValue = true;
556
+ this.readWordText();
557
+ const value = this._wordText;
558
+ this.pos = savedPos;
559
+ this.srcEnd = savedEnd;
560
+ this._buildValue = savedBuildValue;
561
+ this._unbalanced = savedUnbalanced;
562
+ if (this._errors !== null)
563
+ this._errors.length = errorCount;
564
+ return value;
565
+ }
566
+ /** Find the closing bracket for a shell subscript, ignoring brackets inside nested shell syntax. */
567
+ findClosingBracket(start, end = this.srcEnd) {
568
+ return this.findClosingShellDelimiter(start, end, CH_RBRACKET);
569
+ }
570
+ /** Find the closing bracket of `$[ … ]`, which unlike a subscript does not recurse into `${ }`. */
571
+ findClosingArithmeticBracket(start, end = this.srcEnd) {
572
+ return this.findClosingShellDelimiter(start, end, CH_RBRACKET, false, false);
573
+ }
574
+ /** Find the closing brace for a parameter expansion, ignoring braces inside nested shell syntax. */
575
+ findClosingBrace(start, end = this.srcEnd) {
576
+ return this.findClosingShellDelimiter(start, end, CH_RBRACE);
577
+ }
578
+ /** Find the closing parenthesis for a shell substitution using the command-aware scanner. */
579
+ findClosingParenthesis(start, end = this.srcEnd) {
580
+ const savedPos = this.pos;
581
+ const savedEnd = this.srcEnd;
582
+ const savedUnbalanced = this._unbalanced;
583
+ this.pos = start;
584
+ this.srcEnd = Math.min(end, this.srcEnd);
585
+ this.extractBalanced();
586
+ const close = this._unbalanced ? -1 : this.pos - 1;
587
+ this.pos = savedPos;
588
+ this.srcEnd = savedEnd;
589
+ this._unbalanced = savedUnbalanced;
590
+ return close;
591
+ }
592
+ /** Find the end of one arithmetic expansion using the canonical lexer scanner. */
593
+ findArithmeticExpansionEnd(start, end = this.srcEnd) {
594
+ const scanner = new Lexer(this.src, start, end);
595
+ scanner.pos = start + 1;
596
+ scanner.scanArithmeticBody();
597
+ return scanner.pos;
598
+ }
599
+ /** Find the end of one shell-expanded arithmetic word using the canonical lexer scanners. */
600
+ findArithmeticWordEnd(start, end = this.srcEnd) {
601
+ const scanner = new Lexer(this.src, start, end);
602
+ scanner.pos = start;
603
+ return scanner.scanArithmeticWordEnd();
604
+ }
605
+ scanArithmeticWordEnd() {
606
+ while (this.pos < this.srcEnd) {
607
+ const ch = this.src.charCodeAt(this.pos);
608
+ if (ch === CH_DOLLAR) {
609
+ this.readDollar();
610
+ continue;
611
+ }
612
+ if (ch === CH_BACKTICK) {
613
+ this.readBacktickExpansion();
614
+ continue;
615
+ }
616
+ if (ch === CH_SQUOTE) {
617
+ this.pos++;
618
+ this.skipSQ();
619
+ continue;
620
+ }
621
+ if (ch === CH_DQUOTE) {
622
+ this.pos++;
623
+ this.skipDQ();
624
+ continue;
625
+ }
626
+ if (ch === CH_BACKSLASH) {
627
+ this.pos += 2;
628
+ continue;
629
+ }
630
+ if (ch === CH_LBRACKET) {
631
+ const close = this.findClosingBracket(this.pos + 1);
632
+ if (close !== -1) {
633
+ this.pos = close + 1;
634
+ continue;
635
+ }
636
+ }
637
+ if ((ch === CH_LT || ch === CH_GT) && this.src.charCodeAt(this.pos + 1) === CH_LPAREN) {
638
+ this.pos += 2;
639
+ this.extractBalanced();
640
+ continue;
641
+ }
642
+ if (ch < 128 && arithmeticWordDelimiter[ch])
643
+ break;
644
+ this.pos++;
645
+ }
646
+ return this.pos;
647
+ }
648
+ // Only array assignment bodies take comments; extglob shares this scanner, and `#` is
649
+ // pattern data there.
650
+ findClosingShellDelimiter(start, end, closing, comments = false, braces = true) {
651
+ const savedPos = this.pos;
652
+ const savedEnd = this.srcEnd;
653
+ const savedUnbalanced = this._unbalanced;
654
+ this.srcEnd = Math.min(end, this.srcEnd);
655
+ const delimiters = [closing];
656
+ let pos = start;
657
+ let wordStart = true;
658
+ while (pos < this.srcEnd) {
659
+ const ch = this.src.charCodeAt(pos);
660
+ if (ch === CH_BACKSLASH) {
661
+ if (pos + 1 < this.srcEnd && this.src.charCodeAt(pos + 1) !== CH_NL)
662
+ wordStart = false;
663
+ pos += 2;
664
+ continue;
665
+ }
666
+ if (ch === CH_HASH && comments && delimiters.length === 1 && wordStart) {
667
+ while (pos < this.srcEnd && this.src.charCodeAt(pos) !== CH_NL)
668
+ pos++;
669
+ continue;
670
+ }
671
+ if (ch === CH_SQUOTE) {
672
+ this.pos = pos + 1;
673
+ this.skipSQ();
674
+ pos = this.pos;
675
+ wordStart = false;
676
+ continue;
677
+ }
678
+ if (ch === CH_DQUOTE) {
679
+ this.pos = pos + 1;
680
+ this.skipDQ();
681
+ pos = this.pos;
682
+ wordStart = false;
683
+ continue;
684
+ }
685
+ if (ch === CH_BACKTICK) {
686
+ pos++;
687
+ while (pos < this.srcEnd && this.src.charCodeAt(pos) !== CH_BACKTICK) {
688
+ if (this.src.charCodeAt(pos) === CH_BACKSLASH)
689
+ pos++;
690
+ pos++;
691
+ }
692
+ if (pos < this.srcEnd)
693
+ pos++;
694
+ wordStart = false;
695
+ continue;
696
+ }
697
+ if ((ch === CH_DOLLAR && pos + 1 < this.srcEnd && this.src.charCodeAt(pos + 1) === CH_LPAREN) ||
698
+ ((ch === CH_LT || ch === CH_GT) && pos + 1 < this.srcEnd && this.src.charCodeAt(pos + 1) === CH_LPAREN)) {
699
+ this.pos = pos + 2;
700
+ this.extractBalanced();
701
+ pos = this.pos;
702
+ wordStart = false;
703
+ continue;
704
+ }
705
+ const expected = delimiters[delimiters.length - 1];
706
+ if (ch === CH_DOLLAR && pos + 1 < this.srcEnd) {
707
+ const after = this.src.charCodeAt(pos + 1);
708
+ if (after === CH_DOLLAR) {
709
+ pos += 2; // `$$` consumes its second `$`, so a `{` after it opens nothing
710
+ wordStart = false;
711
+ continue;
712
+ }
713
+ if (after === CH_LBRACE && braces) {
714
+ delimiters.push(CH_RBRACE);
715
+ pos += 2;
716
+ wordStart = false;
717
+ continue;
718
+ }
719
+ }
720
+ if (expected === CH_RBRACKET && ch === CH_LBRACKET) {
721
+ delimiters.push(CH_RBRACKET);
722
+ }
723
+ else if (expected === CH_RPAREN && ch === CH_LPAREN) {
724
+ delimiters.push(CH_RPAREN);
725
+ }
726
+ else if (ch === expected) {
727
+ delimiters.pop();
728
+ if (delimiters.length === 0) {
729
+ this.pos = savedPos;
730
+ this.srcEnd = savedEnd;
731
+ this._unbalanced = savedUnbalanced;
732
+ return pos;
733
+ }
734
+ wordStart = false;
735
+ pos++;
736
+ continue;
737
+ }
738
+ wordStart = ch < 128 && (charType[ch] & 1) !== 0;
739
+ pos++;
740
+ }
741
+ this.pos = savedPos;
742
+ this.srcEnd = savedEnd;
743
+ this._unbalanced = savedUnbalanced;
744
+ return -1;
745
+ }
746
+ skipSubshellBody() {
747
+ this.extractBalanced();
748
+ return this._unbalanced ? -1 : this.pos;
749
+ }
750
+ skipCompoundBody(closeToken) {
751
+ const frames = [
752
+ { close: closeToken, phase: closeToken === Token.Esac ? "case-pattern" : "commands" },
753
+ ];
754
+ let commandStart = true;
755
+ for (;;) {
756
+ const value = this.next(commandStart ? LexContext.CommandStart : LexContext.Normal);
757
+ const token = value.token;
758
+ if (token === Token.EOF)
759
+ return -1;
760
+ const last = frames.length - 1;
761
+ const frame = frames[last];
762
+ if (frame.phase === "function-name") {
763
+ if (token === Token.Newline)
764
+ continue;
765
+ frame.phase = "function-body";
766
+ commandStart = true;
767
+ continue;
768
+ }
769
+ else if (frame.phase === "function-body") {
770
+ if (token === Token.Newline)
771
+ continue;
772
+ frame.phase = "commands";
773
+ commandStart = true;
774
+ if (token === Token.LParen && this.peek(LexContext.Normal).token === Token.RParen) {
775
+ this.next(LexContext.Normal);
776
+ frame.phase = "function-body";
777
+ continue;
778
+ }
779
+ }
780
+ else if (frame.phase === "coproc-command") {
781
+ if (token === Token.Newline)
782
+ continue;
783
+ if (token === Token.Word) {
784
+ frame.phase = "coproc-body";
785
+ commandStart = true;
786
+ continue;
787
+ }
788
+ frame.phase = "commands";
789
+ commandStart = true;
790
+ }
791
+ else if (frame.phase === "coproc-body") {
792
+ if (token === Token.Newline)
793
+ continue;
794
+ frame.phase =
795
+ token === Token.Word && value.keywordEligible && value.value === "time" ? "time-command" : "commands";
796
+ commandStart = true;
797
+ if (frame.phase === "time-command")
798
+ continue;
799
+ }
800
+ else if (frame.phase === "time-command") {
801
+ if (token === Token.Word && value.keywordEligible && value.value === "-p") {
802
+ frame.phase = "time-command-after-p";
803
+ continue;
804
+ }
805
+ if (token === Token.Word && value.keywordEligible && value.value === "--") {
806
+ frame.phase = "commands";
807
+ continue;
808
+ }
809
+ frame.phase = "commands";
810
+ commandStart = true;
811
+ }
812
+ else if (frame.phase === "time-command-after-p") {
813
+ if (token === Token.Word && value.keywordEligible && value.value === "--") {
814
+ frame.phase = "commands";
815
+ continue;
816
+ }
817
+ frame.phase = "commands";
818
+ commandStart = true;
819
+ }
820
+ else if (frame.phase === "for-header") {
821
+ if (token === Token.ArithCmd || token === Token.Semi || token === Token.Newline) {
822
+ commandStart = true;
823
+ continue;
824
+ }
825
+ if (token === Token.Do || token === Token.LBrace) {
826
+ frame.close = token === Token.Do ? Token.Done : Token.RBrace;
827
+ frame.phase = "commands";
828
+ commandStart = true;
829
+ continue;
830
+ }
831
+ }
832
+ else if (frame.phase === "case-word") {
833
+ if (token === Token.Newline)
834
+ continue;
835
+ frame.phase = "case-in";
836
+ commandStart = false;
837
+ continue;
838
+ }
839
+ else if (frame.phase === "case-in") {
840
+ if (token === Token.Newline) {
841
+ commandStart = true;
842
+ continue;
843
+ }
844
+ frame.phase = "case-pattern";
845
+ commandStart = true;
846
+ continue;
847
+ }
848
+ else if (frame.phase === "case-pattern") {
849
+ if (token === Token.Esac && commandStart) {
850
+ frames.pop();
851
+ if (frames.length === 0)
852
+ return value.end;
853
+ commandStart = false;
854
+ continue;
855
+ }
856
+ if (token === Token.RParen) {
857
+ frame.phase = "commands";
858
+ commandStart = true;
859
+ }
860
+ else {
861
+ commandStart = token === Token.Newline;
862
+ }
863
+ continue;
864
+ }
865
+ if (token === frame.close) {
866
+ frames.pop();
867
+ if (frames.length === 0)
868
+ return value.end;
869
+ commandStart = false;
870
+ continue;
871
+ }
872
+ if (commandStart) {
873
+ switch (token) {
874
+ case Token.LParen:
875
+ frames.push({ close: Token.RParen, phase: "commands" });
876
+ break;
877
+ case Token.LBrace:
878
+ frames.push({ close: Token.RBrace, phase: "commands" });
879
+ break;
880
+ case Token.If:
881
+ frames.push({ close: Token.Fi, phase: "commands" });
882
+ break;
883
+ case Token.For:
884
+ frames.push({ close: Token.Done, phase: "for-header" });
885
+ break;
886
+ case Token.While:
887
+ case Token.Until:
888
+ case Token.Select:
889
+ frames.push({ close: Token.Done, phase: "commands" });
890
+ break;
891
+ case Token.Case:
892
+ frames.push({ close: Token.Esac, phase: "case-word" });
893
+ break;
894
+ case Token.DblLBracket:
895
+ if (!this.skipTestCommandBody())
896
+ return -1;
897
+ commandStart = false;
898
+ continue;
899
+ case Token.Assignment:
900
+ case Token.Redirect:
901
+ case Token.Bang:
902
+ case Token.Then:
903
+ case Token.Else:
904
+ case Token.Elif:
905
+ case Token.Do:
906
+ case Token.In:
907
+ break;
908
+ case Token.Semi:
909
+ case Token.Newline:
910
+ case Token.Pipe:
911
+ case Token.And:
912
+ case Token.Or:
913
+ case Token.Amp:
914
+ case Token.DoubleSemi:
915
+ case Token.SemiAmp:
916
+ case Token.DoubleSemiAmp:
917
+ break;
918
+ case Token.Function:
919
+ frame.phase = "function-name";
920
+ break;
921
+ case Token.Coproc:
922
+ frame.phase = "coproc-command";
923
+ break;
924
+ default:
925
+ if (token === Token.Word && value.keywordEligible && value.value === "time") {
926
+ frame.phase = "time-command";
927
+ commandStart = true;
928
+ }
929
+ else {
930
+ commandStart = false;
931
+ }
932
+ continue;
933
+ }
934
+ }
935
+ switch (token) {
936
+ case Token.Semi:
937
+ case Token.Newline:
938
+ case Token.Pipe:
939
+ case Token.And:
940
+ case Token.Or:
941
+ case Token.Amp:
942
+ commandStart = true;
943
+ break;
944
+ case Token.DoubleSemi:
945
+ case Token.SemiAmp:
946
+ case Token.DoubleSemiAmp:
947
+ if (frame.close === Token.Esac)
948
+ frame.phase = "case-pattern";
949
+ commandStart = true;
950
+ break;
951
+ case Token.RParen:
952
+ commandStart = true;
953
+ break;
954
+ }
955
+ }
956
+ }
957
+ skipTestGroup() {
958
+ let depth = 1;
959
+ for (;;) {
960
+ const value = this.next(LexContext.TestMode);
961
+ if (value.token === Token.EOF)
962
+ return -1;
963
+ if (value.token === Token.DblRBracket) {
964
+ this.unshift(value);
965
+ return -1;
966
+ }
967
+ if (value.token === Token.LParen)
968
+ depth++;
969
+ else if (value.token === Token.RParen && --depth === 0)
970
+ return value.end;
971
+ }
972
+ }
973
+ skipTestCommandBody() {
974
+ for (;;) {
975
+ const token = this.next(LexContext.TestMode).token;
976
+ if (token === Token.DblRBracket)
977
+ return true;
978
+ if (token === Token.EOF)
979
+ return false;
980
+ }
981
+ }
982
+ /** Set position and scan a word, building parts. Used by computeWordParts. */
983
+ buildWordParts(startPos) {
984
+ this._buildParts = true;
985
+ this.pos = startPos;
986
+ // Handle process substitution words <(...) and >(...)
987
+ const ch = this.src.charCodeAt(startPos);
988
+ if ((ch === 0x3c /* < */ || ch === 0x3e) /* > */ &&
989
+ startPos + 1 < this.srcEnd &&
990
+ this.src.charCodeAt(startPos + 1) === 0x28 /* ( */) {
991
+ this.pos = startPos + 2;
992
+ const inner = this.extractBalanced();
993
+ if (this._unbalanced)
994
+ this.errors.push({ message: "unterminated process substitution", pos: startPos });
995
+ const text = this.src.slice(startPos, this.pos);
996
+ const part = {
997
+ type: "ProcessSubstitution",
998
+ pos: startPos,
999
+ end: this.pos,
1000
+ text,
1001
+ operator: ch === 0x3c ? "<" : ">",
1002
+ script: undefined,
1003
+ };
1004
+ this.collect(part, inner, startPos + 2, !this._unbalanced);
1005
+ // Continue reading any trailing word text (e.g., suffix after proc sub)
1006
+ if (this.pos < this.srcEnd) {
1007
+ this.readWordText();
1008
+ if (this._wordParts) {
1009
+ this._wordParts.unshift(part);
1010
+ }
1011
+ else {
1012
+ this._wordParts = [part];
1013
+ }
1014
+ }
1015
+ else {
1016
+ this._wordParts = [part];
1017
+ }
1018
+ }
1019
+ else {
1020
+ this.readWordText();
1021
+ }
1022
+ return this._wordParts;
1023
+ }
1024
+ /** Scan a bounded word-like span without treating shell operators or whitespace as terminators. */
1025
+ buildEmbeddedWordParts(startPos) {
1026
+ this._buildParts = true;
1027
+ this.pos = startPos;
1028
+ this.readInnerWordText();
1029
+ return this._wordParts;
1030
+ }
1031
+ buildArrayElementParts(startPos) {
1032
+ const close = this.findClosingBracket(startPos + 1);
1033
+ if (close === -1)
1034
+ return this.buildWordParts(startPos);
1035
+ this._buildParts = true;
1036
+ const prefix = hasEmbeddedWordStructure(this.src, startPos, close + 1)
1037
+ ? this.parseSubFieldWord(startPos, close + 1)
1038
+ : undefined;
1039
+ this.pos = close + 1;
1040
+ this.readWordText();
1041
+ const parts = this._wordParts;
1042
+ const prefixParts = prefix?.parts;
1043
+ if (prefixParts) {
1044
+ if (parts)
1045
+ return prefixParts.concat(parts);
1046
+ if (this.pos > close + 1) {
1047
+ prefixParts.push({
1048
+ type: "Literal",
1049
+ pos: close + 1,
1050
+ end: this.pos,
1051
+ text: this.src.slice(close + 1, this.pos),
1052
+ value: this._wordText,
1053
+ });
1054
+ }
1055
+ return prefixParts;
1056
+ }
1057
+ if (parts) {
1058
+ const text = this.src.slice(startPos, close + 1);
1059
+ parts.unshift({ type: "Literal", pos: startPos, end: close + 1, text, value: prefix?.value ?? text });
1060
+ }
1061
+ return parts;
1062
+ }
1063
+ /** Scan a heredoc body for expansions, building parts. Spaces/newlines are literal. */
1064
+ buildHereDocParts(bodyPos, bodyEnd) {
1065
+ this._buildParts = true;
1066
+ const src = this.src;
1067
+ const parts = [];
1068
+ let hasExpansion = false;
1069
+ let litBuf = "";
1070
+ let litStart = bodyPos;
1071
+ let i = bodyPos;
1072
+ const flushLit = () => {
1073
+ if (i > litStart) {
1074
+ parts.push({ type: "Literal", pos: litStart, end: i, value: litBuf, text: src.slice(litStart, i) });
1075
+ litBuf = "";
1076
+ }
1077
+ };
1078
+ while (i < bodyEnd) {
1079
+ const ch = src.charCodeAt(i);
1080
+ if (ch === 0x5c /* \\ */) {
1081
+ // Backslash escape — in unquoted heredoc, \\$, \\`, \\\\ are special
1082
+ if (i + 1 < bodyEnd) {
1083
+ const nc = src.charCodeAt(i + 1);
1084
+ if (nc === CH_NL) {
1085
+ i += 2;
1086
+ continue;
1087
+ }
1088
+ if (nc === 0x24 /* $ */ || nc === 0x60 /* ` */ || nc === 0x5c /* \\ */) {
1089
+ litBuf += String.fromCharCode(nc);
1090
+ i += 2;
1091
+ continue;
1092
+ }
1093
+ }
1094
+ litBuf += "\\";
1095
+ i++;
1096
+ continue;
1097
+ }
1098
+ if (ch === 0x24 /* $ */) {
1099
+ flushLit();
1100
+ litStart = i;
1101
+ this.pos = i;
1102
+ this.readDollar();
1103
+ if (this._resultPart) {
1104
+ hasExpansion = true;
1105
+ parts.push(this._resultPart);
1106
+ litStart = this.pos;
1107
+ }
1108
+ else {
1109
+ litBuf += src.slice(i, this.pos);
1110
+ }
1111
+ i = this.pos;
1112
+ continue;
1113
+ }
1114
+ if (ch === 0x60 /* ` */) {
1115
+ flushLit();
1116
+ litStart = i;
1117
+ this.pos = i;
1118
+ this.readBacktickExpansion();
1119
+ if (this._resultPart) {
1120
+ hasExpansion = true;
1121
+ parts.push(this._resultPart);
1122
+ litStart = this.pos;
1123
+ }
1124
+ else {
1125
+ litBuf += src.slice(i, this.pos);
1126
+ }
1127
+ i = this.pos;
1128
+ continue;
1129
+ }
1130
+ litBuf += src[i];
1131
+ i++;
1132
+ }
1133
+ flushLit();
1134
+ return hasExpansion ? parts : null;
1135
+ }
1136
+ registerHereDocTarget(target) {
1137
+ if (this.pendingHereDocs === null)
1138
+ return;
1139
+ for (const hd of this.pendingHereDocs) {
1140
+ if (!hd.target) {
1141
+ hd.target = target;
1142
+ return;
1143
+ }
1144
+ }
1145
+ }
1146
+ // Read the right-hand operand of =~ in [[ ]]. Bash ends the operand at
1147
+ // depth-zero whitespace, `)`, `;`, `&`, `<`, or `>` (but `<(`/`>(` open a
1148
+ // process substitution and `|` never delimits). An unquoted `(` opens a
1149
+ // group that consumes everything — `]]`, newlines, and metacharacters
1150
+ // included — until its matching `)`; inside a group only quotes stay opaque
1151
+ // and expansion parens count naively, matching bash. Quotes and expansions
1152
+ // at depth zero skip via the same readers normal words use, so their errors
1153
+ // and spans stay identical. The token is the raw source span; value and
1154
+ // parts resolve lazily like every other word.
1155
+ readTestRegexWord() {
1156
+ this.hasPeek = false;
1157
+ this.skipSpacesAndTabs();
1158
+ const src = this.src;
1159
+ const len = this.srcEnd;
1160
+ const start = this.pos;
1161
+ let depth = 0;
1162
+ while (this.pos < len) {
1163
+ const ch = src.charCodeAt(this.pos);
1164
+ if (ch === CH_LPAREN) {
1165
+ depth++;
1166
+ this.pos++;
1167
+ continue;
1168
+ }
1169
+ if (ch === CH_BACKSLASH) {
1170
+ this.pos += this.pos + 1 < len ? 2 : 1;
1171
+ continue;
1172
+ }
1173
+ if (ch === CH_SQUOTE) {
1174
+ const quotePos = this.pos++;
1175
+ const ansiC = quotePos > start && src.charCodeAt(quotePos - 1) === CH_DOLLAR;
1176
+ while (this.pos < len && src.charCodeAt(this.pos) !== CH_SQUOTE) {
1177
+ if (ansiC && src.charCodeAt(this.pos) === CH_BACKSLASH && this.pos + 1 < len)
1178
+ this.pos++;
1179
+ this.pos++;
1180
+ }
1181
+ if (this.pos < len)
1182
+ this.pos++;
1183
+ else
1184
+ this.errors.push({
1185
+ message: ansiC ? "unterminated ANSI-C quote" : "unterminated single quote",
1186
+ pos: quotePos,
1187
+ });
1188
+ continue;
1189
+ }
1190
+ if (ch === CH_DQUOTE) {
1191
+ this.pos++;
1192
+ this.readDoubleQuoted();
1193
+ continue;
1194
+ }
1195
+ if (ch === CH_BACKTICK) {
1196
+ this.readBacktickExpansion();
1197
+ continue;
1198
+ }
1199
+ if (depth > 0) {
1200
+ if (ch === CH_RPAREN)
1201
+ depth--;
1202
+ this.pos++;
1203
+ continue;
1204
+ }
1205
+ if (ch === CH_DOLLAR) {
1206
+ this.readDollar();
1207
+ continue;
1208
+ }
1209
+ if ((ch === CH_LT || ch === CH_GT) && this.pos + 1 < len && src.charCodeAt(this.pos + 1) === CH_LPAREN) {
1210
+ const subPos = this.pos;
1211
+ this.pos += 2;
1212
+ this.extractBalanced();
1213
+ if (this._unbalanced)
1214
+ this.errors.push({ message: "unterminated process substitution", pos: subPos });
1215
+ continue;
1216
+ }
1217
+ if (ch < 128 && charType[ch] & 1 && ch !== CH_PIPE)
1218
+ break;
1219
+ this.pos++;
1220
+ }
1221
+ setToken(this.current, Token.Word, src.slice(start, this.pos), start, this.pos);
1222
+ return this.current;
1223
+ }
1224
+ // Read C-style for expressions: called after first '(' consumed by parser.
1225
+ // Expects pos at second '('. Returns [init, test, update] raw text.
1226
+ readCStyleForExprs() {
1227
+ this.hasPeek = false; // discard any peeked token
1228
+ const src = this.src;
1229
+ const len = this.srcEnd;
1230
+ // Skip spaces to second '('
1231
+ while (this.pos < len && (src.charCodeAt(this.pos) === CH_SPACE || src.charCodeAt(this.pos) === CH_TAB))
1232
+ this.pos++;
1233
+ const openPos = this.pos;
1234
+ if (this.pos < len && src.charCodeAt(this.pos) === CH_LPAREN)
1235
+ this.pos++;
1236
+ const starts = [this.pos, 0, 0];
1237
+ const parts = ["", "", "", 0, 0, 0];
1238
+ let partIdx = 0;
1239
+ let depth = 1;
1240
+ let partStart = this.pos;
1241
+ let extra = false;
1242
+ const setPart = (end) => {
1243
+ const raw = src.slice(partStart, end);
1244
+ parts[partIdx] = raw.trim();
1245
+ parts[3 + partIdx] = starts[partIdx] + raw.length - raw.trimStart().length;
1246
+ };
1247
+ while (this.pos < len && depth > 0) {
1248
+ const c = src.charCodeAt(this.pos);
1249
+ if (c === CH_LPAREN) {
1250
+ depth++;
1251
+ this.pos++;
1252
+ }
1253
+ else if (c === CH_RPAREN) {
1254
+ depth--;
1255
+ if (depth === 0) {
1256
+ if (!extra)
1257
+ setPart(this.pos);
1258
+ if (partIdx < 2)
1259
+ this.errors.push({ message: "expected three arithmetic expressions in for header", pos: this.pos });
1260
+ this.pos++; // skip closing )
1261
+ // Skip the outer ) as well
1262
+ while (this.pos < len && (src.charCodeAt(this.pos) === CH_SPACE || src.charCodeAt(this.pos) === CH_TAB))
1263
+ this.pos++;
1264
+ if (this.pos < len && src.charCodeAt(this.pos) === CH_RPAREN)
1265
+ this.pos++;
1266
+ break;
1267
+ }
1268
+ this.pos++;
1269
+ }
1270
+ else if (c === CH_SEMI && depth === 1) {
1271
+ // Bash requires exactly three expressions; keep the first three and report the rest.
1272
+ if (partIdx === 2) {
1273
+ if (!extra) {
1274
+ setPart(this.pos);
1275
+ this.errors.push({ message: "expected three arithmetic expressions in for header", pos: this.pos });
1276
+ }
1277
+ extra = true;
1278
+ this.pos++;
1279
+ continue;
1280
+ }
1281
+ setPart(this.pos);
1282
+ partIdx++;
1283
+ this.pos++;
1284
+ partStart = this.pos;
1285
+ starts[partIdx] = partStart;
1286
+ }
1287
+ else if (c === CH_SQUOTE) {
1288
+ this.pos++;
1289
+ this.skipSQ();
1290
+ }
1291
+ else if (c === CH_DQUOTE) {
1292
+ this.pos++;
1293
+ this.skipDQ();
1294
+ }
1295
+ else {
1296
+ this.pos++;
1297
+ }
1298
+ }
1299
+ if (depth > 0) {
1300
+ if (!extra)
1301
+ setPart(this.pos);
1302
+ this.errors.push({ message: "unterminated arithmetic for header", pos: openPos });
1303
+ }
1304
+ return parts;
1305
+ }
1306
+ peek(ctx = LexContext.Normal) {
1307
+ if (!this.hasPeek) {
1308
+ this.readNext(this.nextState, ctx);
1309
+ this.hasPeek = true;
1310
+ }
1311
+ return this.nextState;
1312
+ }
1313
+ // Peek where the context depends on the token just consumed. peek() ignores its ctx once a
1314
+ // token is cached, so the caller's context must only be derived when a read actually happens.
1315
+ peekFollow(closers) {
1316
+ if (!this.hasPeek) {
1317
+ const ctx = closers[this.current.token] ? LexContext.CommandStart : LexContext.Normal;
1318
+ this.readNext(this.nextState, ctx);
1319
+ this.hasPeek = true;
1320
+ }
1321
+ return this.nextState;
1322
+ }
1323
+ next(ctx = LexContext.Normal) {
1324
+ if (this.hasPeek) {
1325
+ this.hasPeek = false;
1326
+ const temp = this.current;
1327
+ this.current = this.nextState;
1328
+ this.nextState = temp;
1329
+ return this.current;
1330
+ }
1331
+ this.readNext(this.current, ctx);
1332
+ return this.current;
1333
+ }
1334
+ unshift(tok) {
1335
+ this.nextState.copyFrom(tok);
1336
+ this.hasPeek = true;
1337
+ }
1338
+ readNext(out, ctx) {
1339
+ const src = this.src;
1340
+ const len = this.srcEnd;
1341
+ // Skip spaces and tabs (inlined for hot path)
1342
+ let pos = this.pos;
1343
+ while (pos < len) {
1344
+ const ch = src.charCodeAt(pos);
1345
+ if (ch === CH_SPACE || ch === CH_TAB) {
1346
+ pos++;
1347
+ continue;
1348
+ }
1349
+ if (ch === CH_BACKSLASH && pos + 1 < len && src.charCodeAt(pos + 1) === CH_NL) {
1350
+ pos += 2;
1351
+ continue;
1352
+ }
1353
+ // Inside [[ ]], newlines are whitespace
1354
+ if (ch === CH_NL && ctx === LexContext.TestMode) {
1355
+ pos++;
1356
+ continue;
1357
+ }
1358
+ break;
1359
+ }
1360
+ this.pos = pos;
1361
+ if (pos >= len) {
1362
+ this.consumePendingHereDocs();
1363
+ setToken(out, Token.EOF, "", pos, pos);
1364
+ return;
1365
+ }
1366
+ const tokenStart = pos;
1367
+ const ch = src.charCodeAt(pos);
1368
+ if (ch === CH_HASH) {
1369
+ // Skip comment
1370
+ while (this.pos < len && src.charCodeAt(this.pos) !== CH_NL)
1371
+ this.pos++;
1372
+ this.readNext(out, ctx);
1373
+ return;
1374
+ }
1375
+ if (ch === CH_NL) {
1376
+ this.pos++;
1377
+ this.consumePendingHereDocs();
1378
+ setToken(out, Token.Newline, "\n", tokenStart, this.pos);
1379
+ return;
1380
+ }
1381
+ // In test mode (inside [[ ]]), < and > are string comparison operators, not redirects —
1382
+ // except for an adjacent '(', which still opens a process substitution
1383
+ if (ctx === LexContext.TestMode &&
1384
+ (ch === CH_LT || ch === CH_GT) &&
1385
+ !(this.pos + 1 < this.srcEnd && src.charCodeAt(this.pos + 1) === CH_LPAREN)) {
1386
+ this.pos++;
1387
+ setToken(out, Token.Word, ch === CH_LT ? "<" : ">", tokenStart, this.pos);
1388
+ out.keywordEligible = true; // written unquoted, so it is the comparison operator
1389
+ return;
1390
+ }
1391
+ // Operators all start with a metachar; anything else is a word
1392
+ if (ch < 128 && charType[ch] & 1 && this.tryReadOperator(out, ch, ctx, tokenStart))
1393
+ return;
1394
+ if (ch === CH_LBRACKET && ctx === LexContext.ArrayElement && this.readArrayElement(out, tokenStart))
1395
+ return;
1396
+ this.readWord(out, ctx, tokenStart);
1397
+ }
1398
+ tryReadOperator(out, ch, ctx, tokenStart) {
1399
+ const src = this.src;
1400
+ const pos = this.pos;
1401
+ const next = pos + 1 < this.srcEnd ? src.charCodeAt(pos + 1) : 0;
1402
+ switch (ch) {
1403
+ case CH_SEMI:
1404
+ if (next === CH_SEMI) {
1405
+ if (pos + 2 < this.srcEnd && src.charCodeAt(pos + 2) === CH_AMP) {
1406
+ this.pos += 3;
1407
+ setToken(out, Token.DoubleSemiAmp, ";;&", tokenStart, this.pos);
1408
+ return true;
1409
+ }
1410
+ this.pos += 2;
1411
+ setToken(out, Token.DoubleSemi, ";;", tokenStart, this.pos);
1412
+ return true;
1413
+ }
1414
+ if (next === CH_AMP) {
1415
+ this.pos += 2;
1416
+ setToken(out, Token.SemiAmp, ";&", tokenStart, this.pos);
1417
+ return true;
1418
+ }
1419
+ this.pos++;
1420
+ setToken(out, Token.Semi, ";", tokenStart, this.pos);
1421
+ return true;
1422
+ case CH_PIPE:
1423
+ if (next === CH_PIPE) {
1424
+ this.pos += 2;
1425
+ setToken(out, Token.Or, "||", tokenStart, this.pos);
1426
+ return true;
1427
+ }
1428
+ if (next === CH_AMP) {
1429
+ this.pos += 2;
1430
+ setToken(out, Token.Pipe, "|&", tokenStart, this.pos);
1431
+ return true;
1432
+ } // |& → pipe (stderr merge)
1433
+ this.pos++;
1434
+ setToken(out, Token.Pipe, "|", tokenStart, this.pos);
1435
+ return true;
1436
+ case CH_AMP:
1437
+ if (next === CH_AMP) {
1438
+ this.pos += 2;
1439
+ setToken(out, Token.And, "&&", tokenStart, this.pos);
1440
+ return true;
1441
+ }
1442
+ if (next === CH_GT) {
1443
+ // &> or &>> — redirect, not background
1444
+ this.pos += 2;
1445
+ const append = this.pos < this.srcEnd && src.charCodeAt(this.pos) === CH_GT;
1446
+ if (append)
1447
+ this.pos++;
1448
+ this.skipSpacesAndTabs();
1449
+ const targetPos = this.pos;
1450
+ if (this.pos < this.srcEnd && src.charCodeAt(this.pos) !== CH_NL && src.charCodeAt(this.pos) !== CH_HASH) {
1451
+ this.readRedirectTargetText();
1452
+ }
1453
+ this.redirectToken(out, append ? "&>>" : "&>", tokenStart, targetPos);
1454
+ return true;
1455
+ }
1456
+ this.pos++;
1457
+ setToken(out, Token.Amp, "&", tokenStart, this.pos);
1458
+ return true;
1459
+ case CH_LPAREN:
1460
+ if (ctx === LexContext.CommandStart && next === CH_LPAREN) {
1461
+ const savedErrors = this.errors.length;
1462
+ this.readArithmeticCommand(out, tokenStart);
1463
+ if (!this._notArithmetic)
1464
+ return true;
1465
+ this.errors.length = savedErrors;
1466
+ this.pos = tokenStart;
1467
+ }
1468
+ this.pos++;
1469
+ setToken(out, Token.LParen, "(", tokenStart, this.pos);
1470
+ return true;
1471
+ case CH_RPAREN:
1472
+ this.pos++;
1473
+ setToken(out, Token.RParen, ")", tokenStart, this.pos);
1474
+ return true;
1475
+ case CH_LT:
1476
+ case CH_GT:
1477
+ return this.readRedirection(out, tokenStart);
1478
+ default:
1479
+ return false;
1480
+ }
1481
+ }
1482
+ readRedirection(out, tokenStart) {
1483
+ const src = this.src;
1484
+ const ch = src.charCodeAt(this.pos);
1485
+ let op = "";
1486
+ if (ch === CH_LT) {
1487
+ this.pos++;
1488
+ const next = this.pos < this.srcEnd ? src.charCodeAt(this.pos) : 0;
1489
+ if (next === CH_LT) {
1490
+ this.pos++;
1491
+ const third = this.pos < this.srcEnd ? src.charCodeAt(this.pos) : 0;
1492
+ if (third === CH_LT) {
1493
+ // <<< herestring
1494
+ this.pos++;
1495
+ this.skipSpacesAndTabs();
1496
+ const targetPos = this.pos;
1497
+ if (this.pos < this.srcEnd && src.charCodeAt(this.pos) !== CH_NL && src.charCodeAt(this.pos) !== CH_HASH) {
1498
+ this.readRedirectTargetText();
1499
+ }
1500
+ this.redirectToken(out, "<<<", tokenStart, targetPos);
1501
+ return true;
1502
+ }
1503
+ const dash = third === CH_DASH;
1504
+ if (dash)
1505
+ this.pos++;
1506
+ this.skipSpacesAndTabs();
1507
+ const targetPos = this.pos;
1508
+ if (this.pos >= this.srcEnd || src.charCodeAt(this.pos) !== CH_HASH)
1509
+ this.readHereDocDelimiter();
1510
+ const hasTarget = this.pos > targetPos;
1511
+ if (hasTarget) {
1512
+ (this.pendingHereDocs ?? (this.pendingHereDocs = [])).push({
1513
+ delimiter: this._hereDelim,
1514
+ strip: dash,
1515
+ quoted: this._hereQuoted,
1516
+ });
1517
+ }
1518
+ setToken(out, Token.Redirect, dash ? "<<-" : "<<", tokenStart, this.pos);
1519
+ out.content = hasTarget ? this._hereDelim : undefined;
1520
+ out.delimiterQuoted = hasTarget && this._hereQuoted;
1521
+ out.targetPos = targetPos;
1522
+ out.targetEnd = hasTarget ? this.pos : targetPos;
1523
+ return true;
1524
+ }
1525
+ if (next === CH_LPAREN) {
1526
+ this.readProcessSubstitution(out, "<", tokenStart);
1527
+ return true;
1528
+ }
1529
+ if (next === CH_GT) {
1530
+ op = "<>";
1531
+ this.pos++;
1532
+ }
1533
+ else if (next === CH_AMP) {
1534
+ op = "<&";
1535
+ this.pos++;
1536
+ }
1537
+ else {
1538
+ op = "<";
1539
+ }
1540
+ }
1541
+ else if (ch === CH_GT) {
1542
+ this.pos++;
1543
+ const next = this.pos < this.srcEnd ? src.charCodeAt(this.pos) : 0;
1544
+ if (next === CH_LPAREN) {
1545
+ this.readProcessSubstitution(out, ">", tokenStart);
1546
+ return true;
1547
+ }
1548
+ if (next === CH_GT) {
1549
+ op = ">>";
1550
+ this.pos++;
1551
+ }
1552
+ else if (next === CH_AMP) {
1553
+ op = ">&";
1554
+ this.pos++;
1555
+ }
1556
+ else if (next === CH_PIPE) {
1557
+ op = ">|";
1558
+ this.pos++;
1559
+ }
1560
+ else {
1561
+ op = ">";
1562
+ }
1563
+ }
1564
+ this.skipSpacesAndTabs();
1565
+ if (this.pos < this.srcEnd) {
1566
+ const nc = src.charCodeAt(this.pos);
1567
+ if ((nc === CH_LT || nc === CH_GT) && this.pos + 1 < this.srcEnd && src.charCodeAt(this.pos + 1) === CH_LPAREN) {
1568
+ const psStart = this.pos;
1569
+ this.pos += 2;
1570
+ this.extractBalanced();
1571
+ if (this._unbalanced)
1572
+ this.errors.push({ message: "unterminated process substitution", pos: psStart });
1573
+ const psText = src.slice(psStart, this.pos);
1574
+ setToken(out, Token.Redirect, op, tokenStart, this.pos);
1575
+ out.content = psText;
1576
+ out.targetPos = psStart;
1577
+ out.targetEnd = this.pos;
1578
+ return true;
1579
+ }
1580
+ const targetPos = this.pos;
1581
+ if (nc !== CH_NL && nc !== CH_HASH)
1582
+ this.readRedirectTargetText();
1583
+ this.redirectToken(out, op, tokenStart, targetPos);
1584
+ return true;
1585
+ }
1586
+ this.redirectToken(out, op, tokenStart, this.pos);
1587
+ return true;
1588
+ }
1589
+ // Redirect targets keep their processed text eagerly (it becomes the target
1590
+ // word's content), so scan them in value mode.
1591
+ readRedirectTargetText() {
1592
+ const savedBuildValue = this._buildValue;
1593
+ this._buildValue = true;
1594
+ this.readWordText();
1595
+ this._buildValue = savedBuildValue;
1596
+ }
1597
+ redirectToken(out, operator, tokenStart, targetPos) {
1598
+ const hasTarget = this.pos > targetPos && (this._wordText.length > 0 || this._wordQuoted);
1599
+ setToken(out, Token.Redirect, operator, tokenStart, this.pos);
1600
+ out.content = hasTarget ? this._wordText : undefined;
1601
+ out.targetPos = targetPos;
1602
+ out.targetEnd = hasTarget ? this.pos : targetPos;
1603
+ }
1604
+ readProcessSubstitution(out, operator, tokenStart) {
1605
+ this.pos++; // skip (
1606
+ this.extractBalanced();
1607
+ if (this._unbalanced)
1608
+ this.errors.push({ message: "unterminated process substitution", pos: tokenStart });
1609
+ const text = this.src.slice(tokenStart, this.pos);
1610
+ setToken(out, Token.Word, text, tokenStart, this.pos);
1611
+ }
1612
+ // The delimiter is the word after quote removal: quote and escape segments may
1613
+ // appear anywhere in the word, any of them makes the heredoc quoted, and inside
1614
+ // double quotes a backslash is removed only before $ ` " \.
1615
+ readHereDocDelimiter() {
1616
+ const src = this.src;
1617
+ const len = this.srcEnd;
1618
+ const savedBuildValue = this._buildValue;
1619
+ this._buildValue = true; // the delimiter is assembled from processed segments
1620
+ let delimiter = "";
1621
+ let quoted = false;
1622
+ while (this.pos < len) {
1623
+ const c = src.charCodeAt(this.pos);
1624
+ if (c === CH_SQUOTE) {
1625
+ quoted = true;
1626
+ this.pos++;
1627
+ while (this.pos < len && src.charCodeAt(this.pos) !== CH_SQUOTE) {
1628
+ delimiter += src[this.pos];
1629
+ this.pos++;
1630
+ }
1631
+ if (this.pos < len)
1632
+ this.pos++;
1633
+ }
1634
+ else if (c === CH_DQUOTE) {
1635
+ quoted = true;
1636
+ this.pos++;
1637
+ while (this.pos < len && src.charCodeAt(this.pos) !== CH_DQUOTE) {
1638
+ if (src.charCodeAt(this.pos) === CH_BACKSLASH && this.pos + 1 < len) {
1639
+ const next = src.charCodeAt(this.pos + 1);
1640
+ if (next === CH_NL) {
1641
+ this.pos += 2;
1642
+ continue;
1643
+ }
1644
+ if (next === CH_DOLLAR || next === CH_BACKTICK || next === CH_DQUOTE || next === CH_BACKSLASH)
1645
+ this.pos++;
1646
+ }
1647
+ delimiter += src[this.pos];
1648
+ this.pos++;
1649
+ }
1650
+ if (this.pos < len)
1651
+ this.pos++;
1652
+ }
1653
+ else if (c === CH_BACKSLASH) {
1654
+ if (this.pos + 1 < len && src.charCodeAt(this.pos + 1) === CH_NL) {
1655
+ this.pos += 2;
1656
+ continue;
1657
+ }
1658
+ quoted = true;
1659
+ this.pos++;
1660
+ if (this.pos < len) {
1661
+ delimiter += src[this.pos];
1662
+ this.pos++;
1663
+ }
1664
+ else {
1665
+ delimiter += "\\"; // trailing backslash escapes nothing and stays as itself
1666
+ }
1667
+ }
1668
+ else if (c === CH_BACKTICK) {
1669
+ const btStart = this.pos;
1670
+ this.pos++;
1671
+ while (this.pos < len && src.charCodeAt(this.pos) !== CH_BACKTICK) {
1672
+ if (src.charCodeAt(this.pos) === CH_BACKSLASH)
1673
+ this.pos++;
1674
+ this.pos++;
1675
+ }
1676
+ if (this.pos < len)
1677
+ this.pos++;
1678
+ delimiter += src.slice(btStart, this.pos);
1679
+ }
1680
+ else if (c === CH_DOLLAR) {
1681
+ const next = this.pos + 1 < len ? src.charCodeAt(this.pos + 1) : 0;
1682
+ if (next === CH_SQUOTE || next === CH_DQUOTE)
1683
+ quoted = true;
1684
+ this.readDollar();
1685
+ delimiter += this._resultText;
1686
+ }
1687
+ else if (c < 128 && charType[c] & 1) {
1688
+ break;
1689
+ }
1690
+ else {
1691
+ delimiter += src[this.pos];
1692
+ this.pos++;
1693
+ }
1694
+ }
1695
+ this._buildValue = savedBuildValue;
1696
+ this._hereDelim = delimiter;
1697
+ this._hereQuoted = quoted;
1698
+ }
1699
+ consumePendingHereDocs() {
1700
+ const pending = this.pendingHereDocs;
1701
+ if (pending === null || pending.length === 0)
1702
+ return;
1703
+ for (const hd of pending) {
1704
+ const bodyPos = this.pos;
1705
+ const bodyEnd = this.skipHereDocBody(hd.delimiter, hd.strip, this.parenBoundary, hd.quoted);
1706
+ if (hd.target) {
1707
+ hd.target.body.pos = bodyPos;
1708
+ hd.target.body.end = bodyEnd;
1709
+ hd.target.body.text = this.src.slice(bodyPos, bodyEnd);
1710
+ if (this.hereDocClosingPos !== -1)
1711
+ hd.target.closing = { pos: this.hereDocClosingPos, end: this.hereDocClosingEnd };
1712
+ }
1713
+ }
1714
+ pending.length = 0;
1715
+ }
1716
+ hereDocClosingPos = -1;
1717
+ hereDocClosingEnd = -1;
1718
+ // Advance past the heredoc body and its delimiter line; return the body end
1719
+ // (start of the delimiter line, or srcEnd when delimited by end-of-input).
1720
+ // With parenEnds (inside $(...)), a line starting with the delimiter directly
1721
+ // followed by ")" also terminates the body, resuming at the ")" — bash treats
1722
+ // the substitution's closing paren as end-of-file for its heredocs.
1723
+ // `join` crosses `\`+newline pairs, which an unquoted delimiter may straddle.
1724
+ matchHereDocDelimiter(delimiter, lineStart, end, join) {
1725
+ const src = this.src;
1726
+ let pos = lineStart;
1727
+ for (let i = 0; i < delimiter.length;) {
1728
+ if (join && src.charCodeAt(pos) === CH_BACKSLASH && pos + 1 < end && src.charCodeAt(pos + 1) === CH_NL) {
1729
+ pos += 2;
1730
+ continue;
1731
+ }
1732
+ if (pos >= end || src.charCodeAt(pos) !== delimiter.charCodeAt(i))
1733
+ return -1;
1734
+ pos++;
1735
+ i++;
1736
+ }
1737
+ return pos;
1738
+ }
1739
+ // A `\` consumes the next character, so `\`+newline continues the line and `\\` does not.
1740
+ logicalLineEnd(from, end, join) {
1741
+ const src = this.src;
1742
+ let lineEnd = src.indexOf("\n", from);
1743
+ while (lineEnd !== -1 && lineEnd < end) {
1744
+ if (!join)
1745
+ return lineEnd;
1746
+ let backslashes = lineEnd;
1747
+ while (backslashes > from && src.charCodeAt(backslashes - 1) === CH_BACKSLASH)
1748
+ backslashes--;
1749
+ if ((lineEnd - backslashes) % 2 === 0)
1750
+ return lineEnd;
1751
+ from = lineEnd + 1;
1752
+ lineEnd = src.indexOf("\n", from);
1753
+ }
1754
+ return end;
1755
+ }
1756
+ skipHereDocBody(delimiter, strip, parenEnds = false, quoted = false) {
1757
+ this.hereDocClosingPos = -1;
1758
+ this.hereDocClosingEnd = -1;
1759
+ const src = this.src;
1760
+ const len = this.srcEnd;
1761
+ while (this.pos < len) {
1762
+ let lineStart = this.pos;
1763
+ const lineEnd = this.logicalLineEnd(lineStart, len, !quoted);
1764
+ for (;;) {
1765
+ const before = lineStart;
1766
+ if (strip)
1767
+ while (lineStart < lineEnd && src.charCodeAt(lineStart) === CH_TAB)
1768
+ lineStart++;
1769
+ if (!quoted)
1770
+ lineStart = skipLineContinuations(src, lineStart, lineEnd);
1771
+ if (lineStart === before)
1772
+ break;
1773
+ }
1774
+ let afterDelim = this.matchHereDocDelimiter(delimiter, lineStart, lineEnd, !quoted);
1775
+ if (afterDelim !== -1 && !quoted)
1776
+ afterDelim = skipLineContinuations(src, afterDelim, lineEnd);
1777
+ if (afterDelim === lineEnd) {
1778
+ const bodyEnd = this.pos;
1779
+ if (!parenEnds || !this.parenBoundary || lineEnd !== len) {
1780
+ this.hereDocClosingPos = lineStart;
1781
+ this.hereDocClosingEnd = lineEnd;
1782
+ }
1783
+ this.pos = lineEnd < len ? lineEnd + 1 : lineEnd;
1784
+ return bodyEnd;
1785
+ }
1786
+ // A line starting with the delimiter also ends the body when a `)` follows on the same
1787
+ // logical line; the scan then resumes mid-line, right after the delimiter.
1788
+ if (parenEnds && afterDelim !== -1) {
1789
+ let paren = afterDelim;
1790
+ while (paren < lineEnd && src.charCodeAt(paren) !== CH_RPAREN)
1791
+ paren++;
1792
+ if (paren < lineEnd || (this.parenBoundary && lineEnd === len)) {
1793
+ const bodyEnd = this.pos;
1794
+ this.pos = afterDelim;
1795
+ return bodyEnd;
1796
+ }
1797
+ }
1798
+ this.pos = lineEnd < len ? lineEnd + 1 : lineEnd;
1799
+ }
1800
+ return this.pos;
1801
+ }
1802
+ _wordText = "";
1803
+ _wordRaw = false;
1804
+ _wordQuoted = false;
1805
+ _wordHasExpansions = false;
1806
+ _wordKeywordEligible = false;
1807
+ _wordIsAssignment;
1808
+ _wordAssignmentOperatorPos;
1809
+ _wordParts = null;
1810
+ _resultText = "";
1811
+ // True when the last $-construct's value is exactly its raw source span.
1812
+ _resultIsRaw = true;
1813
+ _resultHasExpansion = false;
1814
+ _resultPart;
1815
+ // Set by extractBalanced when the input ended before the closing paren, so
1816
+ // callers can report the construct they were scanning.
1817
+ _unbalanced = false;
1818
+ // Set by scanArithmeticBody when the construct turns out not to be arithmetic, so the
1819
+ // caller can re-read it as a subshell or command substitution instead.
1820
+ _notArithmetic = false;
1821
+ _arithUnterminated = false;
1822
+ _dqText = "";
1823
+ _dqHasExpansions = false;
1824
+ _dqParts = null;
1825
+ // Content end (before the closing quote, or end of input when unterminated).
1826
+ _dqEnd = 0;
1827
+ _hereDelim = "";
1828
+ _hereQuoted = false;
1829
+ readWord(out, ctx, tokenStart = 0) {
1830
+ this.readWordText();
1831
+ if (ctx === LexContext.Normal) {
1832
+ const next = this.pos < this.srcEnd ? this.src.charCodeAt(this.pos) : 0;
1833
+ if (!this._wordRaw || (next !== CH_LT && next !== CH_GT)) {
1834
+ setSpanToken(out, Token.Word, tokenStart, this.pos, this._wordRaw);
1835
+ out.keywordEligible = this._wordKeywordEligible;
1836
+ return;
1837
+ }
1838
+ }
1839
+ this.classifyWord(out, ctx, tokenStart);
1840
+ }
1841
+ isRedirectVariable(start, end) {
1842
+ const src = this.src;
1843
+ for (let i = start; i < end; i++) {
1844
+ const c = src.charCodeAt(i);
1845
+ if (c === CH_LBRACKET && i > start) {
1846
+ return (i + 2 < end && src.charCodeAt(end - 1) === CH_RBRACKET && this.findClosingBracket(i + 1, end) === end - 1);
1847
+ }
1848
+ if (c >= 128 || !(isIdChar[c] & (i === start ? 1 : 2)))
1849
+ return false;
1850
+ }
1851
+ return end > start;
1852
+ }
1853
+ readArrayElement(out, tokenStart) {
1854
+ const close = this.findClosingBracket(tokenStart + 1);
1855
+ if (close === -1)
1856
+ return false;
1857
+ this.pos = close + 1;
1858
+ this.readWordText();
1859
+ setSpanToken(out, Token.Word, tokenStart, this.pos, true);
1860
+ return true;
1861
+ }
1862
+ classifyWord(out, ctx, tokenStart) {
1863
+ const src = this.src;
1864
+ const raw = this._wordRaw;
1865
+ const hasExpansions = this._wordHasExpansions;
1866
+ const quoted = this._wordQuoted;
1867
+ const keywordEligible = this._wordKeywordEligible;
1868
+ const isAssignment = this._wordIsAssignment;
1869
+ let assignmentOpPos = this._wordAssignmentOperatorPos;
1870
+ const wordEnd = this.pos;
1871
+ const wordLen = wordEnd - tokenStart;
1872
+ // Only keyword classification needs a non-raw word's processed value.
1873
+ let value = null;
1874
+ if (!raw && !hasExpansions && keywordEligible && (ctx === LexContext.CommandStart || ctx === LexContext.TestMode)) {
1875
+ const nextCh = wordEnd < this.srcEnd ? src.charCodeAt(wordEnd) : 0;
1876
+ if ((!quoted && wordLen <= 16) || nextCh === CH_LT || nextCh === CH_GT) {
1877
+ value = this.wordValueOf(tokenStart, wordEnd);
1878
+ }
1879
+ }
1880
+ if (ctx === LexContext.CommandStart && keywordEligible) {
1881
+ if (raw) {
1882
+ if (wordLen <= 8) {
1883
+ const reserved = matchReservedWord(src, tokenStart, wordLen);
1884
+ if (reserved !== undefined) {
1885
+ setSpanToken(out, reserved, tokenStart, wordEnd, true);
1886
+ return;
1887
+ }
1888
+ }
1889
+ if (wordLen === 2 &&
1890
+ src.charCodeAt(tokenStart) === CH_LBRACKET &&
1891
+ src.charCodeAt(tokenStart + 1) === CH_LBRACKET) {
1892
+ setSpanToken(out, Token.DblLBracket, tokenStart, wordEnd, true);
1893
+ return;
1894
+ }
1895
+ }
1896
+ else if (value !== null && value.length > 0) {
1897
+ const fc = value.charCodeAt(0);
1898
+ if ((fc >= CH_a && fc <= CH_z && value.length <= 8) || fc === CH_BANG || fc === CH_LBRACE || fc === CH_RBRACE) {
1899
+ const reserved = RESERVED_WORDS.get(value);
1900
+ if (reserved !== undefined) {
1901
+ setToken(out, reserved, value, tokenStart, wordEnd);
1902
+ return;
1903
+ }
1904
+ }
1905
+ if (fc === CH_LBRACKET && value === "[[") {
1906
+ setToken(out, Token.DblLBracket, value, tokenStart, wordEnd);
1907
+ return;
1908
+ }
1909
+ }
1910
+ }
1911
+ if (ctx === LexContext.CommandStart || ctx === LexContext.CommandPrefix || ctx === LexContext.Declaration) {
1912
+ if (isAssignment === undefined) {
1913
+ // Fast-path word (value === raw span): detect assignment positionally.
1914
+ let eq = -1;
1915
+ let bracket = false;
1916
+ for (let i = tokenStart + 1; i < wordEnd; i++) {
1917
+ const c = src.charCodeAt(i);
1918
+ if (c === CH_EQ) {
1919
+ eq = i;
1920
+ break;
1921
+ }
1922
+ if (c === CH_LBRACKET)
1923
+ bracket = true;
1924
+ }
1925
+ if (eq !== -1) {
1926
+ const state = scanAssignmentPrefix(src, tokenStart, wordEnd, ASSIGNMENT_NAME_START);
1927
+ if (isMatchedAssignment(state))
1928
+ assignmentOpPos = assignmentOperatorPos(state);
1929
+ }
1930
+ else if (ctx !== LexContext.Declaration &&
1931
+ bracket &&
1932
+ wordEnd < this.srcEnd &&
1933
+ scanAssignmentPrefix(src, tokenStart, wordEnd, ASSIGNMENT_NAME_START) >= ASSIGNMENT_INDEX_BASE) {
1934
+ // The second pass leaves the fast path, setting `_wordIsAssignment`, so this
1935
+ // cannot recur.
1936
+ this.pos = tokenStart;
1937
+ this.readWordText(true);
1938
+ this.classifyWord(out, ctx, tokenStart);
1939
+ return;
1940
+ }
1941
+ }
1942
+ if (assignmentOpPos !== undefined) {
1943
+ setSpanToken(out, Token.Assignment, tokenStart, wordEnd, raw);
1944
+ if (value !== null)
1945
+ out._value = value;
1946
+ out.assignmentOperatorPos = assignmentOpPos;
1947
+ return;
1948
+ }
1949
+ }
1950
+ // `]]` is reserved only where it can close a `[[`; operands and wordlist entries, which
1951
+ // read Normal, are ordinary words, as is a `]]` demoted by a command prefix.
1952
+ if ((ctx === LexContext.CommandStart || ctx === LexContext.TestMode) && keywordEligible) {
1953
+ if (raw) {
1954
+ if (wordLen === 2 &&
1955
+ src.charCodeAt(tokenStart) === CH_RBRACKET &&
1956
+ src.charCodeAt(tokenStart + 1) === CH_RBRACKET) {
1957
+ setSpanToken(out, Token.DblRBracket, tokenStart, wordEnd, true);
1958
+ return;
1959
+ }
1960
+ }
1961
+ else if (value === "]]") {
1962
+ setToken(out, Token.DblRBracket, value, tokenStart, wordEnd);
1963
+ return;
1964
+ }
1965
+ }
1966
+ // FD number prefix: all-digit word followed by < or > → redirect with fd
1967
+ if (!hasExpansions && this.pos < this.srcEnd) {
1968
+ const nc = src.charCodeAt(this.pos);
1969
+ if (nc === CH_LT || nc === CH_GT) {
1970
+ if (raw) {
1971
+ const fc = src.charCodeAt(tokenStart);
1972
+ if (fc >= CH_0 && fc <= CH_9 && isAllDigitsRange(src, tokenStart, wordEnd)) {
1973
+ const fd = Number.parseInt(src.slice(tokenStart, wordEnd), 10);
1974
+ if (this.readRedirection(out, tokenStart)) {
1975
+ out.fileDescriptor = fd;
1976
+ out.descriptorEnd = wordEnd;
1977
+ return;
1978
+ }
1979
+ }
1980
+ if (fc === CH_LBRACE &&
1981
+ src.charCodeAt(wordEnd - 1) === CH_RBRACE &&
1982
+ this.isRedirectVariable(tokenStart + 1, wordEnd - 1)) {
1983
+ const varname = src.slice(tokenStart + 1, wordEnd - 1);
1984
+ if (this.readRedirection(out, tokenStart)) {
1985
+ out.variableName = varname;
1986
+ out.descriptorEnd = wordEnd;
1987
+ return;
1988
+ }
1989
+ }
1990
+ }
1991
+ }
1992
+ }
1993
+ setSpanToken(out, Token.Word, tokenStart, wordEnd, raw);
1994
+ if (value !== null)
1995
+ out._value = value;
1996
+ out.keywordEligible = keywordEligible;
1997
+ }
1998
+ // `subscripts` re-reads a word known to stop inside an array subscript, where
1999
+ // metacharacters are ordinary text.
2000
+ readWordText(subscripts = false) {
2001
+ const src = this.src;
2002
+ const len = this.srcEnd;
2003
+ let pos = this.pos;
2004
+ // Fast path: scan a single run of plain chars (covers most words)
2005
+ const fastStart = pos;
2006
+ let exitCh = 0;
2007
+ while (pos < len) {
2008
+ const c = src.charCodeAt(pos);
2009
+ if (c < 128 && charType[c]) {
2010
+ exitCh = c;
2011
+ break;
2012
+ }
2013
+ pos++;
2014
+ }
2015
+ if (pos >= len ||
2016
+ (charType[exitCh] & 1 &&
2017
+ !(exitCh === CH_LPAREN && pos > fastStart && extglobPrefix[src.charCodeAt(pos - 1)]) &&
2018
+ !subscripts)) {
2019
+ this.pos = pos;
2020
+ this._wordText = (this._buildParts || this._buildValue) && pos > fastStart ? src.slice(fastStart, pos) : "";
2021
+ this._wordRaw = true;
2022
+ this._wordQuoted = false;
2023
+ this._wordHasExpansions = false;
2024
+ this._wordKeywordEligible = true;
2025
+ this._wordIsAssignment = undefined;
2026
+ this._wordAssignmentOperatorPos = undefined;
2027
+ if (this._buildParts)
2028
+ this._wordParts = null;
2029
+ return;
2030
+ }
2031
+ // Slow path: word contains quotes, expansions, escapes, etc. `text` (the
2032
+ // processed value) is only assembled when building; the token path tracks
2033
+ // positions and flags plus the last value char (for extglob prefixes) and
2034
+ // whether the value still equals the raw span.
2035
+ const bp = this._buildParts;
2036
+ const bt = bp || this._buildValue;
2037
+ let text = bt && pos > fastStart ? src.slice(fastStart, pos) : "";
2038
+ let quoted = false;
2039
+ let hasExpansions = false;
2040
+ let keywordEligible = true;
2041
+ let valueIsRaw = true;
2042
+ let lastValueChar = pos > fastStart ? src.charCodeAt(pos - 1) : 0;
2043
+ let assignmentState = scanAssignmentPrefix(src, fastStart, pos, ASSIGNMENT_NAME_START);
2044
+ let parts;
2045
+ let litBuf = "";
2046
+ let litStart = 0;
2047
+ if (bp) {
2048
+ parts = [];
2049
+ litBuf = text; // fast-path prefix is literal
2050
+ litStart = fastStart;
2051
+ }
2052
+ while (pos < len) {
2053
+ const ch = src.charCodeAt(pos);
2054
+ if (ch >= 128 || !charType[ch]) {
2055
+ const runStart = pos;
2056
+ pos++;
2057
+ while (pos < len) {
2058
+ const c = src.charCodeAt(pos);
2059
+ if (c < 128 && charType[c])
2060
+ break;
2061
+ pos++;
2062
+ }
2063
+ lastValueChar = src.charCodeAt(pos - 1);
2064
+ assignmentState = scanAssignmentPrefix(src, runStart, pos, assignmentState);
2065
+ if (bt) {
2066
+ const chunk = src.slice(runStart, pos);
2067
+ text += chunk;
2068
+ if (bp)
2069
+ litBuf += chunk;
2070
+ }
2071
+ continue;
2072
+ }
2073
+ if (charType[ch] & 1) {
2074
+ if (ch === CH_LPAREN && lastValueChar < 128 && extglobPrefix[lastValueChar]) {
2075
+ keywordEligible = false;
2076
+ const prefixChar = lastValueChar;
2077
+ pos++;
2078
+ const innerStart = pos;
2079
+ const close = this.findClosingShellDelimiter(innerStart, len, CH_RPAREN, prefixChar === CH_EQ);
2080
+ const patternEnd = close === -1 ? len : close;
2081
+ pos = close === -1 ? len : close + 1;
2082
+ if (close === -1)
2083
+ this.errors.push({ message: "unterminated extended glob", pos: innerStart - 2 });
2084
+ lastValueChar = src.charCodeAt(pos - 1);
2085
+ if (bt) {
2086
+ const eg = "(" + src.slice(innerStart, pos);
2087
+ text += eg;
2088
+ // Create ExtendedGlob part for real extglob operators (not = which is array assignment)
2089
+ if (bp && prefixChar !== CH_EQ) {
2090
+ let prefixPos = innerStart - 2;
2091
+ while (src.charCodeAt(prefixPos) === CH_NL && src.charCodeAt(prefixPos - 1) === CH_BACKSLASH) {
2092
+ prefixPos -= 2;
2093
+ }
2094
+ // Remove the prefix char from litBuf (it was appended in the previous iteration)
2095
+ if (litBuf.length > 0) {
2096
+ const trimmed = litBuf.slice(0, -1);
2097
+ if (prefixPos > litStart)
2098
+ parts.push({
2099
+ type: "Literal",
2100
+ pos: litStart,
2101
+ end: prefixPos,
2102
+ value: trimmed,
2103
+ text: src.slice(litStart, prefixPos),
2104
+ });
2105
+ litBuf = "";
2106
+ }
2107
+ const op = extglobOp[prefixChar];
2108
+ parts.push({
2109
+ type: "ExtendedGlob",
2110
+ pos: prefixPos,
2111
+ end: pos,
2112
+ text: src.slice(prefixPos, pos),
2113
+ operator: op,
2114
+ pattern: src.slice(innerStart, patternEnd),
2115
+ parts: hasEmbeddedWordStructure(src, innerStart, patternEnd)
2116
+ ? this.parseSubFieldWord(innerStart, patternEnd).parts
2117
+ : undefined,
2118
+ });
2119
+ litStart = pos;
2120
+ }
2121
+ else if (bp) {
2122
+ litBuf += eg;
2123
+ }
2124
+ }
2125
+ continue;
2126
+ }
2127
+ if (subscripts && assignmentState >= ASSIGNMENT_INDEX_BASE) {
2128
+ const close = this.findClosingBracket(pos);
2129
+ if (close !== -1) {
2130
+ const spanEnd = close + 1;
2131
+ assignmentState = scanAssignmentPrefix(src, pos, spanEnd, assignmentState);
2132
+ lastValueChar = src.charCodeAt(close);
2133
+ if (bt) {
2134
+ const chunk = src.slice(pos, spanEnd);
2135
+ text += chunk;
2136
+ if (bp)
2137
+ litBuf += chunk;
2138
+ }
2139
+ pos = spanEnd;
2140
+ continue;
2141
+ }
2142
+ }
2143
+ break;
2144
+ }
2145
+ if (ch === CH_BACKSLASH) {
2146
+ pos++;
2147
+ if (pos < len) {
2148
+ if (src.charCodeAt(pos) === CH_NL) {
2149
+ pos++;
2150
+ valueIsRaw = false;
2151
+ }
2152
+ else {
2153
+ if (assignmentState >= 0 && assignmentState < ASSIGNMENT_INDEX_BASE)
2154
+ assignmentState = ASSIGNMENT_INVALID;
2155
+ quoted = true;
2156
+ keywordEligible = false;
2157
+ valueIsRaw = false;
2158
+ lastValueChar = src.charCodeAt(pos);
2159
+ if (bt) {
2160
+ text += src[pos];
2161
+ if (bp)
2162
+ litBuf += src[pos];
2163
+ }
2164
+ pos++;
2165
+ }
2166
+ }
2167
+ else {
2168
+ // A backslash at end of input escapes nothing and stays as itself.
2169
+ if (assignmentState >= 0 && assignmentState < ASSIGNMENT_INDEX_BASE)
2170
+ assignmentState = ASSIGNMENT_INVALID;
2171
+ quoted = true;
2172
+ keywordEligible = false;
2173
+ lastValueChar = CH_BACKSLASH;
2174
+ if (bt) {
2175
+ text += "\\";
2176
+ if (bp)
2177
+ litBuf += "\\";
2178
+ }
2179
+ }
2180
+ continue;
2181
+ }
2182
+ if (ch === CH_SQUOTE) {
2183
+ const sqStart = pos;
2184
+ if (assignmentState >= 0 && assignmentState < ASSIGNMENT_INDEX_BASE)
2185
+ assignmentState = ASSIGNMENT_INVALID;
2186
+ quoted = true;
2187
+ keywordEligible = false;
2188
+ valueIsRaw = false;
2189
+ pos++;
2190
+ const start = pos;
2191
+ while (pos < len && src.charCodeAt(pos) !== CH_SQUOTE)
2192
+ pos++;
2193
+ if (pos > start)
2194
+ lastValueChar = src.charCodeAt(pos - 1);
2195
+ const value = bt ? src.slice(start, pos) : "";
2196
+ if (bt)
2197
+ text += value;
2198
+ if (pos < len)
2199
+ pos++;
2200
+ else
2201
+ this.errors.push({ message: "unterminated single quote", pos: start - 1 });
2202
+ if (bp) {
2203
+ if (sqStart > litStart) {
2204
+ parts.push({
2205
+ type: "Literal",
2206
+ pos: litStart,
2207
+ end: sqStart,
2208
+ value: litBuf,
2209
+ text: src.slice(litStart, sqStart),
2210
+ });
2211
+ litBuf = "";
2212
+ }
2213
+ parts.push({ type: "SingleQuoted", pos: sqStart, end: pos, value, text: src.slice(sqStart, pos) });
2214
+ litStart = pos;
2215
+ }
2216
+ continue;
2217
+ }
2218
+ if (ch === CH_DQUOTE) {
2219
+ const dqStart = pos;
2220
+ if (assignmentState >= 0 && assignmentState < ASSIGNMENT_INDEX_BASE)
2221
+ assignmentState = ASSIGNMENT_INVALID;
2222
+ quoted = true;
2223
+ keywordEligible = false;
2224
+ valueIsRaw = false;
2225
+ pos++;
2226
+ this.pos = pos;
2227
+ this.readDoubleQuoted();
2228
+ pos = this.pos;
2229
+ if (this._dqEnd > dqStart + 1)
2230
+ lastValueChar = src.charCodeAt(this._dqEnd - 1);
2231
+ if (this._dqHasExpansions)
2232
+ hasExpansions = true;
2233
+ if (bt)
2234
+ text += this._dqText;
2235
+ if (bp) {
2236
+ if (dqStart > litStart) {
2237
+ parts.push({
2238
+ type: "Literal",
2239
+ pos: litStart,
2240
+ end: dqStart,
2241
+ value: litBuf,
2242
+ text: src.slice(litStart, dqStart),
2243
+ });
2244
+ litBuf = "";
2245
+ }
2246
+ const dqText = src.slice(dqStart, pos);
2247
+ parts.push({
2248
+ type: "DoubleQuoted",
2249
+ pos: dqStart,
2250
+ end: pos,
2251
+ text: dqText,
2252
+ parts: this._dqParts ?? [
2253
+ {
2254
+ type: "Literal",
2255
+ pos: dqStart + 1,
2256
+ end: this._dqEnd,
2257
+ value: this._dqText,
2258
+ text: src.slice(dqStart + 1, this._dqEnd),
2259
+ },
2260
+ ],
2261
+ });
2262
+ litStart = pos;
2263
+ }
2264
+ continue;
2265
+ }
2266
+ if (ch === CH_DOLLAR) {
2267
+ keywordEligible = false;
2268
+ const dollarStart = pos;
2269
+ if (assignmentState >= 0 && assignmentState < ASSIGNMENT_INDEX_BASE)
2270
+ assignmentState = ASSIGNMENT_INVALID;
2271
+ this.pos = pos;
2272
+ this.readDollar();
2273
+ pos = this.pos;
2274
+ if (!this._resultIsRaw)
2275
+ valueIsRaw = false;
2276
+ if (pos > dollarStart)
2277
+ lastValueChar = src.charCodeAt(pos - 1);
2278
+ if (this._resultHasExpansion)
2279
+ hasExpansions = true;
2280
+ if (bt)
2281
+ text += this._resultText;
2282
+ if (bp) {
2283
+ if (this._resultPart) {
2284
+ if (dollarStart > litStart) {
2285
+ parts.push({
2286
+ type: "Literal",
2287
+ pos: litStart,
2288
+ end: dollarStart,
2289
+ value: litBuf,
2290
+ text: src.slice(litStart, dollarStart),
2291
+ });
2292
+ litBuf = "";
2293
+ }
2294
+ parts.push(this._resultPart);
2295
+ litStart = pos;
2296
+ }
2297
+ else {
2298
+ litBuf += this._resultText;
2299
+ }
2300
+ }
2301
+ continue;
2302
+ }
2303
+ if (ch === CH_BACKTICK) {
2304
+ keywordEligible = false;
2305
+ const btStart = pos;
2306
+ if (assignmentState >= 0 && assignmentState < ASSIGNMENT_INDEX_BASE)
2307
+ assignmentState = ASSIGNMENT_INVALID;
2308
+ this.pos = pos;
2309
+ this.readBacktickExpansion();
2310
+ pos = this.pos;
2311
+ valueIsRaw = false;
2312
+ lastValueChar = src.charCodeAt(pos - 1);
2313
+ hasExpansions = true;
2314
+ if (bt)
2315
+ text += this._resultText;
2316
+ if (bp) {
2317
+ if (btStart > litStart) {
2318
+ parts.push({
2319
+ type: "Literal",
2320
+ pos: litStart,
2321
+ end: btStart,
2322
+ value: litBuf,
2323
+ text: src.slice(litStart, btStart),
2324
+ });
2325
+ litBuf = "";
2326
+ }
2327
+ parts.push(this._resultPart);
2328
+ litStart = pos;
2329
+ }
2330
+ continue;
2331
+ }
2332
+ if (ch === CH_LBRACE) {
2333
+ if (assignmentState >= 0 && assignmentState < ASSIGNMENT_INDEX_BASE)
2334
+ assignmentState = ASSIGNMENT_INVALID;
2335
+ const braceEnd = scanBraceExpansion(src, pos, len);
2336
+ if (braceEnd > 0) {
2337
+ keywordEligible = false;
2338
+ lastValueChar = src.charCodeAt(braceEnd - 1);
2339
+ if (bt) {
2340
+ const braceText = src.slice(pos, braceEnd);
2341
+ text += braceText;
2342
+ if (bp) {
2343
+ if (pos > litStart) {
2344
+ parts.push({
2345
+ type: "Literal",
2346
+ pos: litStart,
2347
+ end: pos,
2348
+ value: litBuf,
2349
+ text: src.slice(litStart, pos),
2350
+ });
2351
+ litBuf = "";
2352
+ }
2353
+ parts.push({
2354
+ type: "BraceExpansion",
2355
+ pos,
2356
+ end: braceEnd,
2357
+ text: braceText,
2358
+ parts: hasEmbeddedWordStructure(src, pos + 1, braceEnd - 1)
2359
+ ? this.parseSubFieldWord(pos + 1, braceEnd - 1).parts
2360
+ : undefined,
2361
+ });
2362
+ litStart = braceEnd;
2363
+ }
2364
+ }
2365
+ pos = braceEnd;
2366
+ continue;
2367
+ }
2368
+ lastValueChar = CH_LBRACE;
2369
+ if (bt) {
2370
+ text += "{";
2371
+ if (bp)
2372
+ litBuf += "{";
2373
+ }
2374
+ pos++;
2375
+ continue;
2376
+ }
2377
+ pos++;
2378
+ }
2379
+ if (bp && pos > litStart)
2380
+ parts.push({ type: "Literal", pos: litStart, end: pos, value: litBuf, text: src.slice(litStart, pos) });
2381
+ this.pos = pos;
2382
+ this._wordText = text;
2383
+ this._wordRaw = valueIsRaw;
2384
+ this._wordQuoted = quoted;
2385
+ this._wordHasExpansions = hasExpansions;
2386
+ this._wordKeywordEligible = keywordEligible;
2387
+ this._wordIsAssignment = isMatchedAssignment(assignmentState);
2388
+ this._wordAssignmentOperatorPos = this._wordIsAssignment ? assignmentOperatorPos(assignmentState) : undefined;
2389
+ if (bp) {
2390
+ // Only store parts if they add structure beyond a single literal
2391
+ this._wordParts = parts.length > 1 || (parts.length === 1 && parts[0].type !== "Literal") ? parts : null;
2392
+ }
2393
+ }
2394
+ readInnerWordText() {
2395
+ const src = this.src;
2396
+ const len = this.srcEnd;
2397
+ let pos = this.pos;
2398
+ let text = "";
2399
+ const bp = this._buildParts;
2400
+ let parts;
2401
+ let litBuf = "";
2402
+ let litStart = 0;
2403
+ if (bp) {
2404
+ parts = [];
2405
+ litStart = pos;
2406
+ }
2407
+ while (pos < len) {
2408
+ const ch = src.charCodeAt(pos);
2409
+ if (ch === CH_BACKSLASH) {
2410
+ pos++;
2411
+ if (pos < len) {
2412
+ if (src.charCodeAt(pos) === CH_NL) {
2413
+ pos++;
2414
+ }
2415
+ else {
2416
+ const escaped = src[pos++];
2417
+ text += escaped;
2418
+ if (bp)
2419
+ litBuf += escaped;
2420
+ }
2421
+ }
2422
+ continue;
2423
+ }
2424
+ if (ch === CH_SQUOTE) {
2425
+ const sqStart = pos;
2426
+ pos++;
2427
+ const start = pos;
2428
+ while (pos < len && src.charCodeAt(pos) !== CH_SQUOTE)
2429
+ pos++;
2430
+ const value = src.slice(start, pos);
2431
+ text += value;
2432
+ if (pos < len)
2433
+ pos++;
2434
+ if (bp) {
2435
+ if (sqStart > litStart) {
2436
+ parts.push({
2437
+ type: "Literal",
2438
+ pos: litStart,
2439
+ end: sqStart,
2440
+ value: litBuf,
2441
+ text: src.slice(litStart, sqStart),
2442
+ });
2443
+ litBuf = "";
2444
+ }
2445
+ parts.push({ type: "SingleQuoted", pos: sqStart, end: pos, value, text: src.slice(sqStart, pos) });
2446
+ litStart = pos;
2447
+ }
2448
+ continue;
2449
+ }
2450
+ if (ch === CH_DQUOTE) {
2451
+ const dqStart = pos;
2452
+ pos++;
2453
+ this.pos = pos;
2454
+ this.readDoubleQuoted();
2455
+ pos = this.pos;
2456
+ text += this._dqText;
2457
+ if (bp) {
2458
+ if (dqStart > litStart) {
2459
+ parts.push({
2460
+ type: "Literal",
2461
+ pos: litStart,
2462
+ end: dqStart,
2463
+ value: litBuf,
2464
+ text: src.slice(litStart, dqStart),
2465
+ });
2466
+ litBuf = "";
2467
+ }
2468
+ const dqText = src.slice(dqStart, pos);
2469
+ parts.push({
2470
+ type: "DoubleQuoted",
2471
+ pos: dqStart,
2472
+ end: pos,
2473
+ text: dqText,
2474
+ parts: this._dqParts ?? [
2475
+ {
2476
+ type: "Literal",
2477
+ pos: dqStart + 1,
2478
+ end: this._dqEnd,
2479
+ value: this._dqText,
2480
+ text: src.slice(dqStart + 1, this._dqEnd),
2481
+ },
2482
+ ],
2483
+ });
2484
+ litStart = pos;
2485
+ }
2486
+ continue;
2487
+ }
2488
+ if (ch === CH_DOLLAR) {
2489
+ const dollarStart = pos;
2490
+ this.pos = pos;
2491
+ this.readDollar();
2492
+ pos = this.pos;
2493
+ text += this._resultText;
2494
+ if (bp) {
2495
+ if (this._resultPart) {
2496
+ if (dollarStart > litStart) {
2497
+ parts.push({
2498
+ type: "Literal",
2499
+ pos: litStart,
2500
+ end: dollarStart,
2501
+ value: litBuf,
2502
+ text: src.slice(litStart, dollarStart),
2503
+ });
2504
+ litBuf = "";
2505
+ }
2506
+ parts.push(this._resultPart);
2507
+ litStart = pos;
2508
+ }
2509
+ else {
2510
+ litBuf += this._resultText;
2511
+ }
2512
+ }
2513
+ continue;
2514
+ }
2515
+ if (ch === CH_BACKTICK) {
2516
+ const btStart = pos;
2517
+ this.pos = pos;
2518
+ this.readBacktickExpansion();
2519
+ pos = this.pos;
2520
+ text += this._resultText;
2521
+ if (bp) {
2522
+ if (btStart > litStart) {
2523
+ parts.push({
2524
+ type: "Literal",
2525
+ pos: litStart,
2526
+ end: btStart,
2527
+ value: litBuf,
2528
+ text: src.slice(litStart, btStart),
2529
+ });
2530
+ litBuf = "";
2531
+ }
2532
+ parts.push(this._resultPart);
2533
+ litStart = pos;
2534
+ }
2535
+ continue;
2536
+ }
2537
+ if ((ch === CH_LT || ch === CH_GT) && pos + 1 < len && src.charCodeAt(pos + 1) === CH_LPAREN) {
2538
+ const psStart = pos;
2539
+ this.pos = pos + 2;
2540
+ const inner = this.extractBalanced();
2541
+ pos = this.pos;
2542
+ const raw = src.slice(psStart, pos);
2543
+ text += raw;
2544
+ if (bp) {
2545
+ if (psStart > litStart) {
2546
+ parts.push({
2547
+ type: "Literal",
2548
+ pos: litStart,
2549
+ end: psStart,
2550
+ value: litBuf,
2551
+ text: src.slice(litStart, psStart),
2552
+ });
2553
+ litBuf = "";
2554
+ }
2555
+ const part = {
2556
+ type: "ProcessSubstitution",
2557
+ pos: psStart,
2558
+ end: pos,
2559
+ text: raw,
2560
+ operator: ch === CH_LT ? "<" : ">",
2561
+ script: undefined,
2562
+ };
2563
+ parts.push(part);
2564
+ this.collect(part, inner, psStart + 2, !this._unbalanced);
2565
+ litStart = pos;
2566
+ }
2567
+ continue;
2568
+ }
2569
+ text += src[pos];
2570
+ if (bp)
2571
+ litBuf += src[pos];
2572
+ pos++;
2573
+ }
2574
+ if (bp && pos > litStart)
2575
+ parts.push({ type: "Literal", pos: litStart, end: pos, value: litBuf, text: src.slice(litStart, pos) });
2576
+ this.pos = pos;
2577
+ this._wordText = text;
2578
+ this._wordRaw = false;
2579
+ this._wordQuoted = false;
2580
+ this._wordHasExpansions = false;
2581
+ this._wordKeywordEligible = false;
2582
+ if (bp) {
2583
+ this._wordParts = parts.length > 1 || (parts.length === 1 && parts[0].type !== "Literal") ? parts : null;
2584
+ }
2585
+ }
2586
+ // Parse a parameter-expansion sub-field (operand, slice bound, replacement pattern) over
2587
+ // the window [start, end) of the original source. Parsing in place — rather than against a
2588
+ // detached slice — gives the word and any nested substitutions absolute offsets, and
2589
+ // composes through nested ${...}. The `${...}` inner is a verbatim substring of the
2590
+ // source, so every sub-field offset maps straight back.
2591
+ parseSubFieldWord(start, end) {
2592
+ if (start >= end)
2593
+ return new WordImpl("", start, start);
2594
+ // Sub-fields nest through readInnerWordText (nested expansions, arithmetic embedded
2595
+ // words); once the shared budget is spent, keep the raw span as an unstructured word.
2596
+ if (this._nestingDepth >= MAX_SYNTAX_NESTING)
2597
+ return new WordImpl(this.src.slice(start, end), start, end);
2598
+ this._nestingDepth++;
2599
+ const savedEnd = this.srcEnd;
2600
+ const savedPos = this.pos;
2601
+ const savedText = this._wordText;
2602
+ const savedParts = this._wordParts;
2603
+ const savedQuoted = this._wordQuoted;
2604
+ const savedKeywordEligible = this._wordKeywordEligible;
2605
+ this.srcEnd = end;
2606
+ this.pos = start;
2607
+ this.readInnerWordText();
2608
+ // text is the raw source span (so source.slice(pos, end) === text holds, as for every
2609
+ // other word); value resolves quotes via parts.
2610
+ const word = new WordImpl(this.src.slice(start, end), start, end);
2611
+ if (this._buildParts && this._wordParts) {
2612
+ word.parts = this._wordParts;
2613
+ }
2614
+ this.srcEnd = savedEnd;
2615
+ this.pos = savedPos;
2616
+ this._wordText = savedText;
2617
+ this._wordParts = savedParts;
2618
+ this._wordQuoted = savedQuoted;
2619
+ this._wordKeywordEligible = savedKeywordEligible;
2620
+ this._nestingDepth--;
2621
+ return word;
2622
+ }
2623
+ skipSQ() {
2624
+ while (this.pos < this.srcEnd && this.src.charCodeAt(this.pos) !== CH_SQUOTE)
2625
+ this.pos++;
2626
+ if (this.pos < this.srcEnd)
2627
+ this.pos++;
2628
+ }
2629
+ skipAnsiCQuoted() {
2630
+ const quotePos = this.pos - 1;
2631
+ const result = decodeAnsiCQuoted(this.src, this.pos, this.srcEnd);
2632
+ this.pos = result.end;
2633
+ if (!result.closed)
2634
+ this.errors.push({ message: "unterminated ANSI-C quote", pos: quotePos });
2635
+ }
2636
+ skipDQ() {
2637
+ const src = this.src;
2638
+ const len = this.srcEnd;
2639
+ while (this.pos < len) {
2640
+ const ch = src.charCodeAt(this.pos);
2641
+ if (ch === CH_DQUOTE) {
2642
+ this.pos++;
2643
+ return;
2644
+ }
2645
+ if (ch === CH_BACKSLASH) {
2646
+ this.pos += 2;
2647
+ continue;
2648
+ }
2649
+ if (ch === CH_DOLLAR && this.pos + 1 < len) {
2650
+ const next = src.charCodeAt(this.pos + 1);
2651
+ if (next === CH_LPAREN) {
2652
+ const csStart = this.pos;
2653
+ this.pos += 2;
2654
+ this.extractBalanced();
2655
+ if (this._unbalanced)
2656
+ this.errors.push({ message: "unterminated command substitution", pos: csStart });
2657
+ continue;
2658
+ }
2659
+ if (next === CH_LBRACE) {
2660
+ this.pos += 2;
2661
+ let d = 1;
2662
+ while (this.pos < len && d > 0) {
2663
+ const c = src.charCodeAt(this.pos);
2664
+ if (c === CH_RBRACE) {
2665
+ if (--d === 0) {
2666
+ this.pos++;
2667
+ break;
2668
+ }
2669
+ }
2670
+ else if (c === CH_LBRACE && this.pos > 0 && src.charCodeAt(this.pos - 1) === CH_DOLLAR)
2671
+ d++;
2672
+ else if (c === CH_BACKSLASH) {
2673
+ this.pos++;
2674
+ }
2675
+ else if (c === CH_SQUOTE) {
2676
+ this.pos++;
2677
+ this.skipSQ();
2678
+ continue;
2679
+ }
2680
+ else if (c === CH_DQUOTE) {
2681
+ this.pos++;
2682
+ this.skipDQ();
2683
+ continue;
2684
+ }
2685
+ this.pos++;
2686
+ }
2687
+ continue;
2688
+ }
2689
+ }
2690
+ if (ch === CH_BACKTICK) {
2691
+ this.pos++;
2692
+ while (this.pos < len && src.charCodeAt(this.pos) !== CH_BACKTICK) {
2693
+ if (src.charCodeAt(this.pos) === CH_BACKSLASH)
2694
+ this.pos++;
2695
+ this.pos++;
2696
+ }
2697
+ if (this.pos < len)
2698
+ this.pos++;
2699
+ continue;
2700
+ }
2701
+ this.pos++;
2702
+ }
2703
+ }
2704
+ skipSpacesAndTabs() {
2705
+ const src = this.src;
2706
+ const len = this.srcEnd;
2707
+ while (this.pos < len) {
2708
+ const ch = src.charCodeAt(this.pos);
2709
+ if (ch === CH_SPACE || ch === CH_TAB)
2710
+ this.pos++;
2711
+ else if (ch === CH_BACKSLASH && this.pos + 1 < len && src.charCodeAt(this.pos + 1) === CH_NL)
2712
+ this.pos += 2;
2713
+ else
2714
+ break;
2715
+ }
2716
+ }
2717
+ readDoubleQuoted() {
2718
+ const src = this.src;
2719
+ const len = this.srcEnd;
2720
+ const contentStart = this.pos;
2721
+ let hasExpansions = false;
2722
+ const bp = this._buildParts;
2723
+ const bt = bp || this._buildValue;
2724
+ // Fast path: pure literal content (no $, `, or \ — just find closing ")
2725
+ if (!bp) {
2726
+ let p = this.pos;
2727
+ while (p < len) {
2728
+ const c = src.charCodeAt(p);
2729
+ if (c === CH_DQUOTE) {
2730
+ this._dqText = bt ? src.slice(contentStart, p) : "";
2731
+ this._dqEnd = p;
2732
+ this.pos = p + 1;
2733
+ this._dqHasExpansions = false;
2734
+ this._dqParts = null;
2735
+ return;
2736
+ }
2737
+ if (c === CH_DOLLAR || c === CH_BACKTICK || c === CH_BACKSLASH)
2738
+ break;
2739
+ p++;
2740
+ }
2741
+ // Fall through to general path
2742
+ }
2743
+ let text = "";
2744
+ let parts = null;
2745
+ let litBuf = "";
2746
+ let litStart = bp ? this.pos : 0;
2747
+ while (this.pos < len && src.charCodeAt(this.pos) !== CH_DQUOTE) {
2748
+ // Scan run of plain chars inside double quotes
2749
+ const runStart = this.pos;
2750
+ while (this.pos < len) {
2751
+ const c = src.charCodeAt(this.pos);
2752
+ if (c === CH_DQUOTE || c === CH_BACKSLASH || c === CH_DOLLAR || c === CH_BACKTICK)
2753
+ break;
2754
+ this.pos++;
2755
+ }
2756
+ if (bt && this.pos > runStart) {
2757
+ const chunk = src.slice(runStart, this.pos);
2758
+ text += chunk;
2759
+ if (bp)
2760
+ litBuf += chunk;
2761
+ }
2762
+ if (this.pos >= len || src.charCodeAt(this.pos) === CH_DQUOTE)
2763
+ break;
2764
+ const ch = src.charCodeAt(this.pos);
2765
+ if (ch === CH_BACKSLASH) {
2766
+ this.pos++;
2767
+ if (this.pos < len) {
2768
+ const next = src.charCodeAt(this.pos);
2769
+ if (next === CH_NL) {
2770
+ this.pos++;
2771
+ continue;
2772
+ }
2773
+ if (bt) {
2774
+ if (next === CH_DOLLAR || next === CH_BACKTICK || next === CH_DQUOTE || next === CH_BACKSLASH) {
2775
+ const c = src[this.pos];
2776
+ text += c;
2777
+ if (bp)
2778
+ litBuf += c;
2779
+ }
2780
+ else {
2781
+ const pair = "\\" + src[this.pos];
2782
+ text += pair;
2783
+ if (bp)
2784
+ litBuf += pair;
2785
+ }
2786
+ }
2787
+ this.pos++;
2788
+ }
2789
+ continue;
2790
+ }
2791
+ if (ch === CH_DOLLAR) {
2792
+ // Inside double quotes `$"` and `$'` are a literal $: no locale string, no ANSI-C quote.
2793
+ const afterDollar = this.pos + 1 < len ? src.charCodeAt(this.pos + 1) : 0;
2794
+ if (afterDollar === CH_DQUOTE || afterDollar === CH_SQUOTE) {
2795
+ if (bt) {
2796
+ text += "$";
2797
+ if (bp)
2798
+ litBuf += "$";
2799
+ }
2800
+ this.pos++;
2801
+ continue;
2802
+ }
2803
+ const expStart = this.pos;
2804
+ this.readDollar();
2805
+ if (bt)
2806
+ text += this._resultText;
2807
+ if (this._resultHasExpansion)
2808
+ hasExpansions = true;
2809
+ if (bp) {
2810
+ const rp = this._resultPart;
2811
+ if (rp && isDQChild(rp)) {
2812
+ if (!parts)
2813
+ parts = [];
2814
+ if (expStart > litStart) {
2815
+ parts.push({
2816
+ type: "Literal",
2817
+ pos: litStart,
2818
+ end: expStart,
2819
+ value: litBuf,
2820
+ text: src.slice(litStart, expStart),
2821
+ });
2822
+ litBuf = "";
2823
+ }
2824
+ parts.push(rp);
2825
+ litStart = this.pos;
2826
+ }
2827
+ else {
2828
+ litBuf += this._resultText;
2829
+ }
2830
+ }
2831
+ continue;
2832
+ }
2833
+ if (ch === CH_BACKTICK) {
2834
+ const btStart = this.pos;
2835
+ this.readBacktickExpansion(true);
2836
+ if (bt)
2837
+ text += this._resultText;
2838
+ hasExpansions = true;
2839
+ if (bp && this._resultPart && isDQChild(this._resultPart)) {
2840
+ if (!parts)
2841
+ parts = [];
2842
+ if (btStart > litStart) {
2843
+ parts.push({
2844
+ type: "Literal",
2845
+ pos: litStart,
2846
+ end: btStart,
2847
+ value: litBuf,
2848
+ text: src.slice(litStart, btStart),
2849
+ });
2850
+ litBuf = "";
2851
+ }
2852
+ parts.push(this._resultPart);
2853
+ litStart = this.pos;
2854
+ }
2855
+ continue;
2856
+ }
2857
+ }
2858
+ if (bp && parts && this.pos > litStart)
2859
+ parts.push({ type: "Literal", pos: litStart, end: this.pos, value: litBuf, text: src.slice(litStart, this.pos) });
2860
+ this._dqEnd = this.pos;
2861
+ if (this.pos < len)
2862
+ this.pos++; // closing "
2863
+ else
2864
+ this.errors.push({ message: "unterminated double quote", pos: contentStart - 1 });
2865
+ this._dqText = text;
2866
+ this._dqHasExpansions = hasExpansions;
2867
+ this._dqParts = parts;
2868
+ }
2869
+ readDollar() {
2870
+ const dollarPos = this.pos;
2871
+ this.pos++; // skip $
2872
+ const src = this.src;
2873
+ const len = this.srcEnd;
2874
+ const bt = this._buildParts || this._buildValue;
2875
+ if (this.pos >= len) {
2876
+ this._resultText = "$";
2877
+ this._resultIsRaw = true;
2878
+ this._resultHasExpansion = false;
2879
+ this._resultPart = undefined;
2880
+ return;
2881
+ }
2882
+ const logicalPos = skipLineContinuations(src, this.pos, len);
2883
+ if (logicalPos > this.pos &&
2884
+ src.charCodeAt(logicalPos) === CH_LPAREN &&
2885
+ src.charCodeAt(logicalPos + 1) !== CH_LPAREN)
2886
+ this.pos = logicalPos;
2887
+ const ch = src.charCodeAt(this.pos);
2888
+ if (ch === CH_LPAREN) {
2889
+ if (this.pos + 1 < len && src.charCodeAt(this.pos + 1) === CH_LPAREN) {
2890
+ const savedPos = this.pos;
2891
+ const savedErrors = this.errors.length;
2892
+ this.readArithmeticExpansion(dollarPos);
2893
+ if (!this._notArithmetic)
2894
+ return;
2895
+ this.errors.length = savedErrors;
2896
+ this.pos = savedPos;
2897
+ }
2898
+ this.readCommandSubstitution(dollarPos);
2899
+ return;
2900
+ }
2901
+ if (ch === CH_LBRACE) {
2902
+ const after = this.pos + 1 < len ? src.charCodeAt(this.pos + 1) : 0;
2903
+ if (after === CH_SPACE || after === CH_TAB || after === CH_NL) {
2904
+ this.readBraceCommandSubstitution();
2905
+ return;
2906
+ }
2907
+ if (after === CH_PIPE) {
2908
+ this.readValueSubstitution();
2909
+ return;
2910
+ }
2911
+ this.readParameterExpansion();
2912
+ return;
2913
+ }
2914
+ if (ch === CH_SQUOTE) {
2915
+ this.pos++;
2916
+ if (bt) {
2917
+ const value = this.readAnsiCQuoted();
2918
+ this._resultText = value;
2919
+ this._resultPart = this._buildParts
2920
+ ? { type: "AnsiCQuoted", pos: dollarPos, end: this.pos, text: src.slice(dollarPos, this.pos), value }
2921
+ : undefined;
2922
+ }
2923
+ else {
2924
+ this.skipAnsiCQuoted();
2925
+ this._resultText = "";
2926
+ this._resultPart = undefined;
2927
+ }
2928
+ this._resultIsRaw = false;
2929
+ this._resultHasExpansion = false;
2930
+ return;
2931
+ }
2932
+ if (ch === CH_DQUOTE) {
2933
+ this.pos++;
2934
+ this.readDoubleQuoted();
2935
+ this._resultText = this._dqText;
2936
+ this._resultIsRaw = false;
2937
+ this._resultHasExpansion = this._dqHasExpansions;
2938
+ if (this._buildParts) {
2939
+ const text = src.slice(dollarPos, this.pos);
2940
+ this._resultPart = {
2941
+ type: "LocaleString",
2942
+ pos: dollarPos,
2943
+ end: this.pos,
2944
+ text,
2945
+ parts: this._dqParts ?? [
2946
+ {
2947
+ type: "Literal",
2948
+ pos: dollarPos + 2,
2949
+ end: this._dqEnd,
2950
+ value: this._dqText,
2951
+ text: src.slice(dollarPos + 2, this._dqEnd),
2952
+ },
2953
+ ],
2954
+ };
2955
+ }
2956
+ else {
2957
+ this._resultPart = undefined;
2958
+ }
2959
+ return;
2960
+ }
2961
+ if (ch === CH_AT ||
2962
+ ch === CH_STAR ||
2963
+ ch === CH_HASH ||
2964
+ ch === CH_QUESTION ||
2965
+ ch === CH_DASH ||
2966
+ ch === CH_DOLLAR ||
2967
+ ch === CH_BANG ||
2968
+ (ch >= CH_0 && ch <= CH_9)) {
2969
+ this.pos++;
2970
+ const text = bt ? src.slice(this.pos - 2, this.pos) : "";
2971
+ this._resultText = text;
2972
+ this._resultIsRaw = true;
2973
+ this._resultHasExpansion = false;
2974
+ this._resultPart = this._buildParts
2975
+ ? { type: "SimpleExpansion", pos: dollarPos, end: this.pos, text }
2976
+ : undefined;
2977
+ return;
2978
+ }
2979
+ if (ch < 128 && isIdChar[ch] & 1) {
2980
+ const namePos = this.pos - 1;
2981
+ while (this.pos < len) {
2982
+ const c = src.charCodeAt(this.pos);
2983
+ if (c < 128 && isIdChar[c] & 2)
2984
+ this.pos++;
2985
+ else
2986
+ break;
2987
+ }
2988
+ const text = bt ? src.slice(namePos, this.pos) : "";
2989
+ this._resultText = text;
2990
+ this._resultIsRaw = true;
2991
+ this._resultHasExpansion = false;
2992
+ this._resultPart = this._buildParts
2993
+ ? { type: "SimpleExpansion", pos: dollarPos, end: this.pos, text }
2994
+ : undefined;
2995
+ return;
2996
+ }
2997
+ // $[ expr ] is bash's deprecated spelling of $(( expr )). It runs to the matching ']',
2998
+ // so parentheses inside belong to the expansion instead of ending the word.
2999
+ if (ch === CH_LBRACKET) {
3000
+ const close = this.findClosingArithmeticBracket(this.pos + 1);
3001
+ if (close !== -1) {
3002
+ const bodyStart = this.pos + 1;
3003
+ const body = src.slice(bodyStart, close);
3004
+ this.pos = close + 1;
3005
+ const text = bt ? src.slice(dollarPos, this.pos) : "";
3006
+ this._resultText = text;
3007
+ this._resultIsRaw = true;
3008
+ this._resultHasExpansion = false;
3009
+ this._resultPart = this._buildParts
3010
+ ? {
3011
+ type: "ArithmeticExpansion",
3012
+ pos: bodyStart - 2,
3013
+ end: this.pos,
3014
+ text,
3015
+ expression: this.buildArithmeticExpression(body, bodyStart),
3016
+ }
3017
+ : undefined;
3018
+ return;
3019
+ }
3020
+ }
3021
+ this._resultText = "$";
3022
+ this._resultIsRaw = true;
3023
+ this._resultHasExpansion = false;
3024
+ this._resultPart = undefined;
3025
+ }
3026
+ scanArithmeticBody() {
3027
+ this._notArithmetic = false;
3028
+ this._arithUnterminated = false;
3029
+ this.pos += 2;
3030
+ let depth = 1;
3031
+ let parenDepth = 0;
3032
+ let parentParenDepth = 0;
3033
+ let parenDepths;
3034
+ let expansions = 0;
3035
+ let reported = false;
3036
+ const src = this.src;
3037
+ const len = this.srcEnd;
3038
+ const start = this.pos;
3039
+ while (this.pos < len && depth > 0) {
3040
+ const c = src.charCodeAt(this.pos);
3041
+ if (c === CH_BACKSLASH) {
3042
+ this.pos += this.pos + 1 < len ? 2 : 1;
3043
+ }
3044
+ else if (c === CH_SQUOTE) {
3045
+ this.pos++;
3046
+ this.skipSQ();
3047
+ }
3048
+ else if (c === CH_DQUOTE) {
3049
+ this.pos++;
3050
+ this.skipDQ();
3051
+ }
3052
+ else if (c === CH_BACKTICK) {
3053
+ this.pos++;
3054
+ while (this.pos < len && src.charCodeAt(this.pos) !== CH_BACKTICK) {
3055
+ if (src.charCodeAt(this.pos) === CH_BACKSLASH)
3056
+ this.pos++;
3057
+ this.pos++;
3058
+ }
3059
+ if (this.pos < len)
3060
+ this.pos++;
3061
+ }
3062
+ else if (c === CH_DOLLAR &&
3063
+ this.pos + 2 < len &&
3064
+ src.charCodeAt(this.pos + 1) === CH_LPAREN &&
3065
+ src.charCodeAt(this.pos + 2) !== CH_LPAREN) {
3066
+ const dollarPos = this.pos;
3067
+ this.pos += 2;
3068
+ this.extractBalanced();
3069
+ if (this._unbalanced)
3070
+ this.errors.push({ message: "unterminated command substitution", pos: dollarPos });
3071
+ }
3072
+ else if (c === CH_DOLLAR && this.pos + 1 < len && src.charCodeAt(this.pos + 1) === CH_LBRACE) {
3073
+ const close = this.findClosingBrace(this.pos + 2, len);
3074
+ this.pos = close === -1 ? len : close + 1;
3075
+ }
3076
+ else if ((c === CH_LT || c === CH_GT) && this.pos + 1 < len && src.charCodeAt(this.pos + 1) === CH_LPAREN) {
3077
+ this.pos += 2;
3078
+ this.extractBalanced();
3079
+ }
3080
+ else if (c === CH_LPAREN) {
3081
+ if (src.charCodeAt(this.pos - 1) === CH_DOLLAR && src.charCodeAt(this.pos + 1) === CH_LPAREN) {
3082
+ if (depth === 1)
3083
+ parentParenDepth = parenDepth;
3084
+ else
3085
+ (parenDepths ?? (parenDepths = [])).push(parenDepth);
3086
+ depth++;
3087
+ parenDepth = 0;
3088
+ // Nested $((...)) — count it against the shared budget (the expansion this scan
3089
+ // belongs to is level one, hence >=) so over-deep chains surface a parse error.
3090
+ if (++expansions + this._nestingDepth >= MAX_SYNTAX_NESTING) {
3091
+ if (!reported) {
3092
+ this.errors.push({ message: "maximum arithmetic expansion nesting depth exceeded", pos: this.pos - 1 });
3093
+ reported = true;
3094
+ }
3095
+ }
3096
+ this.pos += 2;
3097
+ }
3098
+ else {
3099
+ parenDepth++;
3100
+ this.pos++;
3101
+ }
3102
+ }
3103
+ else if (c === CH_RPAREN && parenDepth > 0) {
3104
+ parenDepth--;
3105
+ this.pos++;
3106
+ }
3107
+ else if (c === CH_RPAREN && this.pos + 1 < len && src.charCodeAt(this.pos + 1) === CH_RPAREN) {
3108
+ if (--depth === 0) {
3109
+ this.pos += 2;
3110
+ break;
3111
+ }
3112
+ parenDepth = depth === 1 ? parentParenDepth : parenDepths.pop();
3113
+ this.pos += 2;
3114
+ }
3115
+ else if (c === CH_RPAREN && depth === 1) {
3116
+ // Bash reads `((` and `$((` as arithmetic only when the inner parenthesis pair is
3117
+ // immediately followed by `)`. It closed without one, so `((a) )` is nested subshells
3118
+ // and `$((a) || (b))` a command substitution; callers re-read from the start.
3119
+ this._notArithmetic = true;
3120
+ return "";
3121
+ }
3122
+ else {
3123
+ this.pos++;
3124
+ }
3125
+ }
3126
+ const bt = this._buildParts || this._buildValue;
3127
+ if (depth > 0) {
3128
+ this._arithUnterminated = true;
3129
+ return bt ? src.slice(start, this.pos) : "";
3130
+ }
3131
+ return bt ? src.slice(start, this.pos - 2) : "";
3132
+ }
3133
+ readArithmeticExpansion(dollarPos) {
3134
+ const bodyStart = this.pos + 2; // absolute offset of the body, past the "((" at this.pos
3135
+ const body = this.scanArithmeticBody();
3136
+ if (this._notArithmetic)
3137
+ return;
3138
+ if (this._arithUnterminated)
3139
+ this.errors.push({ message: "unterminated arithmetic expansion", pos: dollarPos });
3140
+ const text = this._buildParts || this._buildValue ? this.src.slice(dollarPos, this.pos) : "";
3141
+ this._resultText = text;
3142
+ this._resultIsRaw = true;
3143
+ this._resultHasExpansion = false;
3144
+ this._resultPart = this._buildParts
3145
+ ? {
3146
+ type: "ArithmeticExpansion",
3147
+ pos: dollarPos,
3148
+ end: this.pos,
3149
+ text,
3150
+ expression: this.buildArithmeticExpression(body, bodyStart),
3151
+ }
3152
+ : undefined;
3153
+ }
3154
+ // Pass the absolute body offset so arithmetic nodes index the original source directly
3155
+ // (no re-basing). Nested $(...) command subs inside the arithmetic get an absolute
3156
+ // innerStart so resolveCollected parses their window in place.
3157
+ buildArithmeticExpression(body, bodyStart) {
3158
+ if (!hasEmbeddedWordStructure(this.src, bodyStart, bodyStart + body.length)) {
3159
+ return parseArithmeticExpression(body, bodyStart) ?? undefined;
3160
+ }
3161
+ const commandExpansions = [];
3162
+ const embeddedWords = [];
3163
+ const expr = parseArithmeticExpression(body, bodyStart, {
3164
+ commandExpansions,
3165
+ embeddedWords,
3166
+ findClosingBracket: (start, end) => this.findClosingBracket(start, end),
3167
+ findClosingBrace: (start, end) => this.findClosingBrace(start, end),
3168
+ findClosingParenthesis: (start, end) => this.findClosingParenthesis(start, end),
3169
+ findArithmeticExpansionEnd: (start, end) => this.findArithmeticExpansionEnd(start, end),
3170
+ findArithmeticWordEnd: (start, end) => this.findArithmeticWordEnd(start, end),
3171
+ }) ?? undefined;
3172
+ for (const node of commandExpansions) {
3173
+ this.collect(node, node.text.slice(2, -1), node.pos + 2, true);
3174
+ }
3175
+ for (const node of embeddedWords)
3176
+ node.parts = this.parseSubFieldWord(node.pos, node.end).parts;
3177
+ return expr;
3178
+ }
3179
+ readArithmeticCommand(out, tokenStart) {
3180
+ const savedBuildValue = this._buildValue;
3181
+ this._buildValue = true;
3182
+ const body = this.scanArithmeticBody();
3183
+ this._buildValue = savedBuildValue;
3184
+ if (this._arithUnterminated)
3185
+ this.errors.push({ message: "unterminated arithmetic command", pos: tokenStart });
3186
+ setToken(out, Token.ArithCmd, body, tokenStart, this.pos);
3187
+ }
3188
+ readCommandSubstitution(dollarPos) {
3189
+ const openPos = this.pos;
3190
+ this.pos++; // skip (
3191
+ // extractBalanced returns the inner text without the closing paren, so it
3192
+ // stays correct when the input ended before that paren was reached.
3193
+ const inner = this.extractBalanced();
3194
+ if (this._unbalanced)
3195
+ this.errors.push({ message: "unterminated command substitution", pos: dollarPos });
3196
+ const bt = this._buildParts || this._buildValue;
3197
+ const rawText = bt ? this.src.slice(dollarPos, this.pos) : "";
3198
+ const text = !bt || openPos === dollarPos + 1 ? rawText : "$" + this.src.slice(openPos, this.pos);
3199
+ this._resultText = text;
3200
+ this._resultIsRaw = openPos === dollarPos + 1;
3201
+ this._resultHasExpansion = true;
3202
+ if (this._buildParts) {
3203
+ this._resultPart = { type: "CommandExpansion", pos: dollarPos, end: this.pos, text: rawText, script: undefined };
3204
+ this.collect(this._resultPart, inner, openPos + 1, !this._unbalanced);
3205
+ }
3206
+ else {
3207
+ this._resultPart = undefined;
3208
+ }
3209
+ }
3210
+ readBraceCommandSubstitution() {
3211
+ this.readBraceSubstitution(1);
3212
+ }
3213
+ readValueSubstitution() {
3214
+ this.readBraceSubstitution(2);
3215
+ }
3216
+ readBraceSubstitution(skip) {
3217
+ const dollarPos = this.pos - 1;
3218
+ this.pos += skip;
3219
+ const src = this.src;
3220
+ const len = this.srcEnd;
3221
+ let depth = 1;
3222
+ const start = this.pos;
3223
+ while (this.pos < len) {
3224
+ const c = src.charCodeAt(this.pos);
3225
+ if (c === CH_LBRACE)
3226
+ depth++;
3227
+ else if (c === CH_RBRACE) {
3228
+ if (--depth === 0) {
3229
+ this.pos++;
3230
+ break;
3231
+ }
3232
+ }
3233
+ else if (c === CH_SQUOTE) {
3234
+ this.pos++;
3235
+ this.skipSQ();
3236
+ continue;
3237
+ }
3238
+ else if (c === CH_DQUOTE) {
3239
+ this.pos++;
3240
+ this.skipDQ();
3241
+ continue;
3242
+ }
3243
+ else if (c === CH_BACKSLASH)
3244
+ this.pos++;
3245
+ this.pos++;
3246
+ }
3247
+ this._resultIsRaw = true;
3248
+ this._resultHasExpansion = true;
3249
+ if (this._buildParts || this._buildValue) {
3250
+ const rawInner = src.slice(start, this.pos - 1);
3251
+ const inner = rawInner.trim();
3252
+ const text = src.slice(dollarPos, this.pos);
3253
+ this._resultText = text;
3254
+ if (this._buildParts) {
3255
+ const innerStart = start + (rawInner.length - rawInner.trimStart().length);
3256
+ this._resultPart = { type: "CommandExpansion", pos: dollarPos, end: this.pos, text, script: undefined };
3257
+ this.collect(this._resultPart, inner, innerStart);
3258
+ }
3259
+ else {
3260
+ this._resultPart = undefined;
3261
+ }
3262
+ }
3263
+ else {
3264
+ this._resultText = "";
3265
+ this._resultPart = undefined;
3266
+ }
3267
+ }
3268
+ readBacktickExpansion(insideDoubleQuotes = false) {
3269
+ this.pos++; // skip opening `
3270
+ const src = this.src;
3271
+ const len = this.srcEnd;
3272
+ const start = this.pos;
3273
+ if (!this._buildParts && !this._buildValue) {
3274
+ while (this.pos < len && src.charCodeAt(this.pos) !== CH_BACKTICK) {
3275
+ if (src.charCodeAt(this.pos) === CH_BACKSLASH && this.pos + 1 < len)
3276
+ this.pos++;
3277
+ this.pos++;
3278
+ }
3279
+ if (this.pos < len)
3280
+ this.pos++;
3281
+ else
3282
+ this.errors.push({ message: "unterminated backtick", pos: start - 1 });
3283
+ this._resultText = "";
3284
+ this._resultIsRaw = false;
3285
+ this._resultHasExpansion = true;
3286
+ this._resultPart = undefined;
3287
+ return;
3288
+ }
3289
+ let inner = "";
3290
+ let hasEscapes = false;
3291
+ while (this.pos < len && src.charCodeAt(this.pos) !== CH_BACKTICK) {
3292
+ if (src.charCodeAt(this.pos) === CH_BACKSLASH) {
3293
+ hasEscapes = true;
3294
+ break;
3295
+ }
3296
+ this.pos++;
3297
+ }
3298
+ if (!hasEscapes) {
3299
+ inner = src.slice(start, this.pos);
3300
+ }
3301
+ else {
3302
+ inner = src.slice(start, this.pos);
3303
+ while (this.pos < len && src.charCodeAt(this.pos) !== CH_BACKTICK) {
3304
+ if (src.charCodeAt(this.pos) === CH_BACKSLASH) {
3305
+ this.pos++;
3306
+ if (this.pos < len) {
3307
+ const c = src.charCodeAt(this.pos);
3308
+ if (c === CH_DOLLAR || c === CH_BACKTICK || c === CH_BACKSLASH || (insideDoubleQuotes && c === CH_DQUOTE)) {
3309
+ inner += src[this.pos];
3310
+ }
3311
+ else {
3312
+ inner += "\\" + src[this.pos];
3313
+ }
3314
+ this.pos++;
3315
+ }
3316
+ }
3317
+ else {
3318
+ const runStart = this.pos;
3319
+ while (this.pos < len) {
3320
+ const c = src.charCodeAt(this.pos);
3321
+ if (c === CH_BACKTICK || c === CH_BACKSLASH)
3322
+ break;
3323
+ this.pos++;
3324
+ }
3325
+ inner += src.slice(runStart, this.pos);
3326
+ }
3327
+ }
3328
+ }
3329
+ if (this.pos < len)
3330
+ this.pos++; // closing `
3331
+ else
3332
+ this.errors.push({ message: "unterminated backtick", pos: start - 1 });
3333
+ const text = src.slice(start - 1, this.pos); // raw source including backticks
3334
+ this._resultText = inner;
3335
+ this._resultHasExpansion = true;
3336
+ if (this._buildParts) {
3337
+ // Escaped backticks rebuild `inner` (escapes removed), so its offsets no
3338
+ // longer map linearly onto the source — leave innerStart undefined there.
3339
+ this._resultPart = {
3340
+ type: "CommandExpansion",
3341
+ pos: start - 1,
3342
+ end: this.pos,
3343
+ text,
3344
+ script: undefined,
3345
+ };
3346
+ this.collect(this._resultPart, inner, hasEscapes ? undefined : start);
3347
+ }
3348
+ else {
3349
+ this._resultPart = undefined;
3350
+ }
3351
+ }
3352
+ readParameterExpansion() {
3353
+ const src = this.src;
3354
+ const len = this.srcEnd;
3355
+ const start = this.pos; // at {
3356
+ this.pos++;
3357
+ let depth = 1;
3358
+ let reported = false;
3359
+ while (this.pos < len && depth > 0) {
3360
+ const ch = src.charCodeAt(this.pos);
3361
+ if (ch === CH_DOLLAR) {
3362
+ // Dispatch forward: `$$` consumes its own second `$`, so a `{` after it is literal.
3363
+ const next = this.pos + 1 < len ? src.charCodeAt(this.pos + 1) : 0;
3364
+ if (next === CH_LBRACE) {
3365
+ depth++;
3366
+ if (this._nestingDepth + depth > MAX_SYNTAX_NESTING && !reported) {
3367
+ this.errors.push({ message: "maximum parameter expansion nesting depth exceeded", pos: this.pos });
3368
+ reported = true;
3369
+ }
3370
+ this.pos += 2;
3371
+ continue;
3372
+ }
3373
+ if (next === CH_DOLLAR) {
3374
+ this.pos += 2;
3375
+ continue;
3376
+ }
3377
+ if (next === CH_LPAREN) {
3378
+ const dollarPos = this.pos;
3379
+ this.pos += 2;
3380
+ this.extractBalanced();
3381
+ if (this._unbalanced)
3382
+ this.errors.push({ message: "unterminated command substitution", pos: dollarPos });
3383
+ continue;
3384
+ }
3385
+ }
3386
+ else if (ch === CH_BACKTICK) {
3387
+ this.pos++;
3388
+ while (this.pos < len && src.charCodeAt(this.pos) !== CH_BACKTICK) {
3389
+ if (src.charCodeAt(this.pos) === CH_BACKSLASH)
3390
+ this.pos++;
3391
+ this.pos++;
3392
+ }
3393
+ if (this.pos < len)
3394
+ this.pos++;
3395
+ continue;
3396
+ }
3397
+ else if (ch === CH_RBRACE) {
3398
+ if (--depth === 0) {
3399
+ this.pos++;
3400
+ break;
3401
+ }
3402
+ }
3403
+ else if (ch === CH_BACKSLASH) {
3404
+ if (this.pos + 1 < len)
3405
+ this.pos++;
3406
+ }
3407
+ else if (ch === CH_SQUOTE) {
3408
+ this.pos++;
3409
+ if (this.pos > start + 1 && src.charCodeAt(this.pos - 2) === CH_DOLLAR)
3410
+ this.skipAnsiCQuoted();
3411
+ else
3412
+ this.skipSQ();
3413
+ continue;
3414
+ }
3415
+ else if (ch === CH_DQUOTE) {
3416
+ this.pos++;
3417
+ this.skipDQ();
3418
+ continue;
3419
+ }
3420
+ this.pos++;
3421
+ }
3422
+ const closed = depth === 0;
3423
+ if (!closed)
3424
+ this.errors.push({ message: "unterminated parameter expansion", pos: start - 1 });
3425
+ const text = this._buildParts || this._buildValue ? src.slice(start - 1, this.pos) : "";
3426
+ this._resultText = text;
3427
+ this._resultIsRaw = true;
3428
+ this._resultHasExpansion = false;
3429
+ if (this._buildParts) {
3430
+ const inner = src.slice(start + 1, closed ? this.pos - 1 : this.pos);
3431
+ this._resultPart = this.parseParamInner(text, inner, start + 1);
3432
+ }
3433
+ else {
3434
+ this._resultPart = undefined;
3435
+ }
3436
+ }
3437
+ // `innerStart` is the absolute offset of `inner` in the original source, so each sub-field
3438
+ // word is parsed in place at its true position. `sub(a, b)` maps inner-relative offsets to
3439
+ // that absolute window.
3440
+ parseParamInner(text, inner, innerStart) {
3441
+ const result = {
3442
+ type: "ParameterExpansion",
3443
+ pos: innerStart - 2,
3444
+ end: this.pos,
3445
+ text,
3446
+ parameter: "",
3447
+ parameterPos: innerStart,
3448
+ parameterEnd: innerStart,
3449
+ prefix: undefined,
3450
+ index: undefined,
3451
+ operation: undefined,
3452
+ };
3453
+ const ilen = inner.length;
3454
+ if (ilen === 0)
3455
+ return result;
3456
+ const sub = (a, b) => this.parseSubFieldWord(innerStart + a, innerStart + b);
3457
+ const closeBracket = (start) => {
3458
+ const close = this.findClosingBracket(innerStart + start, innerStart + ilen);
3459
+ return close === -1 ? -1 : close - innerStart;
3460
+ };
3461
+ let i = 0;
3462
+ if (ilen > 1 && inner.charCodeAt(0) === CH_BANG) {
3463
+ result.prefix = "!";
3464
+ i = 1;
3465
+ }
3466
+ if (ilen > 1 && inner.charCodeAt(0) === CH_HASH) {
3467
+ const nameEnd = this.scanParamName(inner, 1);
3468
+ let parameterEnd = nameEnd;
3469
+ let indexEnd = -1;
3470
+ if (parameterEnd < ilen && inner.charCodeAt(parameterEnd) === CH_LBRACKET) {
3471
+ indexEnd = closeBracket(parameterEnd + 1);
3472
+ if (indexEnd !== -1)
3473
+ parameterEnd = indexEnd + 1;
3474
+ }
3475
+ if (nameEnd > 1 && parameterEnd === ilen) {
3476
+ result.prefix = "#";
3477
+ result.parameter = inner.slice(1, nameEnd);
3478
+ result.parameterPos = innerStart + 1;
3479
+ result.parameterEnd = innerStart + nameEnd;
3480
+ if (indexEnd !== -1)
3481
+ result.index = sub(nameEnd + 1, indexEnd);
3482
+ return result;
3483
+ }
3484
+ }
3485
+ const nameStart = i;
3486
+ i = this.scanParamName(inner, i);
3487
+ result.parameter = inner.slice(nameStart, i);
3488
+ result.parameterPos = innerStart + nameStart;
3489
+ result.parameterEnd = innerStart + i;
3490
+ if (i < ilen && inner.charCodeAt(i) === CH_LBRACKET) {
3491
+ const closeB = closeBracket(i + 1);
3492
+ if (closeB !== -1) {
3493
+ result.index = sub(i + 1, closeB);
3494
+ i = closeB + 1;
3495
+ }
3496
+ }
3497
+ if (i >= ilen)
3498
+ return result;
3499
+ const pos = innerStart + i;
3500
+ const end = innerStart + ilen;
3501
+ const opChar = inner.charCodeAt(i);
3502
+ const nextChar = inner.charCodeAt(i + 1);
3503
+ const defaultChar = inner[opChar === CH_COLON ? i + 1 : i];
3504
+ if (defaultChar === "-" || defaultChar === "=" || defaultChar === "+" || defaultChar === "?") {
3505
+ const operandStart = i + (opChar === CH_COLON ? 2 : 1);
3506
+ result.operation = {
3507
+ type: "Default",
3508
+ pos,
3509
+ end,
3510
+ operatorEnd: innerStart + operandStart,
3511
+ operator: opChar === CH_COLON ? `:${defaultChar}` : defaultChar,
3512
+ operand: sub(operandStart, ilen),
3513
+ };
3514
+ return result;
3515
+ }
3516
+ if (opChar === CH_COLON) {
3517
+ i++;
3518
+ const sliceStart = innerStart + i;
3519
+ const colonIdx = findUnnested(inner.slice(i), CH_COLON, true, (index, quoted) => {
3520
+ return this.findNestedShellEnd(sliceStart + index, end, quoted) - sliceStart;
3521
+ });
3522
+ result.operation = {
3523
+ type: "Slice",
3524
+ pos,
3525
+ end,
3526
+ operatorEnd: sliceStart,
3527
+ operator: ":",
3528
+ offset: sub(i, colonIdx === -1 ? ilen : i + colonIdx),
3529
+ length: colonIdx === -1 ? undefined : sub(i + colonIdx + 1, ilen),
3530
+ };
3531
+ return result;
3532
+ }
3533
+ if (opChar === CH_HASH || opChar === CH_PERCENT) {
3534
+ const doubled = nextChar === opChar;
3535
+ const operator = opChar === CH_HASH ? (doubled ? "##" : "#") : doubled ? "%%" : "%";
3536
+ const operandStart = i + (doubled ? 2 : 1);
3537
+ result.operation = {
3538
+ type: "Remove",
3539
+ pos,
3540
+ end,
3541
+ operatorEnd: innerStart + operandStart,
3542
+ operator,
3543
+ operand: sub(operandStart, ilen),
3544
+ };
3545
+ return result;
3546
+ }
3547
+ if (opChar === CH_SLASH) {
3548
+ i++;
3549
+ let operator = "/";
3550
+ if (nextChar === CH_SLASH || nextChar === CH_HASH || nextChar === CH_PERCENT) {
3551
+ operator = nextChar === CH_SLASH ? "//" : nextChar === CH_HASH ? "/#" : "/%";
3552
+ i++;
3553
+ }
3554
+ let separatorStart = i;
3555
+ if (nextChar === CH_SLASH) {
3556
+ // After // a leading slash belongs to the pattern; /# and /% allow empty patterns.
3557
+ separatorStart = skipLineContinuations(inner, i, ilen);
3558
+ if (inner.charCodeAt(separatorStart) === CH_SLASH)
3559
+ separatorStart++;
3560
+ }
3561
+ const sepIdx = findUnnested(inner.slice(separatorStart), CH_SLASH);
3562
+ const patternEnd = sepIdx === -1 ? ilen : separatorStart + sepIdx;
3563
+ result.operation = {
3564
+ type: "Replace",
3565
+ pos,
3566
+ end,
3567
+ operatorEnd: innerStart + i,
3568
+ operator,
3569
+ pattern: sub(i, patternEnd),
3570
+ replacement: sub(sepIdx === -1 ? ilen : patternEnd + 1, ilen),
3571
+ };
3572
+ return result;
3573
+ }
3574
+ if (opChar === CH_CARET || opChar === CH_COMMA) {
3575
+ const doubled = nextChar === opChar;
3576
+ const operator = opChar === CH_CARET ? (doubled ? "^^" : "^") : doubled ? ",," : ",";
3577
+ const operandStart = i + (doubled ? 2 : 1);
3578
+ result.operation = {
3579
+ type: "CaseModification",
3580
+ pos,
3581
+ end,
3582
+ operatorEnd: innerStart + operandStart,
3583
+ operator,
3584
+ operand: operandStart === ilen ? undefined : sub(operandStart, ilen),
3585
+ };
3586
+ return result;
3587
+ }
3588
+ if (result.prefix === "!" &&
3589
+ result.index === undefined &&
3590
+ i + 1 === ilen &&
3591
+ (opChar === CH_AT || opChar === CH_STAR)) {
3592
+ result.operation = { type: "Names", pos, end, operatorEnd: end, operator: opChar === CH_AT ? "@" : "*" };
3593
+ return result;
3594
+ }
3595
+ if (opChar === CH_AT) {
3596
+ result.operation = {
3597
+ type: "Transform",
3598
+ pos,
3599
+ end,
3600
+ operatorEnd: pos + 1,
3601
+ operator: "@",
3602
+ operand: sub(i + 1, ilen),
3603
+ };
3604
+ return result;
3605
+ }
3606
+ result.operation = { type: "Unknown", pos, end, operatorEnd: end, operator: inner.slice(i) };
3607
+ return result;
3608
+ }
3609
+ findNestedShellEnd(start, end, quoted) {
3610
+ const ch = this.src.charCodeAt(start);
3611
+ if (ch === CH_BACKTICK) {
3612
+ let pos = start + 1;
3613
+ while (pos < end) {
3614
+ const current = this.src.charCodeAt(pos);
3615
+ if (current === CH_BACKSLASH) {
3616
+ pos += 2;
3617
+ continue;
3618
+ }
3619
+ pos++;
3620
+ if (current === CH_BACKTICK)
3621
+ return pos;
3622
+ }
3623
+ return end;
3624
+ }
3625
+ let close;
3626
+ if (ch === CH_DOLLAR) {
3627
+ const next = start + 1;
3628
+ if (next < end && this.src.charCodeAt(next) === CH_LBRACE) {
3629
+ close = this.findClosingBrace(next + 1, end);
3630
+ }
3631
+ else {
3632
+ const open = skipLineContinuations(this.src, next, end);
3633
+ if (open >= end || this.src.charCodeAt(open) !== CH_LPAREN)
3634
+ return start;
3635
+ close = this.findClosingParenthesis(open + 1, end);
3636
+ }
3637
+ }
3638
+ else {
3639
+ const open = start + 1;
3640
+ if (quoted || (ch !== CH_LT && ch !== CH_GT) || open >= end || this.src.charCodeAt(open) !== CH_LPAREN) {
3641
+ return start;
3642
+ }
3643
+ close = this.findClosingParenthesis(open + 1, end);
3644
+ }
3645
+ return close === -1 ? end : close + 1;
3646
+ }
3647
+ scanParamName(s, start) {
3648
+ let i = start;
3649
+ if (i >= s.length)
3650
+ return i;
3651
+ const c = s.charCodeAt(i);
3652
+ // Special single-char params
3653
+ if (c === CH_AT ||
3654
+ c === CH_STAR ||
3655
+ c === CH_HASH ||
3656
+ c === CH_QUESTION ||
3657
+ c === CH_DASH ||
3658
+ c === CH_DOLLAR ||
3659
+ c === CH_BANG) {
3660
+ return i + 1;
3661
+ }
3662
+ // Digits
3663
+ if (c >= CH_0 && c <= CH_9) {
3664
+ while (i < s.length && s.charCodeAt(i) >= CH_0 && s.charCodeAt(i) <= CH_9)
3665
+ i++;
3666
+ return i;
3667
+ }
3668
+ // Regular name: [a-zA-Z_][a-zA-Z0-9_]*
3669
+ if ((c >= CH_a && c <= CH_z) || (c >= CH_A && c <= CH_Z) || c === CH_UNDERSCORE) {
3670
+ i++;
3671
+ while (i < s.length) {
3672
+ const ch = s.charCodeAt(i);
3673
+ if ((ch >= CH_a && ch <= CH_z) ||
3674
+ (ch >= CH_A && ch <= CH_Z) ||
3675
+ (ch >= CH_0 && ch <= CH_9) ||
3676
+ ch === CH_UNDERSCORE)
3677
+ i++;
3678
+ else
3679
+ break;
3680
+ }
3681
+ }
3682
+ return i;
3683
+ }
3684
+ readAnsiCQuoted() {
3685
+ const quotePos = this.pos - 1;
3686
+ const result = decodeAnsiCQuoted(this.src, this.pos, this.srcEnd);
3687
+ this.pos = result.end;
3688
+ if (!result.closed)
3689
+ this.errors.push({ message: "unterminated ANSI-C quote", pos: quotePos });
3690
+ return result.value;
3691
+ }
3692
+ // Extract balanced parens for $(...) — respects nested quotes and case..esac
3693
+ extractBalanced() {
3694
+ const src = this.src;
3695
+ const len = this.srcEnd;
3696
+ const bt = this._buildParts || this._buildValue;
3697
+ let depth = 1;
3698
+ const start = this.pos;
3699
+ this._unbalanced = false;
3700
+ let wordStart = true;
3701
+ // Fast path: scan for simple cases with no nested quotes/parens/case. Nothing here
3702
+ // opens a paren — `(` breaks to the slow path — so depth stays 1 and the first `)`
3703
+ // closes the construct.
3704
+ while (this.pos < len) {
3705
+ const c = src.charCodeAt(this.pos);
3706
+ if (c === CH_RPAREN) {
3707
+ const result = bt ? src.slice(start, this.pos) : "";
3708
+ this.pos++;
3709
+ return result;
3710
+ }
3711
+ else if (c === CH_LPAREN || c === CH_BACKSLASH || c === CH_SQUOTE || c === CH_DQUOTE || c === CH_BACKTICK) {
3712
+ break;
3713
+ }
3714
+ else if (c === CH_LT && this.pos + 1 < len && src.charCodeAt(this.pos + 1) === CH_LT) {
3715
+ break;
3716
+ }
3717
+ else if (c === CH_HASH && wordStart) {
3718
+ break;
3719
+ }
3720
+ else if (c === 99 /* c */ &&
3721
+ wordStart &&
3722
+ this.pos + 3 < len &&
3723
+ src.charCodeAt(this.pos + 1) === 97 /* a */ &&
3724
+ src.charCodeAt(this.pos + 2) === 115 /* s */ &&
3725
+ src.charCodeAt(this.pos + 3) === 101 /* e */ &&
3726
+ (this.pos + 4 >= len || (src.charCodeAt(this.pos + 4) < 128 && charType[src.charCodeAt(this.pos + 4)] & 1))) {
3727
+ break;
3728
+ }
3729
+ else {
3730
+ wordStart = c < 128 && (charType[c] & 1) !== 0;
3731
+ this.pos++;
3732
+ }
3733
+ }
3734
+ // Slow path: just track position (source is copied verbatim, so slice at end)
3735
+ let caseDepth = 0;
3736
+ // Parentheses opened since the innermost `case` began. A `)` only terminates a case
3737
+ // pattern when nothing is open — otherwise it closes a pattern's leading `(`, an
3738
+ // extglob group, or a subshell in the case body, and must rebalance `depth`.
3739
+ let caseParens = 0;
3740
+ // Word-form parens resume mid-word; grammar parens resume where `#` can open a comment.
3741
+ const wordStartAfterParen = [];
3742
+ let pendingDelims = null;
3743
+ let arithBase = -1;
3744
+ // A `$((` extent is found by bash's arithmetic scanner, where `#` is ordinary, and keeps
3745
+ // that extent even for a command body. The two chars before `start` are the delimiter.
3746
+ const arithExtent = start >= 2 &&
3747
+ src.charCodeAt(start) === CH_LPAREN &&
3748
+ src.charCodeAt(start - 1) === CH_LPAREN &&
3749
+ src.charCodeAt(start - 2) === CH_DOLLAR;
3750
+ let substitutions = 0;
3751
+ let reported = false;
3752
+ let continuedDollarPos = -1;
3753
+ let continuedParenPos = -1;
3754
+ while (this.pos < len && depth > 0) {
3755
+ const ch = src.charCodeAt(this.pos);
3756
+ if (ch === CH_LPAREN) {
3757
+ const prev = this.pos > start ? src.charCodeAt(this.pos - 1) : 0;
3758
+ const commandDollarPos = prev === CH_DOLLAR ? this.pos - 1 : this.pos === continuedParenPos ? continuedDollarPos : -1;
3759
+ const wordParen = commandDollarPos !== -1 ||
3760
+ prev === CH_LT ||
3761
+ prev === CH_GT ||
3762
+ prev === CH_EQ ||
3763
+ prev === CH_AT ||
3764
+ prev === CH_QUESTION ||
3765
+ prev === CH_STAR ||
3766
+ prev === CH_PLUS ||
3767
+ prev === CH_BANG ||
3768
+ (prev === CH_LPAREN && wordStartAfterParen[wordStartAfterParen.length - 1] === false) ||
3769
+ (arithExtent && this.pos === start);
3770
+ wordStartAfterParen.push(!wordParen);
3771
+ wordStart = true;
3772
+ // (( opens arithmetic — suppress heredoc detection until the parens
3773
+ // rebalance so shift operators inside aren't mistaken for heredocs
3774
+ if (arithBase < 0 && this.pos + 1 < len && src.charCodeAt(this.pos + 1) === CH_LPAREN) {
3775
+ arithBase = depth;
3776
+ }
3777
+ // Nested $( — count it against the shared budget (the substitution being
3778
+ // scanned is level one, hence >=) so over-deep chains surface a parse error.
3779
+ if (commandDollarPos !== -1 && ++substitutions + this._nestingDepth >= MAX_SYNTAX_NESTING) {
3780
+ if (!reported) {
3781
+ this.errors.push({ message: "maximum command substitution nesting depth exceeded", pos: commandDollarPos });
3782
+ reported = true;
3783
+ }
3784
+ }
3785
+ continuedDollarPos = -1;
3786
+ continuedParenPos = -1;
3787
+ depth++;
3788
+ if (caseDepth > 0)
3789
+ caseParens++;
3790
+ this.pos++;
3791
+ }
3792
+ else if (ch === CH_RPAREN) {
3793
+ if (caseDepth > 0 && caseParens === 0) {
3794
+ this.pos++;
3795
+ wordStart = true;
3796
+ }
3797
+ else {
3798
+ if (caseDepth > 0)
3799
+ caseParens--;
3800
+ depth--;
3801
+ if (depth === 0) {
3802
+ const result = bt ? src.slice(start, this.pos) : "";
3803
+ this.pos++;
3804
+ return result;
3805
+ }
3806
+ if (depth <= arithBase)
3807
+ arithBase = -1;
3808
+ wordStart = wordStartAfterParen.pop() ?? true;
3809
+ this.pos++;
3810
+ }
3811
+ }
3812
+ else if (ch === CH_BACKSLASH) {
3813
+ if (this.pos > start && src.charCodeAt(this.pos - 1) === CH_DOLLAR) {
3814
+ const logicalPos = skipLineContinuations(src, this.pos, len);
3815
+ if (logicalPos > this.pos &&
3816
+ src.charCodeAt(logicalPos) === CH_LPAREN &&
3817
+ src.charCodeAt(logicalPos + 1) !== CH_LPAREN) {
3818
+ continuedDollarPos = this.pos - 1;
3819
+ continuedParenPos = logicalPos;
3820
+ }
3821
+ }
3822
+ this.pos++;
3823
+ if (this.pos < len) {
3824
+ if (src.charCodeAt(this.pos) !== CH_NL) {
3825
+ wordStart = false;
3826
+ }
3827
+ this.pos++;
3828
+ }
3829
+ }
3830
+ else if (ch === CH_SQUOTE) {
3831
+ this.pos++;
3832
+ this.skipSQ();
3833
+ wordStart = false;
3834
+ }
3835
+ else if (ch === CH_DQUOTE) {
3836
+ this.pos++;
3837
+ this.skipDQ();
3838
+ wordStart = false;
3839
+ }
3840
+ else if (ch === CH_BACKTICK) {
3841
+ this.pos++;
3842
+ while (this.pos < len && src.charCodeAt(this.pos) !== CH_BACKTICK) {
3843
+ if (src.charCodeAt(this.pos) === CH_BACKSLASH)
3844
+ this.pos++;
3845
+ if (this.pos < len)
3846
+ this.pos++;
3847
+ }
3848
+ if (this.pos < len)
3849
+ this.pos++;
3850
+ wordStart = false;
3851
+ }
3852
+ else if (ch === CH_LT && arithBase < 0 && this.pos + 1 < len && src.charCodeAt(this.pos + 1) === CH_LT) {
3853
+ // << is a heredoc operator even in case-pattern position — unquoted <<
3854
+ // inside a pattern is a bash syntax error, so no caseDepth gate here
3855
+ if (this.pos + 2 < len && src.charCodeAt(this.pos + 2) === CH_LT) {
3856
+ this.pos += 3; // <<< herestring
3857
+ }
3858
+ else {
3859
+ this.pos += 2;
3860
+ const strip = this.pos < len && src.charCodeAt(this.pos) === CH_DASH;
3861
+ if (strip)
3862
+ this.pos++;
3863
+ this.skipSpacesAndTabs();
3864
+ this.readHereDocDelimiter();
3865
+ // Empty unquoted delimiter means there was no delimiter word (`<< )`)
3866
+ if (this._hereDelim || this._hereQuoted) {
3867
+ (pendingDelims ?? (pendingDelims = [])).push({
3868
+ delimiter: this._hereDelim,
3869
+ strip,
3870
+ quoted: this._hereQuoted,
3871
+ });
3872
+ }
3873
+ }
3874
+ wordStart = false;
3875
+ }
3876
+ else if (ch === CH_NL && pendingDelims) {
3877
+ this.pos++;
3878
+ for (const hd of pendingDelims)
3879
+ this.skipHereDocBody(hd.delimiter, hd.strip, true, hd.quoted);
3880
+ pendingDelims = null;
3881
+ wordStart = true;
3882
+ }
3883
+ else if (ch === CH_HASH && arithBase < 0 && !arithExtent && wordStart) {
3884
+ // Word-boundary # opens a comment — opaque up to (not including) the
3885
+ // newline, so quotes and << inside it stay inert
3886
+ while (this.pos < len && src.charCodeAt(this.pos) !== CH_NL)
3887
+ this.pos++;
3888
+ }
3889
+ else {
3890
+ const wStart = this.pos;
3891
+ while (this.pos < len) {
3892
+ const wc = src.charCodeAt(this.pos);
3893
+ if (wc < 128 && charType[wc])
3894
+ break;
3895
+ this.pos++;
3896
+ }
3897
+ if (this.pos > wStart) {
3898
+ const wLen = this.pos - wStart;
3899
+ if (wLen === 4 && wordStart) {
3900
+ const c0 = src.charCodeAt(wStart);
3901
+ if (c0 === 99 &&
3902
+ src.charCodeAt(wStart + 1) === 97 &&
3903
+ src.charCodeAt(wStart + 2) === 115 &&
3904
+ src.charCodeAt(wStart + 3) === 101) {
3905
+ caseDepth++;
3906
+ }
3907
+ else if (c0 === 101 &&
3908
+ src.charCodeAt(wStart + 1) === 115 &&
3909
+ src.charCodeAt(wStart + 2) === 97 &&
3910
+ src.charCodeAt(wStart + 3) === 99 &&
3911
+ caseDepth > 0) {
3912
+ caseDepth--;
3913
+ if (caseDepth === 0)
3914
+ caseParens = 0;
3915
+ }
3916
+ }
3917
+ wordStart = false;
3918
+ }
3919
+ else {
3920
+ const wc = src.charCodeAt(this.pos);
3921
+ wordStart = wc < 128 && (charType[wc] & 1) !== 0;
3922
+ this.pos++;
3923
+ }
3924
+ }
3925
+ }
3926
+ // Ran out of input before the closing paren.
3927
+ this._unbalanced = true;
3928
+ return bt ? src.slice(start, this.pos) : "";
3929
+ }
3930
+ }