dotdo 0.0.1 → 0.0.2

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 (727) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +446 -315
  3. package/dist/ai/index.js +19 -0
  4. package/dist/ai/index.js.map +1 -0
  5. package/dist/ai/template-literals.js +852 -0
  6. package/dist/ai/template-literals.js.map +1 -0
  7. package/dist/api/analytics/router.js +601 -0
  8. package/dist/api/analytics/router.js.map +1 -0
  9. package/dist/api/index.js +158 -0
  10. package/dist/api/index.js.map +1 -0
  11. package/dist/api/middleware/auth-federation.js +573 -0
  12. package/dist/api/middleware/auth-federation.js.map +1 -0
  13. package/dist/api/middleware/auth.js +544 -0
  14. package/dist/api/middleware/auth.js.map +1 -0
  15. package/dist/api/middleware/error-handling.js +176 -0
  16. package/dist/api/middleware/error-handling.js.map +1 -0
  17. package/dist/api/middleware/request-id.js +21 -0
  18. package/dist/api/middleware/request-id.js.map +1 -0
  19. package/dist/api/pages.js +1180 -0
  20. package/dist/api/pages.js.map +1 -0
  21. package/dist/api/routes/api.js +612 -0
  22. package/dist/api/routes/api.js.map +1 -0
  23. package/dist/api/routes/browsers.js +471 -0
  24. package/dist/api/routes/browsers.js.map +1 -0
  25. package/dist/api/routes/do.js +188 -0
  26. package/dist/api/routes/do.js.map +1 -0
  27. package/dist/api/routes/mcp.js +459 -0
  28. package/dist/api/routes/mcp.js.map +1 -0
  29. package/dist/api/routes/obs.js +445 -0
  30. package/dist/api/routes/obs.js.map +1 -0
  31. package/dist/api/routes/openapi.js +794 -0
  32. package/dist/api/routes/openapi.js.map +1 -0
  33. package/dist/api/routes/rpc.js +1103 -0
  34. package/dist/api/routes/rpc.js.map +1 -0
  35. package/dist/api/routes/sandboxes.js +389 -0
  36. package/dist/api/routes/sandboxes.js.map +1 -0
  37. package/dist/api/test-do.js +38 -0
  38. package/dist/api/test-do.js.map +1 -0
  39. package/dist/api/types.js +11 -0
  40. package/dist/api/types.js.map +1 -0
  41. package/dist/cli/bin.js +2 -0
  42. package/dist/cli/main.js +52342 -0
  43. package/dist/db/actions.js +212 -0
  44. package/dist/db/actions.js.map +1 -0
  45. package/dist/db/auth.js +506 -0
  46. package/dist/db/auth.js.map +1 -0
  47. package/dist/db/branches.js +65 -0
  48. package/dist/db/branches.js.map +1 -0
  49. package/dist/db/clickhouse.js +1074 -0
  50. package/dist/db/clickhouse.js.map +1 -0
  51. package/dist/db/dlq.js +39 -0
  52. package/dist/db/dlq.js.map +1 -0
  53. package/dist/db/events.js +28 -0
  54. package/dist/db/events.js.map +1 -0
  55. package/dist/db/exec.js +64 -0
  56. package/dist/db/exec.js.map +1 -0
  57. package/dist/db/files.js +85 -0
  58. package/dist/db/files.js.map +1 -0
  59. package/dist/db/flags.js +24 -0
  60. package/dist/db/flags.js.map +1 -0
  61. package/dist/db/git.js +116 -0
  62. package/dist/db/git.js.map +1 -0
  63. package/dist/db/iceberg/inverted-index.js +862 -0
  64. package/dist/db/iceberg/inverted-index.js.map +1 -0
  65. package/dist/db/iceberg/puffin.js +878 -0
  66. package/dist/db/iceberg/puffin.js.map +1 -0
  67. package/dist/db/iceberg/search-manifest.js +422 -0
  68. package/dist/db/iceberg/search-manifest.js.map +1 -0
  69. package/dist/db/iceberg/types.js +8 -0
  70. package/dist/db/iceberg/types.js.map +1 -0
  71. package/dist/db/index.js +121 -0
  72. package/dist/db/index.js.map +1 -0
  73. package/dist/db/integrations.js +368 -0
  74. package/dist/db/integrations.js.map +1 -0
  75. package/dist/db/json-indexes.js +332 -0
  76. package/dist/db/json-indexes.js.map +1 -0
  77. package/dist/db/linked-accounts.js +287 -0
  78. package/dist/db/linked-accounts.js.map +1 -0
  79. package/dist/db/nouns.js +183 -0
  80. package/dist/db/nouns.js.map +1 -0
  81. package/dist/db/objects.js +170 -0
  82. package/dist/db/objects.js.map +1 -0
  83. package/dist/db/primitives/dag-scheduler/index.js +869 -0
  84. package/dist/db/primitives/dag-scheduler/index.js.map +1 -0
  85. package/dist/db/primitives/exactly-once-context.js +237 -0
  86. package/dist/db/primitives/exactly-once-context.js.map +1 -0
  87. package/dist/db/primitives/index.js +62 -0
  88. package/dist/db/primitives/index.js.map +1 -0
  89. package/dist/db/primitives/keyed-router.js +145 -0
  90. package/dist/db/primitives/keyed-router.js.map +1 -0
  91. package/dist/db/primitives/observability.js +162 -0
  92. package/dist/db/primitives/observability.js.map +1 -0
  93. package/dist/db/primitives/schema-evolution.js +643 -0
  94. package/dist/db/primitives/schema-evolution.js.map +1 -0
  95. package/dist/db/primitives/stateful-operator/index.js +770 -0
  96. package/dist/db/primitives/stateful-operator/index.js.map +1 -0
  97. package/dist/db/primitives/temporal-store.js +306 -0
  98. package/dist/db/primitives/temporal-store.js.map +1 -0
  99. package/dist/db/primitives/typed-column-store.js +1229 -0
  100. package/dist/db/primitives/typed-column-store.js.map +1 -0
  101. package/dist/db/primitives/utils/duration.js +162 -0
  102. package/dist/db/primitives/utils/duration.js.map +1 -0
  103. package/dist/db/primitives/utils/murmur3.js +118 -0
  104. package/dist/db/primitives/utils/murmur3.js.map +1 -0
  105. package/dist/db/primitives/watermark-service.js +136 -0
  106. package/dist/db/primitives/watermark-service.js.map +1 -0
  107. package/dist/db/primitives/window-manager.js +764 -0
  108. package/dist/db/primitives/window-manager.js.map +1 -0
  109. package/dist/db/relationships.js +66 -0
  110. package/dist/db/relationships.js.map +1 -0
  111. package/dist/db/schema-minimal.js +61 -0
  112. package/dist/db/schema-minimal.js.map +1 -0
  113. package/dist/db/search.js +28 -0
  114. package/dist/db/search.js.map +1 -0
  115. package/dist/db/stores.js +1665 -0
  116. package/dist/db/stores.js.map +1 -0
  117. package/dist/db/things.js +297 -0
  118. package/dist/db/things.js.map +1 -0
  119. package/dist/db/vault.js +171 -0
  120. package/dist/db/vault.js.map +1 -0
  121. package/dist/db/verbs.js +102 -0
  122. package/dist/db/verbs.js.map +1 -0
  123. package/dist/do/base.js +48 -0
  124. package/dist/do/base.js.map +1 -0
  125. package/dist/do/bash.js +35 -0
  126. package/dist/do/bash.js.map +1 -0
  127. package/dist/do/fs.js +25 -0
  128. package/dist/do/fs.js.map +1 -0
  129. package/dist/do/full.js +61 -0
  130. package/dist/do/full.js.map +1 -0
  131. package/dist/do/git.js +28 -0
  132. package/dist/do/git.js.map +1 -0
  133. package/dist/do/index.js +52 -0
  134. package/dist/do/index.js.map +1 -0
  135. package/dist/do/tiny.js +31 -0
  136. package/dist/do/tiny.js.map +1 -0
  137. package/dist/lib/DOAuth.js +261 -0
  138. package/dist/lib/DOAuth.js.map +1 -0
  139. package/dist/lib/DODispatcher.js +72 -0
  140. package/dist/lib/DODispatcher.js.map +1 -0
  141. package/dist/lib/Modifier.js +189 -0
  142. package/dist/lib/Modifier.js.map +1 -0
  143. package/dist/lib/StateStorage.js +403 -0
  144. package/dist/lib/StateStorage.js.map +1 -0
  145. package/dist/lib/TypeRegistry.js +122 -0
  146. package/dist/lib/TypeRegistry.js.map +1 -0
  147. package/dist/lib/agent/tools/bash.js +336 -0
  148. package/dist/lib/agent/tools/bash.js.map +1 -0
  149. package/dist/lib/agent/tools/edit.js +157 -0
  150. package/dist/lib/agent/tools/edit.js.map +1 -0
  151. package/dist/lib/agent/tools/glob.js +137 -0
  152. package/dist/lib/agent/tools/glob.js.map +1 -0
  153. package/dist/lib/agent/tools/grep.js +315 -0
  154. package/dist/lib/agent/tools/grep.js.map +1 -0
  155. package/dist/lib/agent/tools/index.js +71 -0
  156. package/dist/lib/agent/tools/index.js.map +1 -0
  157. package/dist/lib/agent/tools/read.js +212 -0
  158. package/dist/lib/agent/tools/read.js.map +1 -0
  159. package/dist/lib/agent/tools/types.js +197 -0
  160. package/dist/lib/agent/tools/types.js.map +1 -0
  161. package/dist/lib/agent/tools/write.js +159 -0
  162. package/dist/lib/agent/tools/write.js.map +1 -0
  163. package/dist/lib/ai/gateway.js +247 -0
  164. package/dist/lib/ai/gateway.js.map +1 -0
  165. package/dist/lib/ai/tool-loop-agent.js +591 -0
  166. package/dist/lib/ai/tool-loop-agent.js.map +1 -0
  167. package/dist/lib/auto-wiring.js +439 -0
  168. package/dist/lib/auto-wiring.js.map +1 -0
  169. package/dist/lib/browse/browserbase.js +163 -0
  170. package/dist/lib/browse/browserbase.js.map +1 -0
  171. package/dist/lib/browse/cloudflare.js +144 -0
  172. package/dist/lib/browse/cloudflare.js.map +1 -0
  173. package/dist/lib/browse/index.js +62 -0
  174. package/dist/lib/browse/index.js.map +1 -0
  175. package/dist/lib/browse/types.js +13 -0
  176. package/dist/lib/browse/types.js.map +1 -0
  177. package/dist/lib/cache/index.js +37 -0
  178. package/dist/lib/cache/index.js.map +1 -0
  179. package/dist/lib/cache/visibility.js +638 -0
  180. package/dist/lib/cache/visibility.js.map +1 -0
  181. package/dist/lib/capabilities.js +268 -0
  182. package/dist/lib/capabilities.js.map +1 -0
  183. package/dist/lib/channels/base.js +106 -0
  184. package/dist/lib/channels/base.js.map +1 -0
  185. package/dist/lib/channels/discord.js +94 -0
  186. package/dist/lib/channels/discord.js.map +1 -0
  187. package/dist/lib/channels/email.js +204 -0
  188. package/dist/lib/channels/email.js.map +1 -0
  189. package/dist/lib/channels/index.js +90 -0
  190. package/dist/lib/channels/index.js.map +1 -0
  191. package/dist/lib/channels/mdxui-chat.js +95 -0
  192. package/dist/lib/channels/mdxui-chat.js.map +1 -0
  193. package/dist/lib/channels/slack-blockkit.js +121 -0
  194. package/dist/lib/channels/slack-blockkit.js.map +1 -0
  195. package/dist/lib/channels/types.js +7 -0
  196. package/dist/lib/channels/types.js.map +1 -0
  197. package/dist/lib/cloudflare/ai.js +654 -0
  198. package/dist/lib/cloudflare/ai.js.map +1 -0
  199. package/dist/lib/cloudflare/index.js +88 -0
  200. package/dist/lib/cloudflare/index.js.map +1 -0
  201. package/dist/lib/cloudflare/kv.js +342 -0
  202. package/dist/lib/cloudflare/kv.js.map +1 -0
  203. package/dist/lib/cloudflare/queues.js +434 -0
  204. package/dist/lib/cloudflare/queues.js.map +1 -0
  205. package/dist/lib/cloudflare/r2.js +604 -0
  206. package/dist/lib/cloudflare/r2.js.map +1 -0
  207. package/dist/lib/cloudflare/vectorize.js +494 -0
  208. package/dist/lib/cloudflare/vectorize.js.map +1 -0
  209. package/dist/lib/cloudflare/workflows.js +569 -0
  210. package/dist/lib/cloudflare/workflows.js.map +1 -0
  211. package/dist/lib/colo/caching.js +196 -0
  212. package/dist/lib/colo/caching.js.map +1 -0
  213. package/dist/lib/colo/detection.js +194 -0
  214. package/dist/lib/colo/detection.js.map +1 -0
  215. package/dist/lib/colo/external-data.js +219 -0
  216. package/dist/lib/colo/external-data.js.map +1 -0
  217. package/dist/lib/colo/globe-data.js +179 -0
  218. package/dist/lib/colo/globe-data.js.map +1 -0
  219. package/dist/lib/colo/index.js +16 -0
  220. package/dist/lib/colo/index.js.map +1 -0
  221. package/dist/lib/decorators.js +37 -0
  222. package/dist/lib/decorators.js.map +1 -0
  223. package/dist/lib/discovery.js +81 -0
  224. package/dist/lib/discovery.js.map +1 -0
  225. package/dist/lib/executors/AgenticFunctionExecutor.js +619 -0
  226. package/dist/lib/executors/AgenticFunctionExecutor.js.map +1 -0
  227. package/dist/lib/executors/BaseFunctionExecutor.js +328 -0
  228. package/dist/lib/executors/BaseFunctionExecutor.js.map +1 -0
  229. package/dist/lib/executors/CascadeExecutor.js +418 -0
  230. package/dist/lib/executors/CascadeExecutor.js.map +1 -0
  231. package/dist/lib/executors/CodeFunctionExecutor.js +904 -0
  232. package/dist/lib/executors/CodeFunctionExecutor.js.map +1 -0
  233. package/dist/lib/executors/GenerativeFunctionExecutor.js +904 -0
  234. package/dist/lib/executors/GenerativeFunctionExecutor.js.map +1 -0
  235. package/dist/lib/executors/HumanFunctionExecutor.js +884 -0
  236. package/dist/lib/executors/HumanFunctionExecutor.js.map +1 -0
  237. package/dist/lib/executors/ParallelStepExecutor.js +308 -0
  238. package/dist/lib/executors/ParallelStepExecutor.js.map +1 -0
  239. package/dist/lib/executors/types.js +12 -0
  240. package/dist/lib/executors/types.js.map +1 -0
  241. package/dist/lib/experiments.js +89 -0
  242. package/dist/lib/experiments.js.map +1 -0
  243. package/dist/lib/flags/store.js +262 -0
  244. package/dist/lib/flags/store.js.map +1 -0
  245. package/dist/lib/functions/FunctionComposition.js +467 -0
  246. package/dist/lib/functions/FunctionComposition.js.map +1 -0
  247. package/dist/lib/functions/FunctionMiddleware.js +457 -0
  248. package/dist/lib/functions/FunctionMiddleware.js.map +1 -0
  249. package/dist/lib/functions/FunctionRegistry.js +426 -0
  250. package/dist/lib/functions/FunctionRegistry.js.map +1 -0
  251. package/dist/lib/functions/createFunction.js +1048 -0
  252. package/dist/lib/functions/createFunction.js.map +1 -0
  253. package/dist/lib/humans/index.js +68 -0
  254. package/dist/lib/humans/index.js.map +1 -0
  255. package/dist/lib/humans/templates.js +117 -0
  256. package/dist/lib/humans/templates.js.map +1 -0
  257. package/dist/lib/identity.js +98 -0
  258. package/dist/lib/identity.js.map +1 -0
  259. package/dist/lib/index.js +9 -0
  260. package/dist/lib/index.js.map +1 -0
  261. package/dist/lib/logging/error-logger.js +163 -0
  262. package/dist/lib/logging/error-logger.js.map +1 -0
  263. package/dist/lib/logging/index.js +160 -0
  264. package/dist/lib/logging/index.js.map +1 -0
  265. package/dist/lib/mixins/bash.js +825 -0
  266. package/dist/lib/mixins/bash.js.map +1 -0
  267. package/dist/lib/mixins/fs.js +648 -0
  268. package/dist/lib/mixins/fs.js.map +1 -0
  269. package/dist/lib/mixins/git.js +1011 -0
  270. package/dist/lib/mixins/git.js.map +1 -0
  271. package/dist/lib/mixins/index.js +29 -0
  272. package/dist/lib/mixins/index.js.map +1 -0
  273. package/dist/lib/mixins/npm.js +662 -0
  274. package/dist/lib/mixins/npm.js.map +1 -0
  275. package/dist/lib/noun-id.js +278 -0
  276. package/dist/lib/noun-id.js.map +1 -0
  277. package/dist/lib/rate-limit/sliding-window.js +148 -0
  278. package/dist/lib/rate-limit/sliding-window.js.map +1 -0
  279. package/dist/lib/rate-limit.js +110 -0
  280. package/dist/lib/rate-limit.js.map +1 -0
  281. package/dist/lib/rpc/bindings.js +548 -0
  282. package/dist/lib/rpc/bindings.js.map +1 -0
  283. package/dist/lib/rpc/index.js +64 -0
  284. package/dist/lib/rpc/index.js.map +1 -0
  285. package/dist/lib/safe-stringify.js +223 -0
  286. package/dist/lib/safe-stringify.js.map +1 -0
  287. package/dist/lib/sandbox/miniflare-sandbox.js +1007 -0
  288. package/dist/lib/sandbox/miniflare-sandbox.js.map +1 -0
  289. package/dist/lib/sqids.js +110 -0
  290. package/dist/lib/sqids.js.map +1 -0
  291. package/dist/lib/sql/adapters/index.js +10 -0
  292. package/dist/lib/sql/adapters/index.js.map +1 -0
  293. package/dist/lib/sql/adapters/node-sql-parser.js +552 -0
  294. package/dist/lib/sql/adapters/node-sql-parser.js.map +1 -0
  295. package/dist/lib/sql/adapters/pgsql-parser.js +1189 -0
  296. package/dist/lib/sql/adapters/pgsql-parser.js.map +1 -0
  297. package/dist/lib/sql/index.js +277 -0
  298. package/dist/lib/sql/index.js.map +1 -0
  299. package/dist/lib/sql/types.js +56 -0
  300. package/dist/lib/sql/types.js.map +1 -0
  301. package/dist/lib/type-classifier.js +126 -0
  302. package/dist/lib/type-classifier.js.map +1 -0
  303. package/dist/lib/utils/html.js +47 -0
  304. package/dist/lib/utils/html.js.map +1 -0
  305. package/dist/lib/validation.js +48 -0
  306. package/dist/lib/validation.js.map +1 -0
  307. package/dist/lib/vault/store.js +411 -0
  308. package/dist/lib/vault/store.js.map +1 -0
  309. package/dist/metrics/hunch.js +739 -0
  310. package/dist/metrics/hunch.js.map +1 -0
  311. package/dist/objects/API.js +302 -0
  312. package/dist/objects/API.js.map +1 -0
  313. package/dist/objects/Agent.js +179 -0
  314. package/dist/objects/Agent.js.map +1 -0
  315. package/dist/objects/AgenticFunctionExecutor.js +8 -0
  316. package/dist/objects/AgenticFunctionExecutor.js.map +1 -0
  317. package/dist/objects/App.js +83 -0
  318. package/dist/objects/App.js.map +1 -0
  319. package/dist/objects/Browser.js +884 -0
  320. package/dist/objects/Browser.js.map +1 -0
  321. package/dist/objects/Business.js +107 -0
  322. package/dist/objects/Business.js.map +1 -0
  323. package/dist/objects/CLI.js +221 -0
  324. package/dist/objects/CLI.js.map +1 -0
  325. package/dist/objects/CodeFunctionExecutor.js +8 -0
  326. package/dist/objects/CodeFunctionExecutor.js.map +1 -0
  327. package/dist/objects/Collection.js +161 -0
  328. package/dist/objects/Collection.js.map +1 -0
  329. package/dist/objects/DO.js +41 -0
  330. package/dist/objects/DO.js.map +1 -0
  331. package/dist/objects/DOBase.js +2309 -0
  332. package/dist/objects/DOBase.js.map +1 -0
  333. package/dist/objects/DOFull.js +1676 -0
  334. package/dist/objects/DOFull.js.map +1 -0
  335. package/dist/objects/DOTiny.js +207 -0
  336. package/dist/objects/DOTiny.js.map +1 -0
  337. package/dist/objects/Directory.js +199 -0
  338. package/dist/objects/Directory.js.map +1 -0
  339. package/dist/objects/Entity.js +413 -0
  340. package/dist/objects/Entity.js.map +1 -0
  341. package/dist/objects/Function.js +116 -0
  342. package/dist/objects/Function.js.map +1 -0
  343. package/dist/objects/Human.js +231 -0
  344. package/dist/objects/Human.js.map +1 -0
  345. package/dist/objects/HumanFunctionExecutor.js +8 -0
  346. package/dist/objects/HumanFunctionExecutor.js.map +1 -0
  347. package/dist/objects/IcebergMetadataDO.js +938 -0
  348. package/dist/objects/IcebergMetadataDO.js.map +1 -0
  349. package/dist/objects/IntegrationsDO.js +1174 -0
  350. package/dist/objects/IntegrationsDO.js.map +1 -0
  351. package/dist/objects/ObservabilityBroadcaster.js +149 -0
  352. package/dist/objects/ObservabilityBroadcaster.js.map +1 -0
  353. package/dist/objects/Package.js +154 -0
  354. package/dist/objects/Package.js.map +1 -0
  355. package/dist/objects/Product.js +193 -0
  356. package/dist/objects/Product.js.map +1 -0
  357. package/dist/objects/SDK.js +152 -0
  358. package/dist/objects/SDK.js.map +1 -0
  359. package/dist/objects/SaaS.js +235 -0
  360. package/dist/objects/SaaS.js.map +1 -0
  361. package/dist/objects/SandboxDO.js +759 -0
  362. package/dist/objects/SandboxDO.js.map +1 -0
  363. package/dist/objects/Service.js +337 -0
  364. package/dist/objects/Service.js.map +1 -0
  365. package/dist/objects/Site.js +80 -0
  366. package/dist/objects/Site.js.map +1 -0
  367. package/dist/objects/Startup.js +479 -0
  368. package/dist/objects/Startup.js.map +1 -0
  369. package/dist/objects/ThingsDO.js +170 -0
  370. package/dist/objects/ThingsDO.js.map +1 -0
  371. package/dist/objects/VectorShardDO.js +648 -0
  372. package/dist/objects/VectorShardDO.js.map +1 -0
  373. package/dist/objects/Worker.js +144 -0
  374. package/dist/objects/Worker.js.map +1 -0
  375. package/dist/objects/Workflow.js +196 -0
  376. package/dist/objects/Workflow.js.map +1 -0
  377. package/dist/objects/WorkflowFactory.js +313 -0
  378. package/dist/objects/WorkflowFactory.js.map +1 -0
  379. package/dist/objects/WorkflowRuntime.js +863 -0
  380. package/dist/objects/WorkflowRuntime.js.map +1 -0
  381. package/dist/objects/circuit-breaker-bulkhead.js +178 -0
  382. package/dist/objects/circuit-breaker-bulkhead.js.map +1 -0
  383. package/dist/objects/createFunction.js +934 -0
  384. package/dist/objects/createFunction.js.map +1 -0
  385. package/dist/objects/index.js +80 -0
  386. package/dist/objects/index.js.map +1 -0
  387. package/dist/objects/lifecycle/Branch.js +275 -0
  388. package/dist/objects/lifecycle/Branch.js.map +1 -0
  389. package/dist/objects/lifecycle/Clone.js +1499 -0
  390. package/dist/objects/lifecycle/Clone.js.map +1 -0
  391. package/dist/objects/lifecycle/Compact.js +237 -0
  392. package/dist/objects/lifecycle/Compact.js.map +1 -0
  393. package/dist/objects/lifecycle/Promote.js +476 -0
  394. package/dist/objects/lifecycle/Promote.js.map +1 -0
  395. package/dist/objects/lifecycle/Shard.js +560 -0
  396. package/dist/objects/lifecycle/Shard.js.map +1 -0
  397. package/dist/objects/lifecycle/index.js +15 -0
  398. package/dist/objects/lifecycle/index.js.map +1 -0
  399. package/dist/objects/lifecycle/types.js +33 -0
  400. package/dist/objects/lifecycle/types.js.map +1 -0
  401. package/dist/objects/mixins/infrastructure.js +171 -0
  402. package/dist/objects/mixins/infrastructure.js.map +1 -0
  403. package/dist/objects/modules/StoresModule.js +153 -0
  404. package/dist/objects/modules/StoresModule.js.map +1 -0
  405. package/dist/objects/persistence/checkpoint-manager.js +606 -0
  406. package/dist/objects/persistence/checkpoint-manager.js.map +1 -0
  407. package/dist/objects/persistence/index.js +72 -0
  408. package/dist/objects/persistence/index.js.map +1 -0
  409. package/dist/objects/persistence/migration-runner.js +562 -0
  410. package/dist/objects/persistence/migration-runner.js.map +1 -0
  411. package/dist/objects/persistence/replication-manager.js +501 -0
  412. package/dist/objects/persistence/replication-manager.js.map +1 -0
  413. package/dist/objects/persistence/tiered-storage-manager.js +595 -0
  414. package/dist/objects/persistence/tiered-storage-manager.js.map +1 -0
  415. package/dist/objects/persistence/types.js +14 -0
  416. package/dist/objects/persistence/types.js.map +1 -0
  417. package/dist/objects/persistence/wal-manager.js +653 -0
  418. package/dist/objects/persistence/wal-manager.js.map +1 -0
  419. package/dist/objects/presets/index.js +20 -0
  420. package/dist/objects/presets/index.js.map +1 -0
  421. package/dist/objects/presets/primitives.js +188 -0
  422. package/dist/objects/presets/primitives.js.map +1 -0
  423. package/dist/objects/primitives/alarm-adapter.js +141 -0
  424. package/dist/objects/primitives/alarm-adapter.js.map +1 -0
  425. package/dist/objects/primitives/index.js +337 -0
  426. package/dist/objects/primitives/index.js.map +1 -0
  427. package/dist/objects/primitives/storage-adapter.js +182 -0
  428. package/dist/objects/primitives/storage-adapter.js.map +1 -0
  429. package/dist/objects/primitives/with-primitives.js +102 -0
  430. package/dist/objects/primitives/with-primitives.js.map +1 -0
  431. package/dist/objects/services/StoreManager.js +227 -0
  432. package/dist/objects/services/StoreManager.js.map +1 -0
  433. package/dist/objects/services/index.js +13 -0
  434. package/dist/objects/services/index.js.map +1 -0
  435. package/dist/objects/transport/auth-layer.js +1451 -0
  436. package/dist/objects/transport/auth-layer.js.map +1 -0
  437. package/dist/objects/transport/capnweb-target.js +355 -0
  438. package/dist/objects/transport/capnweb-target.js.map +1 -0
  439. package/dist/objects/transport/chain.js +441 -0
  440. package/dist/objects/transport/chain.js.map +1 -0
  441. package/dist/objects/transport/handler.js +58 -0
  442. package/dist/objects/transport/handler.js.map +1 -0
  443. package/dist/objects/transport/index.js +53 -0
  444. package/dist/objects/transport/index.js.map +1 -0
  445. package/dist/objects/transport/mcp-server.js +690 -0
  446. package/dist/objects/transport/mcp-server.js.map +1 -0
  447. package/dist/objects/transport/rest-autowire.js +1507 -0
  448. package/dist/objects/transport/rest-autowire.js.map +1 -0
  449. package/dist/objects/transport/rest-router.js +440 -0
  450. package/dist/objects/transport/rest-router.js.map +1 -0
  451. package/dist/objects/transport/rpc-server.js +1536 -0
  452. package/dist/objects/transport/rpc-server.js.map +1 -0
  453. package/dist/objects/transport/shared.js +575 -0
  454. package/dist/objects/transport/shared.js.map +1 -0
  455. package/dist/objects/transport/sync-engine.js +291 -0
  456. package/dist/objects/transport/sync-engine.js.map +1 -0
  457. package/dist/objects/transport/types.js +8 -0
  458. package/dist/objects/transport/types.js.map +1 -0
  459. package/dist/primitives/bashx/src/ast/analyze.js +1472 -0
  460. package/dist/primitives/bashx/src/ast/analyze.js.map +1 -0
  461. package/dist/primitives/bashx/src/ast/parser.js +1488 -0
  462. package/dist/primitives/bashx/src/ast/parser.js.map +1 -0
  463. package/dist/primitives/bashx/src/do/commands/crypto.js +1954 -0
  464. package/dist/primitives/bashx/src/do/commands/crypto.js.map +1 -0
  465. package/dist/primitives/bashx/src/do/commands/data-processing.js +1812 -0
  466. package/dist/primitives/bashx/src/do/commands/data-processing.js.map +1 -0
  467. package/dist/primitives/bashx/src/do/commands/extended-utils.js +804 -0
  468. package/dist/primitives/bashx/src/do/commands/extended-utils.js.map +1 -0
  469. package/dist/primitives/bashx/src/do/commands/math-control.js +1122 -0
  470. package/dist/primitives/bashx/src/do/commands/math-control.js.map +1 -0
  471. package/dist/primitives/bashx/src/do/commands/posix-utils.js +1015 -0
  472. package/dist/primitives/bashx/src/do/commands/posix-utils.js.map +1 -0
  473. package/dist/primitives/bashx/src/do/commands/system-utils.js +687 -0
  474. package/dist/primitives/bashx/src/do/commands/system-utils.js.map +1 -0
  475. package/dist/primitives/bashx/src/do/commands/test-command.js +523 -0
  476. package/dist/primitives/bashx/src/do/commands/test-command.js.map +1 -0
  477. package/dist/primitives/bashx/src/do/commands/text-processing.js +1550 -0
  478. package/dist/primitives/bashx/src/do/commands/text-processing.js.map +1 -0
  479. package/dist/primitives/bashx/src/do/container-executor.js +429 -0
  480. package/dist/primitives/bashx/src/do/container-executor.js.map +1 -0
  481. package/dist/primitives/bashx/src/do/index.js +668 -0
  482. package/dist/primitives/bashx/src/do/index.js.map +1 -0
  483. package/dist/primitives/bashx/src/do/tiered-executor.js +2647 -0
  484. package/dist/primitives/bashx/src/do/tiered-executor.js.map +1 -0
  485. package/dist/primitives/bashx/src/do/worker.js +352 -0
  486. package/dist/primitives/bashx/src/do/worker.js.map +1 -0
  487. package/dist/primitives/bashx/src/types.js +10 -0
  488. package/dist/primitives/bashx/src/types.js.map +1 -0
  489. package/dist/primitives/fsx/core/backend.js +480 -0
  490. package/dist/primitives/fsx/core/backend.js.map +1 -0
  491. package/dist/primitives/fsx/core/constants.js +140 -0
  492. package/dist/primitives/fsx/core/constants.js.map +1 -0
  493. package/dist/primitives/fsx/core/fsx.js +1184 -0
  494. package/dist/primitives/fsx/core/fsx.js.map +1 -0
  495. package/dist/primitives/fsx/core/glob/glob.js +438 -0
  496. package/dist/primitives/fsx/core/glob/glob.js.map +1 -0
  497. package/dist/primitives/fsx/core/glob/index.js +8 -0
  498. package/dist/primitives/fsx/core/glob/index.js.map +1 -0
  499. package/dist/primitives/fsx/core/glob/match.js +392 -0
  500. package/dist/primitives/fsx/core/glob/match.js.map +1 -0
  501. package/dist/primitives/fsx/core/types.js +307 -0
  502. package/dist/primitives/fsx/core/types.js.map +1 -0
  503. package/dist/sandbox/index.js +258 -0
  504. package/dist/sandbox/index.js.map +1 -0
  505. package/dist/sdk/capnweb-compat.js +42 -0
  506. package/dist/sdk/capnweb-compat.js.map +1 -0
  507. package/dist/sdk/client.js +20 -0
  508. package/dist/sdk/client.js.map +1 -0
  509. package/dist/sdk/index.js +17 -0
  510. package/dist/sdk/index.js.map +1 -0
  511. package/dist/snippets/artifacts-config.js +241 -0
  512. package/dist/snippets/artifacts-config.js.map +1 -0
  513. package/dist/snippets/artifacts-ingest.js +832 -0
  514. package/dist/snippets/artifacts-ingest.js.map +1 -0
  515. package/dist/snippets/artifacts-serve.js +1035 -0
  516. package/dist/snippets/artifacts-serve.js.map +1 -0
  517. package/dist/snippets/artifacts-types.js +161 -0
  518. package/dist/snippets/artifacts-types.js.map +1 -0
  519. package/dist/snippets/cache-probe.js +376 -0
  520. package/dist/snippets/cache-probe.js.map +1 -0
  521. package/dist/snippets/cache.js +10 -0
  522. package/dist/snippets/cache.js.map +1 -0
  523. package/dist/snippets/events.js +469 -0
  524. package/dist/snippets/events.js.map +1 -0
  525. package/dist/snippets/index.js +7 -0
  526. package/dist/snippets/index.js.map +1 -0
  527. package/dist/snippets/proxy.js +495 -0
  528. package/dist/snippets/proxy.js.map +1 -0
  529. package/dist/snippets/search.js +1759 -0
  530. package/dist/snippets/search.js.map +1 -0
  531. package/dist/streams/index.js +30 -0
  532. package/dist/streams/index.js.map +1 -0
  533. package/dist/streams/observability.js +68 -0
  534. package/dist/streams/observability.js.map +1 -0
  535. package/dist/types/AI.js +92 -0
  536. package/dist/types/AI.js.map +1 -0
  537. package/dist/types/AIFunction.js +171 -0
  538. package/dist/types/AIFunction.js.map +1 -0
  539. package/dist/types/BrowseVerb.js +89 -0
  540. package/dist/types/BrowseVerb.js.map +1 -0
  541. package/dist/types/Browser.js +31 -0
  542. package/dist/types/Browser.js.map +1 -0
  543. package/dist/types/Chaos.js +15 -0
  544. package/dist/types/Chaos.js.map +1 -0
  545. package/dist/types/CloudflareBindings.js +109 -0
  546. package/dist/types/CloudflareBindings.js.map +1 -0
  547. package/dist/types/Collection.js +50 -0
  548. package/dist/types/Collection.js.map +1 -0
  549. package/dist/types/DO.js +2 -0
  550. package/dist/types/DO.js.map +1 -0
  551. package/dist/types/DOLocation.js +63 -0
  552. package/dist/types/DOLocation.js.map +1 -0
  553. package/dist/types/EventHandler.js +57 -0
  554. package/dist/types/EventHandler.js.map +1 -0
  555. package/dist/types/Experiment.js +33 -0
  556. package/dist/types/Experiment.js.map +1 -0
  557. package/dist/types/Flag.js +57 -0
  558. package/dist/types/Flag.js.map +1 -0
  559. package/dist/types/Lifecycle.js +13 -0
  560. package/dist/types/Lifecycle.js.map +1 -0
  561. package/dist/types/Location.js +169 -0
  562. package/dist/types/Location.js.map +1 -0
  563. package/dist/types/Noun.js +66 -0
  564. package/dist/types/Noun.js.map +1 -0
  565. package/dist/types/SessionEvent.js +194 -0
  566. package/dist/types/SessionEvent.js.map +1 -0
  567. package/dist/types/Thing.js +55 -0
  568. package/dist/types/Thing.js.map +1 -0
  569. package/dist/types/ThingDO.js +153 -0
  570. package/dist/types/ThingDO.js.map +1 -0
  571. package/dist/types/Things.js +2 -0
  572. package/dist/types/Things.js.map +1 -0
  573. package/dist/types/Verb.js +119 -0
  574. package/dist/types/Verb.js.map +1 -0
  575. package/dist/types/WorkflowContext.js +70 -0
  576. package/dist/types/WorkflowContext.js.map +1 -0
  577. package/dist/types/analytics-api.js +13 -0
  578. package/dist/types/analytics-api.js.map +1 -0
  579. package/dist/types/capabilities.js +135 -0
  580. package/dist/types/capabilities.js.map +1 -0
  581. package/dist/types/drizzle.js +12 -0
  582. package/dist/types/drizzle.js.map +1 -0
  583. package/dist/types/event.js +201 -0
  584. package/dist/types/event.js.map +1 -0
  585. package/dist/types/fn.js +12 -0
  586. package/dist/types/fn.js.map +1 -0
  587. package/dist/types/iceberg.js +48 -0
  588. package/dist/types/iceberg.js.map +1 -0
  589. package/dist/types/ids.js +170 -0
  590. package/dist/types/ids.js.map +1 -0
  591. package/dist/types/index.js +41 -0
  592. package/dist/types/index.js.map +1 -0
  593. package/dist/types/introspect.js +54 -0
  594. package/dist/types/introspect.js.map +1 -0
  595. package/dist/types/observability.js +124 -0
  596. package/dist/types/observability.js.map +1 -0
  597. package/dist/types/sync-protocol.js +175 -0
  598. package/dist/types/sync-protocol.js.map +1 -0
  599. package/dist/types/vector.js +13 -0
  600. package/dist/types/vector.js.map +1 -0
  601. package/dist/workflows/ScheduleManager.js +473 -0
  602. package/dist/workflows/ScheduleManager.js.map +1 -0
  603. package/dist/workflows/StepDOBridge.js +149 -0
  604. package/dist/workflows/StepDOBridge.js.map +1 -0
  605. package/dist/workflows/StepResultStorage.js +232 -0
  606. package/dist/workflows/StepResultStorage.js.map +1 -0
  607. package/dist/workflows/WaitForEventManager.js +461 -0
  608. package/dist/workflows/WaitForEventManager.js.map +1 -0
  609. package/dist/workflows/analyzer.js +332 -0
  610. package/dist/workflows/analyzer.js.map +1 -0
  611. package/dist/workflows/compat/activity-router.js +484 -0
  612. package/dist/workflows/compat/activity-router.js.map +1 -0
  613. package/dist/workflows/compat/backends/cloudflare-workflows.js +431 -0
  614. package/dist/workflows/compat/backends/cloudflare-workflows.js.map +1 -0
  615. package/dist/workflows/compat/backends/index.js +14 -0
  616. package/dist/workflows/compat/backends/index.js.map +1 -0
  617. package/dist/workflows/compat/errors/index.js +375 -0
  618. package/dist/workflows/compat/errors/index.js.map +1 -0
  619. package/dist/workflows/compat/index.js +79 -0
  620. package/dist/workflows/compat/index.js.map +1 -0
  621. package/dist/workflows/compat/inngest/index.js +989 -0
  622. package/dist/workflows/compat/inngest/index.js.map +1 -0
  623. package/dist/workflows/compat/qstash/index.js +1263 -0
  624. package/dist/workflows/compat/qstash/index.js.map +1 -0
  625. package/dist/workflows/compat/temporal/activities.js +739 -0
  626. package/dist/workflows/compat/temporal/activities.js.map +1 -0
  627. package/dist/workflows/compat/temporal/child-workflows.js +154 -0
  628. package/dist/workflows/compat/temporal/child-workflows.js.map +1 -0
  629. package/dist/workflows/compat/temporal/client.js +381 -0
  630. package/dist/workflows/compat/temporal/client.js.map +1 -0
  631. package/dist/workflows/compat/temporal/context.js +309 -0
  632. package/dist/workflows/compat/temporal/context.js.map +1 -0
  633. package/dist/workflows/compat/temporal/determinism.js +216 -0
  634. package/dist/workflows/compat/temporal/determinism.js.map +1 -0
  635. package/dist/workflows/compat/temporal/errors.js +128 -0
  636. package/dist/workflows/compat/temporal/errors.js.map +1 -0
  637. package/dist/workflows/compat/temporal/index.js +2464 -0
  638. package/dist/workflows/compat/temporal/index.js.map +1 -0
  639. package/dist/workflows/compat/temporal/saga.js +504 -0
  640. package/dist/workflows/compat/temporal/saga.js.map +1 -0
  641. package/dist/workflows/compat/temporal/signals.js +364 -0
  642. package/dist/workflows/compat/temporal/signals.js.map +1 -0
  643. package/dist/workflows/compat/temporal/storage.js +271 -0
  644. package/dist/workflows/compat/temporal/storage.js.map +1 -0
  645. package/dist/workflows/compat/temporal/timers.js +347 -0
  646. package/dist/workflows/compat/temporal/timers.js.map +1 -0
  647. package/dist/workflows/compat/temporal/types.js +7 -0
  648. package/dist/workflows/compat/temporal/types.js.map +1 -0
  649. package/dist/workflows/compat/temporal/unified-primitives.js +339 -0
  650. package/dist/workflows/compat/temporal/unified-primitives.js.map +1 -0
  651. package/dist/workflows/compat/trigger/index.js +468 -0
  652. package/dist/workflows/compat/trigger/index.js.map +1 -0
  653. package/dist/workflows/compat/utils/index.js +69 -0
  654. package/dist/workflows/compat/utils/index.js.map +1 -0
  655. package/dist/workflows/context/correlation-capability.js +266 -0
  656. package/dist/workflows/context/correlation-capability.js.map +1 -0
  657. package/dist/workflows/context/correlation.js +484 -0
  658. package/dist/workflows/context/correlation.js.map +1 -0
  659. package/dist/workflows/context/experiment.js +289 -0
  660. package/dist/workflows/context/experiment.js.map +1 -0
  661. package/dist/workflows/context/flag.js +244 -0
  662. package/dist/workflows/context/flag.js.map +1 -0
  663. package/dist/workflows/context/foundation.js +648 -0
  664. package/dist/workflows/context/foundation.js.map +1 -0
  665. package/dist/workflows/context/human-base.js +106 -0
  666. package/dist/workflows/context/human-base.js.map +1 -0
  667. package/dist/workflows/context/human.js +368 -0
  668. package/dist/workflows/context/human.js.map +1 -0
  669. package/dist/workflows/context/measure.js +354 -0
  670. package/dist/workflows/context/measure.js.map +1 -0
  671. package/dist/workflows/context/rate-limit.js +358 -0
  672. package/dist/workflows/context/rate-limit.js.map +1 -0
  673. package/dist/workflows/context/user.js +117 -0
  674. package/dist/workflows/context/user.js.map +1 -0
  675. package/dist/workflows/context/vault.js +360 -0
  676. package/dist/workflows/context/vault.js.map +1 -0
  677. package/dist/workflows/data/entity-events/entity-events.js +489 -0
  678. package/dist/workflows/data/entity-events/entity-events.js.map +1 -0
  679. package/dist/workflows/data/experiment/index.js +599 -0
  680. package/dist/workflows/data/experiment/index.js.map +1 -0
  681. package/dist/workflows/data/goal/context.js +558 -0
  682. package/dist/workflows/data/goal/context.js.map +1 -0
  683. package/dist/workflows/data/goal/index.js +32 -0
  684. package/dist/workflows/data/goal/index.js.map +1 -0
  685. package/dist/workflows/data/measure/index.js +840 -0
  686. package/dist/workflows/data/measure/index.js.map +1 -0
  687. package/dist/workflows/data/stream/index.js +1215 -0
  688. package/dist/workflows/data/stream/index.js.map +1 -0
  689. package/dist/workflows/data/track/context.js +883 -0
  690. package/dist/workflows/data/track/context.js.map +1 -0
  691. package/dist/workflows/data/track/index.js +15 -0
  692. package/dist/workflows/data/track/index.js.map +1 -0
  693. package/dist/workflows/data/view/context.js +864 -0
  694. package/dist/workflows/data/view/context.js.map +1 -0
  695. package/dist/workflows/domain.js +93 -0
  696. package/dist/workflows/domain.js.map +1 -0
  697. package/dist/workflows/flag.js +176 -0
  698. package/dist/workflows/flag.js.map +1 -0
  699. package/dist/workflows/flags.js +217 -0
  700. package/dist/workflows/flags.js.map +1 -0
  701. package/dist/workflows/hash.js +209 -0
  702. package/dist/workflows/hash.js.map +1 -0
  703. package/dist/workflows/index.js +50 -0
  704. package/dist/workflows/index.js.map +1 -0
  705. package/dist/workflows/on.js +378 -0
  706. package/dist/workflows/on.js.map +1 -0
  707. package/dist/workflows/pipeline-promise.js +481 -0
  708. package/dist/workflows/pipeline-promise.js.map +1 -0
  709. package/dist/workflows/pipeline-types.js +20 -0
  710. package/dist/workflows/pipeline-types.js.map +1 -0
  711. package/dist/workflows/proxy.js +76 -0
  712. package/dist/workflows/proxy.js.map +1 -0
  713. package/dist/workflows/runtime.js +310 -0
  714. package/dist/workflows/runtime.js.map +1 -0
  715. package/dist/workflows/schedule-builder.js +327 -0
  716. package/dist/workflows/schedule-builder.js.map +1 -0
  717. package/dist/workflows/visibility/index.js +148 -0
  718. package/dist/workflows/visibility/index.js.map +1 -0
  719. package/dist/workflows/visibility/query-parser.js +150 -0
  720. package/dist/workflows/visibility/query-parser.js.map +1 -0
  721. package/dist/workflows/visibility/store.js +223 -0
  722. package/dist/workflows/visibility/store.js.map +1 -0
  723. package/dist/workflows/visibility/types.js +30 -0
  724. package/dist/workflows/visibility/types.js.map +1 -0
  725. package/dist/workflows/workflow.js +53 -0
  726. package/dist/workflows/workflow.js.map +1 -0
  727. package/package.json +279 -46
@@ -0,0 +1,1488 @@
1
+ /**
2
+ * Bash AST Parser
3
+ *
4
+ * A lightweight parser for bash commands that detects common syntax errors.
5
+ * Implements error detection for:
6
+ * - Unclosed quotes (single and double)
7
+ * - Missing terminators (fi, done, esac)
8
+ * - Unbalanced brackets/braces
9
+ * - Invalid pipe/redirect syntax
10
+ */
11
+ // ============================================================================
12
+ // Lexer
13
+ // ============================================================================
14
+ class Lexer {
15
+ input;
16
+ pos = 0;
17
+ line = 1;
18
+ column = 1;
19
+ errors = [];
20
+ constructor(input) {
21
+ this.input = input;
22
+ }
23
+ peek(offset = 0) {
24
+ return this.input[this.pos + offset] ?? '';
25
+ }
26
+ advance() {
27
+ const ch = this.input[this.pos] ?? '';
28
+ this.pos++;
29
+ if (ch === '\n') {
30
+ this.line++;
31
+ this.column = 1;
32
+ }
33
+ else {
34
+ this.column++;
35
+ }
36
+ return ch;
37
+ }
38
+ skipWhitespace() {
39
+ while (this.pos < this.input.length) {
40
+ const ch = this.peek();
41
+ if (ch === ' ' || ch === '\t') {
42
+ this.advance();
43
+ }
44
+ else if (ch === '\\' && this.peek(1) === '\n') {
45
+ // Line continuation
46
+ this.advance();
47
+ this.advance();
48
+ }
49
+ else {
50
+ break;
51
+ }
52
+ }
53
+ }
54
+ isWordChar(ch) {
55
+ // Not a special character
56
+ return ch !== '' &&
57
+ ch !== ' ' && ch !== '\t' && ch !== '\n' &&
58
+ ch !== '|' && ch !== '&' && ch !== ';' &&
59
+ ch !== '(' && ch !== ')' && ch !== '{' && ch !== '}' &&
60
+ ch !== '<' && ch !== '>' && ch !== '#';
61
+ }
62
+ readQuotedString(quote) {
63
+ const startLine = this.line;
64
+ const startColumn = this.column;
65
+ let result = quote;
66
+ this.advance(); // consume opening quote
67
+ while (this.pos < this.input.length) {
68
+ const ch = this.peek();
69
+ if (ch === quote) {
70
+ result += this.advance();
71
+ return result;
72
+ }
73
+ if (ch === '\\' && quote === '"') {
74
+ result += this.advance();
75
+ if (this.pos < this.input.length) {
76
+ result += this.advance();
77
+ }
78
+ }
79
+ else if (ch === '$' && quote === '"') {
80
+ // Handle parameter expansion and command/arithmetic substitution inside double quotes
81
+ result += this.readParameterExpansion();
82
+ }
83
+ else {
84
+ result += this.advance();
85
+ }
86
+ }
87
+ // Unclosed quote
88
+ const quoteType = quote === '"' ? 'double' : 'single';
89
+ this.errors.push({
90
+ message: `Unclosed ${quoteType} quote`,
91
+ line: startLine,
92
+ column: startColumn,
93
+ suggestion: `Add closing ${quote} at the end`,
94
+ });
95
+ return result;
96
+ }
97
+ /**
98
+ * Read a quoted string inside a parameter expansion - doesn't report errors itself
99
+ */
100
+ readQuotedStringInExpansion(quote) {
101
+ let result = quote;
102
+ this.advance(); // consume opening quote
103
+ while (this.pos < this.input.length) {
104
+ const ch = this.peek();
105
+ if (ch === quote) {
106
+ result += this.advance();
107
+ return result;
108
+ }
109
+ if (ch === '\\' && quote === '"') {
110
+ result += this.advance();
111
+ if (this.pos < this.input.length) {
112
+ result += this.advance();
113
+ }
114
+ }
115
+ else {
116
+ result += this.advance();
117
+ }
118
+ }
119
+ // Return without the closing quote - parent will detect
120
+ return result;
121
+ }
122
+ readParameterExpansion() {
123
+ const startLine = this.line;
124
+ const startColumn = this.column;
125
+ let result = '$';
126
+ this.advance(); // consume $
127
+ const next = this.peek();
128
+ if (next === '(') {
129
+ // Command substitution $(...) or arithmetic $((...))
130
+ result += this.advance(); // consume (
131
+ if (this.peek() === '(') {
132
+ // Arithmetic expansion $((
133
+ result += this.advance(); // consume second (
134
+ let depth = 2;
135
+ let foundClosing = false;
136
+ while (this.pos < this.input.length) {
137
+ const ch = this.peek();
138
+ if (ch === '(') {
139
+ depth++;
140
+ result += this.advance();
141
+ }
142
+ else if (ch === ')') {
143
+ depth--;
144
+ result += this.advance();
145
+ if (depth === 0) {
146
+ foundClosing = true;
147
+ break;
148
+ }
149
+ }
150
+ else {
151
+ result += this.advance();
152
+ }
153
+ }
154
+ if (!foundClosing) {
155
+ this.errors.push({
156
+ message: 'Unclosed arithmetic expansion $((',
157
+ line: startLine,
158
+ column: startColumn,
159
+ suggestion: 'Add closing )) at the end',
160
+ });
161
+ }
162
+ }
163
+ else {
164
+ // Command substitution $(
165
+ let depth = 1;
166
+ let foundClosing = false;
167
+ while (this.pos < this.input.length) {
168
+ const ch = this.peek();
169
+ if (ch === '"' || ch === "'") {
170
+ result += this.readQuotedString(ch);
171
+ }
172
+ else if (ch === '(') {
173
+ depth++;
174
+ result += this.advance();
175
+ }
176
+ else if (ch === ')') {
177
+ depth--;
178
+ result += this.advance();
179
+ if (depth === 0) {
180
+ foundClosing = true;
181
+ break;
182
+ }
183
+ }
184
+ else {
185
+ result += this.advance();
186
+ }
187
+ }
188
+ if (!foundClosing) {
189
+ this.errors.push({
190
+ message: 'Unclosed command substitution $(',
191
+ line: startLine,
192
+ column: startColumn,
193
+ suggestion: 'Add closing ) at the end',
194
+ });
195
+ }
196
+ }
197
+ }
198
+ else if (next === '{') {
199
+ // Parameter expansion ${...}
200
+ result += this.advance(); // consume {
201
+ let depth = 1;
202
+ let foundClosing = false;
203
+ let hasUnclosedQuote = false;
204
+ while (this.pos < this.input.length) {
205
+ const ch = this.peek();
206
+ if (ch === '"' || ch === "'") {
207
+ const quotedStr = this.readQuotedStringInExpansion(ch);
208
+ result += quotedStr;
209
+ // Check if quote was unclosed (doesn't end with the same quote char it started with)
210
+ if (!quotedStr.endsWith(ch)) {
211
+ hasUnclosedQuote = true;
212
+ }
213
+ }
214
+ else if (ch === '{') {
215
+ depth++;
216
+ result += this.advance();
217
+ }
218
+ else if (ch === '}') {
219
+ depth--;
220
+ result += this.advance();
221
+ if (depth === 0) {
222
+ foundClosing = true;
223
+ break;
224
+ }
225
+ }
226
+ else {
227
+ result += this.advance();
228
+ }
229
+ }
230
+ if (!foundClosing) {
231
+ if (hasUnclosedQuote) {
232
+ this.errors.push({
233
+ message: 'Unclosed quote in parameter expansion',
234
+ line: startLine,
235
+ column: startColumn,
236
+ suggestion: 'Add closing quote and } at the end',
237
+ });
238
+ }
239
+ else {
240
+ this.errors.push({
241
+ message: 'Unclosed brace in parameter expansion ${',
242
+ line: startLine,
243
+ column: startColumn,
244
+ suggestion: 'Add closing } at the end',
245
+ });
246
+ }
247
+ }
248
+ }
249
+ else {
250
+ // Simple variable $VAR
251
+ while (this.pos < this.input.length) {
252
+ const ch = this.peek();
253
+ if (/[a-zA-Z0-9_]/.test(ch)) {
254
+ result += this.advance();
255
+ }
256
+ else {
257
+ break;
258
+ }
259
+ }
260
+ }
261
+ return result;
262
+ }
263
+ readWord() {
264
+ let result = '';
265
+ while (this.pos < this.input.length) {
266
+ const ch = this.peek();
267
+ if (ch === '"' || ch === "'") {
268
+ result += this.readQuotedString(ch);
269
+ }
270
+ else if (ch === '$') {
271
+ result += this.readParameterExpansion();
272
+ }
273
+ else if (ch === '\\' && this.peek(1) !== '') {
274
+ // Escape sequence
275
+ result += this.advance();
276
+ result += this.advance();
277
+ }
278
+ else if (ch === '`') {
279
+ // Backtick command substitution
280
+ const startLine = this.line;
281
+ const startColumn = this.column;
282
+ result += this.advance();
283
+ let foundClosing = false;
284
+ while (this.pos < this.input.length) {
285
+ const c = this.peek();
286
+ if (c === '`') {
287
+ result += this.advance();
288
+ foundClosing = true;
289
+ break;
290
+ }
291
+ else if (c === '\\') {
292
+ result += this.advance();
293
+ if (this.pos < this.input.length) {
294
+ result += this.advance();
295
+ }
296
+ }
297
+ else {
298
+ result += this.advance();
299
+ }
300
+ }
301
+ if (!foundClosing) {
302
+ this.errors.push({
303
+ message: 'Unclosed backtick command substitution',
304
+ line: startLine,
305
+ column: startColumn,
306
+ suggestion: 'Add closing ` at the end',
307
+ });
308
+ }
309
+ }
310
+ else if (this.isWordChar(ch)) {
311
+ result += this.advance();
312
+ }
313
+ else {
314
+ break;
315
+ }
316
+ }
317
+ return result;
318
+ }
319
+ nextToken() {
320
+ this.skipWhitespace();
321
+ if (this.pos >= this.input.length) {
322
+ return { type: 'EOF', value: '', line: this.line, column: this.column };
323
+ }
324
+ const startLine = this.line;
325
+ const startColumn = this.column;
326
+ const ch = this.peek();
327
+ // Comments
328
+ if (ch === '#') {
329
+ while (this.pos < this.input.length && this.peek() !== '\n') {
330
+ this.advance();
331
+ }
332
+ return this.nextToken();
333
+ }
334
+ // Newline
335
+ if (ch === '\n') {
336
+ this.advance();
337
+ return { type: 'NEWLINE', value: '\n', line: startLine, column: startColumn };
338
+ }
339
+ // Semicolons and case pattern end
340
+ if (ch === ';') {
341
+ this.advance();
342
+ if (this.peek() === ';') {
343
+ this.advance();
344
+ return { type: 'CASE_PATTERN_END', value: ';;', line: startLine, column: startColumn };
345
+ }
346
+ return { type: 'SEMICOLON', value: ';', line: startLine, column: startColumn };
347
+ }
348
+ // Pipes and logical operators
349
+ if (ch === '|') {
350
+ this.advance();
351
+ if (this.peek() === '|') {
352
+ this.advance();
353
+ return { type: 'OR', value: '||', line: startLine, column: startColumn };
354
+ }
355
+ return { type: 'PIPE', value: '|', line: startLine, column: startColumn };
356
+ }
357
+ // And/background/combined redirect
358
+ if (ch === '&') {
359
+ this.advance();
360
+ if (this.peek() === '&') {
361
+ this.advance();
362
+ return { type: 'AND', value: '&&', line: startLine, column: startColumn };
363
+ }
364
+ if (this.peek() === '>') {
365
+ this.advance();
366
+ // &> redirects both stdout and stderr
367
+ return { type: 'REDIRECT_BOTH', value: '&>', line: startLine, column: startColumn };
368
+ }
369
+ return { type: 'BACKGROUND', value: '&', line: startLine, column: startColumn };
370
+ }
371
+ // Parentheses
372
+ if (ch === '(') {
373
+ this.advance();
374
+ return { type: 'LPAREN', value: '(', line: startLine, column: startColumn };
375
+ }
376
+ if (ch === ')') {
377
+ this.advance();
378
+ return { type: 'RPAREN', value: ')', line: startLine, column: startColumn };
379
+ }
380
+ // Braces
381
+ if (ch === '{') {
382
+ this.advance();
383
+ return { type: 'LBRACE', value: '{', line: startLine, column: startColumn };
384
+ }
385
+ if (ch === '}') {
386
+ this.advance();
387
+ return { type: 'RBRACE', value: '}', line: startLine, column: startColumn };
388
+ }
389
+ // Brackets
390
+ if (ch === '[') {
391
+ this.advance();
392
+ if (this.peek() === '[') {
393
+ this.advance();
394
+ return { type: 'DOUBLE_LBRACKET', value: '[[', line: startLine, column: startColumn };
395
+ }
396
+ return { type: 'LBRACKET', value: '[', line: startLine, column: startColumn };
397
+ }
398
+ if (ch === ']') {
399
+ this.advance();
400
+ if (this.peek() === ']') {
401
+ this.advance();
402
+ return { type: 'DOUBLE_RBRACKET', value: ']]', line: startLine, column: startColumn };
403
+ }
404
+ return { type: 'RBRACKET', value: ']', line: startLine, column: startColumn };
405
+ }
406
+ // Redirects
407
+ if (ch === '>') {
408
+ this.advance();
409
+ if (this.peek() === '>') {
410
+ this.advance();
411
+ return { type: 'REDIRECT_APPEND', value: '>>', line: startLine, column: startColumn };
412
+ }
413
+ if (this.peek() === '&') {
414
+ this.advance();
415
+ return { type: 'REDIRECT_OUT', value: '>&', line: startLine, column: startColumn };
416
+ }
417
+ return { type: 'REDIRECT_OUT', value: '>', line: startLine, column: startColumn };
418
+ }
419
+ if (ch === '<') {
420
+ this.advance();
421
+ if (this.peek() === '<') {
422
+ this.advance();
423
+ if (this.peek() === '<') {
424
+ this.advance();
425
+ return { type: 'REDIRECT_HERESTRING', value: '<<<', line: startLine, column: startColumn };
426
+ }
427
+ return { type: 'REDIRECT_HEREDOC', value: '<<', line: startLine, column: startColumn };
428
+ }
429
+ if (this.peek() === '&') {
430
+ this.advance();
431
+ return { type: 'REDIRECT_IN', value: '<&', line: startLine, column: startColumn };
432
+ }
433
+ return { type: 'REDIRECT_IN', value: '<', line: startLine, column: startColumn };
434
+ }
435
+ // Check for digit followed by redirect (e.g., 2>&1, 2>file, 1>out)
436
+ if (/[0-9]/.test(ch)) {
437
+ const nextCh = this.peek(1);
438
+ if (nextCh === '>' || nextCh === '<') {
439
+ const fd = parseInt(this.advance(), 10);
440
+ const redirectCh = this.advance();
441
+ if (redirectCh === '>') {
442
+ if (this.peek() === '>') {
443
+ this.advance();
444
+ return { type: 'REDIRECT_APPEND', value: '>>', line: startLine, column: startColumn, fd };
445
+ }
446
+ if (this.peek() === '&') {
447
+ this.advance();
448
+ return { type: 'REDIRECT_OUT', value: '>&', line: startLine, column: startColumn, fd };
449
+ }
450
+ return { type: 'REDIRECT_OUT', value: '>', line: startLine, column: startColumn, fd };
451
+ }
452
+ else {
453
+ // <
454
+ if (this.peek() === '<') {
455
+ this.advance();
456
+ if (this.peek() === '<') {
457
+ this.advance();
458
+ return { type: 'REDIRECT_HERESTRING', value: '<<<', line: startLine, column: startColumn, fd };
459
+ }
460
+ return { type: 'REDIRECT_HEREDOC', value: '<<', line: startLine, column: startColumn, fd };
461
+ }
462
+ if (this.peek() === '&') {
463
+ this.advance();
464
+ return { type: 'REDIRECT_IN', value: '<&', line: startLine, column: startColumn, fd };
465
+ }
466
+ return { type: 'REDIRECT_IN', value: '<', line: startLine, column: startColumn, fd };
467
+ }
468
+ }
469
+ }
470
+ // Words (including keywords)
471
+ const word = this.readWord();
472
+ if (word === '') {
473
+ // Unknown character, try to recover
474
+ const badChar = this.advance();
475
+ this.errors.push({
476
+ message: `Unexpected character '${badChar}'`,
477
+ line: startLine,
478
+ column: startColumn,
479
+ });
480
+ return { type: 'ERROR', value: badChar, line: startLine, column: startColumn };
481
+ }
482
+ // Check for keywords
483
+ const keywords = {
484
+ if: 'IF',
485
+ then: 'THEN',
486
+ else: 'ELSE',
487
+ elif: 'ELIF',
488
+ fi: 'FI',
489
+ for: 'FOR',
490
+ while: 'WHILE',
491
+ until: 'UNTIL',
492
+ do: 'DO',
493
+ done: 'DONE',
494
+ case: 'CASE',
495
+ esac: 'ESAC',
496
+ in: 'IN',
497
+ };
498
+ const keywordType = keywords[word];
499
+ if (keywordType) {
500
+ return { type: keywordType, value: word, line: startLine, column: startColumn };
501
+ }
502
+ // Check for assignment (VAR=value)
503
+ const assignMatch = word.match(/^([a-zA-Z_][a-zA-Z0-9_]*)(=|\+=)(.*)$/);
504
+ if (assignMatch) {
505
+ return { type: 'ASSIGNMENT', value: word, line: startLine, column: startColumn };
506
+ }
507
+ return { type: 'WORD', value: word, line: startLine, column: startColumn };
508
+ }
509
+ tokenize() {
510
+ const tokens = [];
511
+ let token;
512
+ while ((token = this.nextToken()).type !== 'EOF') {
513
+ if (token.type !== 'ERROR') {
514
+ tokens.push(token);
515
+ }
516
+ }
517
+ tokens.push(token); // Include EOF
518
+ return tokens;
519
+ }
520
+ }
521
+ // ============================================================================
522
+ // Parser
523
+ // ============================================================================
524
+ class Parser {
525
+ tokens;
526
+ pos = 0;
527
+ errors = [];
528
+ constructor(tokens, lexerErrors) {
529
+ this.tokens = tokens;
530
+ this.errors = [...lexerErrors];
531
+ }
532
+ peek(offset = 0) {
533
+ const idx = this.pos + offset;
534
+ if (idx < 0 || idx >= this.tokens.length) {
535
+ return { type: 'EOF', value: '', line: 0, column: 0 };
536
+ }
537
+ return this.tokens[idx];
538
+ }
539
+ advance() {
540
+ const token = this.tokens[this.pos];
541
+ if (this.pos < this.tokens.length) {
542
+ this.pos++;
543
+ }
544
+ return token;
545
+ }
546
+ /** @internal Reserved for future use */
547
+ expect(type, message) {
548
+ const token = this.peek();
549
+ if (token.type !== type) {
550
+ if (message) {
551
+ this.errors.push({
552
+ message,
553
+ line: token.line,
554
+ column: token.column,
555
+ });
556
+ }
557
+ return null;
558
+ }
559
+ return this.advance();
560
+ }
561
+ skipNewlines() {
562
+ while (this.peek().type === 'NEWLINE') {
563
+ this.advance();
564
+ }
565
+ }
566
+ isCommandTerminator(token) {
567
+ return token.type === 'NEWLINE' ||
568
+ token.type === 'SEMICOLON' ||
569
+ token.type === 'PIPE' ||
570
+ token.type === 'AND' ||
571
+ token.type === 'OR' ||
572
+ token.type === 'BACKGROUND' ||
573
+ token.type === 'RPAREN' ||
574
+ token.type === 'RBRACE' ||
575
+ token.type === 'EOF';
576
+ }
577
+ isRedirectToken(token) {
578
+ return token.type === 'REDIRECT_OUT' ||
579
+ token.type === 'REDIRECT_APPEND' ||
580
+ token.type === 'REDIRECT_IN' ||
581
+ token.type === 'REDIRECT_HEREDOC' ||
582
+ token.type === 'REDIRECT_HERESTRING' ||
583
+ token.type === 'REDIRECT_BOTH';
584
+ }
585
+ parseRedirect() {
586
+ const token = this.peek();
587
+ if (!this.isRedirectToken(token)) {
588
+ return null;
589
+ }
590
+ this.advance();
591
+ this.skipWhitespaceTokens();
592
+ const targetToken = this.peek();
593
+ // Check for consecutive redirects (like > >)
594
+ if (this.isRedirectToken(targetToken)) {
595
+ this.errors.push({
596
+ message: `Consecutive redirects: unexpected ${targetToken.value} after ${token.value}`,
597
+ line: targetToken.line,
598
+ column: targetToken.column,
599
+ suggestion: 'Remove one of the redirect operators',
600
+ });
601
+ return null;
602
+ }
603
+ // Accept WORD, ASSIGNMENT, or IN (which can be a filename like 'in')
604
+ if (targetToken.type !== 'WORD' && targetToken.type !== 'ASSIGNMENT' && targetToken.type !== 'IN') {
605
+ this.errors.push({
606
+ message: `Redirect incomplete: missing file after ${token.value}`,
607
+ line: token.line,
608
+ column: token.column,
609
+ suggestion: 'Specify a file name after the redirect operator',
610
+ });
611
+ return null;
612
+ }
613
+ const target = this.advance();
614
+ const opMap = {
615
+ '>': '>',
616
+ '>&': '>&',
617
+ '>>': '>>',
618
+ '<': '<',
619
+ '<&': '<&',
620
+ '<<': '<<',
621
+ '<<<': '<<<',
622
+ '&>': '>&', // &> is equivalent to >& for redirecting both stdout and stderr
623
+ };
624
+ const redirect = {
625
+ type: 'Redirect',
626
+ op: opMap[token.value] ?? '>',
627
+ target: { type: 'Word', value: target.value },
628
+ };
629
+ // Include file descriptor if present
630
+ if (token.fd !== undefined) {
631
+ redirect.fd = token.fd;
632
+ }
633
+ return redirect;
634
+ }
635
+ skipWhitespaceTokens() {
636
+ // No explicit whitespace tokens in our lexer, but keep for consistency
637
+ }
638
+ parseWord() {
639
+ const token = this.peek();
640
+ if (token.type === 'WORD' || token.type === 'ASSIGNMENT') {
641
+ this.advance();
642
+ let quoted = undefined;
643
+ // Check if quoted
644
+ if (token.value.startsWith('"') || token.value.includes('"')) {
645
+ quoted = 'double';
646
+ }
647
+ else if (token.value.startsWith("'") || token.value.includes("'")) {
648
+ quoted = 'single';
649
+ }
650
+ return {
651
+ type: 'Word',
652
+ value: token.value,
653
+ quoted,
654
+ };
655
+ }
656
+ return null;
657
+ }
658
+ parseSimpleCommand() {
659
+ const prefix = [];
660
+ const args = [];
661
+ const redirects = [];
662
+ let name = null;
663
+ // Parse assignments at the start
664
+ while (this.peek().type === 'ASSIGNMENT') {
665
+ const token = this.advance();
666
+ const match = token.value.match(/^([a-zA-Z_][a-zA-Z0-9_]*)(=|\+=)(.*)$/);
667
+ if (match) {
668
+ prefix.push({
669
+ type: 'Assignment',
670
+ name: match[1],
671
+ operator: match[2],
672
+ value: match[3] ? { type: 'Word', value: match[3] } : null,
673
+ });
674
+ }
675
+ }
676
+ // Handle single [ test command
677
+ if (this.peek().type === 'LBRACKET') {
678
+ const startToken = this.advance();
679
+ name = { type: 'Word', value: '[' };
680
+ // Parse test expression arguments until ]
681
+ while (this.peek().type !== 'RBRACKET' && this.peek().type !== 'EOF' &&
682
+ !this.isCommandTerminator(this.peek())) {
683
+ const token = this.peek();
684
+ if (token.type === 'WORD') {
685
+ const word = this.parseWord();
686
+ if (word) {
687
+ args.push(word);
688
+ }
689
+ }
690
+ else {
691
+ break;
692
+ }
693
+ }
694
+ // Expect closing ]
695
+ if (this.peek().type !== 'RBRACKET') {
696
+ this.errors.push({
697
+ message: 'Unclosed [ test bracket: missing ]',
698
+ line: startToken.line,
699
+ column: startToken.column,
700
+ suggestion: 'Add ] to close the test bracket',
701
+ });
702
+ }
703
+ else {
704
+ this.advance(); // consume ]
705
+ args.push({ type: 'Word', value: ']' });
706
+ }
707
+ return { type: 'Command', name, prefix, args, redirects };
708
+ }
709
+ // Parse command name and arguments
710
+ while (true) {
711
+ const token = this.peek();
712
+ if (this.isRedirectToken(token)) {
713
+ // Check for consecutive redirects (like > >)
714
+ const prevToken = this.peek(-1);
715
+ if (prevToken && this.isRedirectToken(prevToken)) {
716
+ this.errors.push({
717
+ message: `Consecutive redirects: unexpected ${token.value} after ${prevToken.value}`,
718
+ line: token.line,
719
+ column: token.column,
720
+ suggestion: 'Remove one of the redirect operators',
721
+ });
722
+ }
723
+ const redirect = this.parseRedirect();
724
+ if (redirect) {
725
+ redirects.push(redirect);
726
+ }
727
+ }
728
+ else if (token.type === 'WORD') {
729
+ const word = this.parseWord();
730
+ if (word) {
731
+ if (!name) {
732
+ name = word;
733
+ }
734
+ else {
735
+ args.push(word);
736
+ }
737
+ }
738
+ }
739
+ else if (token.type === 'IN') {
740
+ // 'in' can appear as a filename (e.g., cmd < in), treat as word in command context
741
+ this.advance();
742
+ const word = { type: 'Word', value: 'in' };
743
+ if (!name) {
744
+ name = word;
745
+ }
746
+ else {
747
+ args.push(word);
748
+ }
749
+ }
750
+ else if (this.isCommandTerminator(token) || token.type === 'THEN' || token.type === 'DO' ||
751
+ token.type === 'ELSE' || token.type === 'ELIF' || token.type === 'FI' ||
752
+ token.type === 'DONE' || token.type === 'ESAC' || token.type === 'CASE_PATTERN_END') {
753
+ break;
754
+ }
755
+ else {
756
+ break;
757
+ }
758
+ }
759
+ if (!name && prefix.length === 0 && redirects.length === 0) {
760
+ return null;
761
+ }
762
+ return {
763
+ type: 'Command',
764
+ name,
765
+ prefix,
766
+ args,
767
+ redirects,
768
+ };
769
+ }
770
+ parseSubshell() {
771
+ if (this.peek().type !== 'LPAREN') {
772
+ return null;
773
+ }
774
+ const startToken = this.advance(); // consume (
775
+ this.skipNewlines();
776
+ const body = [];
777
+ while (this.peek().type !== 'RPAREN' && this.peek().type !== 'EOF') {
778
+ const node = this.parseCompoundList();
779
+ if (node) {
780
+ body.push(node);
781
+ }
782
+ this.skipNewlines();
783
+ if (this.peek().type === 'SEMICOLON') {
784
+ this.advance();
785
+ this.skipNewlines();
786
+ }
787
+ }
788
+ if (this.peek().type !== 'RPAREN') {
789
+ this.errors.push({
790
+ message: 'Unclosed subshell: missing )',
791
+ line: startToken.line,
792
+ column: startToken.column,
793
+ suggestion: 'Add ) to close the subshell',
794
+ });
795
+ return { type: 'Subshell', body };
796
+ }
797
+ this.advance(); // consume )
798
+ return { type: 'Subshell', body };
799
+ }
800
+ parseBraceGroup() {
801
+ if (this.peek().type !== 'LBRACE') {
802
+ return null;
803
+ }
804
+ const startToken = this.advance(); // consume {
805
+ this.skipNewlines();
806
+ const body = [];
807
+ while (this.peek().type !== 'RBRACE' && this.peek().type !== 'EOF') {
808
+ const node = this.parseCompoundList();
809
+ if (node) {
810
+ body.push(node);
811
+ }
812
+ this.skipNewlines();
813
+ if (this.peek().type === 'SEMICOLON') {
814
+ this.advance();
815
+ this.skipNewlines();
816
+ }
817
+ }
818
+ if (this.peek().type !== 'RBRACE') {
819
+ this.errors.push({
820
+ message: 'Unclosed brace group: missing }',
821
+ line: startToken.line,
822
+ column: startToken.column,
823
+ suggestion: 'Add } to close the brace group',
824
+ });
825
+ return { type: 'CompoundCommand', kind: 'brace', body };
826
+ }
827
+ this.advance(); // consume }
828
+ return { type: 'CompoundCommand', kind: 'brace', body };
829
+ }
830
+ parseIfStatement() {
831
+ if (this.peek().type !== 'IF') {
832
+ return null;
833
+ }
834
+ const startToken = this.advance(); // consume 'if'
835
+ this.skipNewlines();
836
+ const body = [];
837
+ // Parse condition
838
+ const condition = this.parseCondition();
839
+ if (condition) {
840
+ body.push(condition);
841
+ }
842
+ this.skipNewlines();
843
+ // Skip semicolon before 'then' (common in one-line if statements)
844
+ if (this.peek().type === 'SEMICOLON') {
845
+ this.advance();
846
+ this.skipNewlines();
847
+ }
848
+ // Expect 'then'
849
+ if (this.peek().type !== 'THEN') {
850
+ this.errors.push({
851
+ message: "Missing 'then' in if statement",
852
+ line: this.peek().line,
853
+ column: this.peek().column,
854
+ suggestion: "Add 'then' after the condition",
855
+ });
856
+ }
857
+ else {
858
+ this.advance();
859
+ }
860
+ this.skipNewlines();
861
+ // Parse 'then' body
862
+ while (this.peek().type !== 'ELSE' && this.peek().type !== 'ELIF' &&
863
+ this.peek().type !== 'FI' && this.peek().type !== 'EOF') {
864
+ const node = this.parseCompoundList();
865
+ if (node) {
866
+ body.push(node);
867
+ }
868
+ else {
869
+ break;
870
+ }
871
+ this.skipNewlines();
872
+ if (this.peek().type === 'SEMICOLON') {
873
+ this.advance();
874
+ this.skipNewlines();
875
+ }
876
+ }
877
+ // Parse 'elif' or 'else'
878
+ while (this.peek().type === 'ELIF' || this.peek().type === 'ELSE') {
879
+ this.advance();
880
+ this.skipNewlines();
881
+ if (this.peek(-1).type === 'ELIF') {
882
+ // Parse elif condition
883
+ const elifCondition = this.parseCondition();
884
+ if (elifCondition) {
885
+ body.push(elifCondition);
886
+ }
887
+ this.skipNewlines();
888
+ // Skip semicolon before 'then'
889
+ if (this.peek().type === 'SEMICOLON') {
890
+ this.advance();
891
+ this.skipNewlines();
892
+ }
893
+ if (this.peek().type === 'THEN') {
894
+ this.advance();
895
+ }
896
+ this.skipNewlines();
897
+ }
898
+ // Parse else/elif body
899
+ while (this.peek().type !== 'ELSE' && this.peek().type !== 'ELIF' &&
900
+ this.peek().type !== 'FI' && this.peek().type !== 'EOF') {
901
+ const node = this.parseCompoundList();
902
+ if (node) {
903
+ body.push(node);
904
+ }
905
+ else {
906
+ break;
907
+ }
908
+ this.skipNewlines();
909
+ if (this.peek().type === 'SEMICOLON') {
910
+ this.advance();
911
+ this.skipNewlines();
912
+ }
913
+ }
914
+ }
915
+ // Expect 'fi'
916
+ if (this.peek().type !== 'FI') {
917
+ this.errors.push({
918
+ message: "Missing 'fi' to close if statement",
919
+ line: startToken.line,
920
+ column: startToken.column,
921
+ suggestion: "Add 'fi' at the end",
922
+ });
923
+ }
924
+ else {
925
+ this.advance();
926
+ }
927
+ return { type: 'CompoundCommand', kind: 'if', body };
928
+ }
929
+ parseForLoop() {
930
+ if (this.peek().type !== 'FOR') {
931
+ return null;
932
+ }
933
+ const startToken = this.advance(); // consume 'for'
934
+ this.skipNewlines();
935
+ const body = [];
936
+ // Check for C-style for loop: for ((...))
937
+ if (this.peek().type === 'LPAREN' && this.peek(1).type === 'LPAREN') {
938
+ this.advance(); // consume first (
939
+ this.advance(); // consume second (
940
+ // Parse the arithmetic expression until ))
941
+ let depth = 2;
942
+ let exprContent = '';
943
+ while (depth > 0 && this.peek().type !== 'EOF') {
944
+ const currentToken = this.peek();
945
+ if (currentToken.type === 'LPAREN') {
946
+ depth++;
947
+ exprContent += '(';
948
+ this.advance();
949
+ }
950
+ else if (currentToken.type === 'RPAREN') {
951
+ depth--;
952
+ if (depth > 0) {
953
+ exprContent += ')';
954
+ }
955
+ this.advance();
956
+ }
957
+ else {
958
+ exprContent += currentToken.value;
959
+ this.advance();
960
+ }
961
+ // Add space between tokens (simplified)
962
+ if (depth > 0) {
963
+ exprContent += ' ';
964
+ }
965
+ }
966
+ // Store the C-style expression
967
+ if (exprContent.trim()) {
968
+ body.push({
969
+ type: 'Command',
970
+ name: { type: 'Word', value: exprContent.trim() },
971
+ prefix: [],
972
+ args: [],
973
+ redirects: []
974
+ });
975
+ }
976
+ this.skipNewlines();
977
+ // Allow semicolon before do
978
+ if (this.peek().type === 'SEMICOLON') {
979
+ this.advance();
980
+ this.skipNewlines();
981
+ }
982
+ // Expect 'do'
983
+ if (this.peek().type !== 'DO') {
984
+ this.errors.push({
985
+ message: "Missing 'do' in for loop",
986
+ line: this.peek().line,
987
+ column: this.peek().column,
988
+ suggestion: "Add 'do' before the loop body",
989
+ });
990
+ }
991
+ else {
992
+ this.advance();
993
+ }
994
+ this.skipNewlines();
995
+ // Parse loop body
996
+ while (this.peek().type !== 'DONE' && this.peek().type !== 'EOF') {
997
+ const node = this.parseCompoundList();
998
+ if (node) {
999
+ body.push(node);
1000
+ }
1001
+ else {
1002
+ break;
1003
+ }
1004
+ this.skipNewlines();
1005
+ if (this.peek().type === 'SEMICOLON') {
1006
+ this.advance();
1007
+ this.skipNewlines();
1008
+ }
1009
+ }
1010
+ // Expect 'done'
1011
+ if (this.peek().type !== 'DONE') {
1012
+ this.errors.push({
1013
+ message: "Missing 'done' to close for loop",
1014
+ line: startToken.line,
1015
+ column: startToken.column,
1016
+ suggestion: "Add 'done' at the end",
1017
+ });
1018
+ }
1019
+ else {
1020
+ this.advance();
1021
+ }
1022
+ return { type: 'CompoundCommand', kind: 'for', body };
1023
+ }
1024
+ // Standard for loop: for var in list
1025
+ // Parse variable name
1026
+ if (this.peek().type === 'WORD') {
1027
+ body.push({ type: 'Command', name: this.parseWord(), prefix: [], args: [], redirects: [] });
1028
+ }
1029
+ this.skipNewlines();
1030
+ // Expect 'in' (optional)
1031
+ if (this.peek().type === 'IN') {
1032
+ this.advance();
1033
+ this.skipNewlines();
1034
+ // Parse word list
1035
+ while (this.peek().type === 'WORD') {
1036
+ body.push({ type: 'Command', name: this.parseWord(), prefix: [], args: [], redirects: [] });
1037
+ }
1038
+ }
1039
+ this.skipNewlines();
1040
+ // Allow semicolon before do
1041
+ if (this.peek().type === 'SEMICOLON') {
1042
+ this.advance();
1043
+ this.skipNewlines();
1044
+ }
1045
+ // Expect 'do'
1046
+ if (this.peek().type !== 'DO') {
1047
+ this.errors.push({
1048
+ message: "Missing 'do' in for loop",
1049
+ line: this.peek().line,
1050
+ column: this.peek().column,
1051
+ suggestion: "Add 'do' before the loop body",
1052
+ });
1053
+ }
1054
+ else {
1055
+ this.advance();
1056
+ }
1057
+ this.skipNewlines();
1058
+ // Parse loop body
1059
+ while (this.peek().type !== 'DONE' && this.peek().type !== 'EOF') {
1060
+ const node = this.parseCompoundList();
1061
+ if (node) {
1062
+ body.push(node);
1063
+ }
1064
+ else {
1065
+ break;
1066
+ }
1067
+ this.skipNewlines();
1068
+ if (this.peek().type === 'SEMICOLON') {
1069
+ this.advance();
1070
+ this.skipNewlines();
1071
+ }
1072
+ }
1073
+ // Expect 'done'
1074
+ if (this.peek().type !== 'DONE') {
1075
+ this.errors.push({
1076
+ message: "Missing 'done' to close for loop",
1077
+ line: startToken.line,
1078
+ column: startToken.column,
1079
+ suggestion: "Add 'done' at the end",
1080
+ });
1081
+ }
1082
+ else {
1083
+ this.advance();
1084
+ }
1085
+ return { type: 'CompoundCommand', kind: 'for', body };
1086
+ }
1087
+ parseWhileLoop() {
1088
+ const tokenType = this.peek().type;
1089
+ if (tokenType !== 'WHILE' && tokenType !== 'UNTIL') {
1090
+ return null;
1091
+ }
1092
+ const startToken = this.advance(); // consume 'while' or 'until'
1093
+ const kind = tokenType === 'WHILE' ? 'while' : 'until';
1094
+ this.skipNewlines();
1095
+ const body = [];
1096
+ // Parse condition
1097
+ const condition = this.parseCondition();
1098
+ if (condition) {
1099
+ body.push(condition);
1100
+ }
1101
+ this.skipNewlines();
1102
+ // Allow semicolon before do
1103
+ if (this.peek().type === 'SEMICOLON') {
1104
+ this.advance();
1105
+ this.skipNewlines();
1106
+ }
1107
+ // Expect 'do'
1108
+ if (this.peek().type !== 'DO') {
1109
+ this.errors.push({
1110
+ message: `Missing 'do' in ${kind} loop`,
1111
+ line: this.peek().line,
1112
+ column: this.peek().column,
1113
+ suggestion: "Add 'do' before the loop body",
1114
+ });
1115
+ }
1116
+ else {
1117
+ this.advance();
1118
+ }
1119
+ this.skipNewlines();
1120
+ // Parse loop body
1121
+ while (this.peek().type !== 'DONE' && this.peek().type !== 'EOF') {
1122
+ const node = this.parseCompoundList();
1123
+ if (node) {
1124
+ body.push(node);
1125
+ }
1126
+ else {
1127
+ break;
1128
+ }
1129
+ this.skipNewlines();
1130
+ if (this.peek().type === 'SEMICOLON') {
1131
+ this.advance();
1132
+ this.skipNewlines();
1133
+ }
1134
+ }
1135
+ // Expect 'done'
1136
+ if (this.peek().type !== 'DONE') {
1137
+ this.errors.push({
1138
+ message: `Missing 'done' to close ${kind} loop`,
1139
+ line: startToken.line,
1140
+ column: startToken.column,
1141
+ suggestion: "Add 'done' at the end",
1142
+ });
1143
+ }
1144
+ else {
1145
+ this.advance();
1146
+ }
1147
+ return { type: 'CompoundCommand', kind, body };
1148
+ }
1149
+ parseCaseStatement() {
1150
+ if (this.peek().type !== 'CASE') {
1151
+ return null;
1152
+ }
1153
+ const startToken = this.advance(); // consume 'case'
1154
+ this.skipNewlines();
1155
+ const body = [];
1156
+ // Parse word
1157
+ if (this.peek().type === 'WORD') {
1158
+ body.push({ type: 'Command', name: this.parseWord(), prefix: [], args: [], redirects: [] });
1159
+ }
1160
+ this.skipNewlines();
1161
+ // Expect 'in'
1162
+ if (this.peek().type === 'IN') {
1163
+ this.advance();
1164
+ }
1165
+ this.skipNewlines();
1166
+ // Parse case items
1167
+ while (this.peek().type !== 'ESAC' && this.peek().type !== 'EOF') {
1168
+ // Parse pattern
1169
+ if (this.peek().type === 'WORD' || this.peek().type === 'LPAREN') {
1170
+ // Skip optional (
1171
+ if (this.peek().type === 'LPAREN') {
1172
+ this.advance();
1173
+ }
1174
+ // Parse pattern word(s)
1175
+ while (this.peek().type === 'WORD') {
1176
+ this.advance();
1177
+ if (this.peek().type === 'PIPE') {
1178
+ this.advance(); // pattern separator
1179
+ }
1180
+ else {
1181
+ break;
1182
+ }
1183
+ }
1184
+ // Expect )
1185
+ if (this.peek().type === 'RPAREN') {
1186
+ this.advance();
1187
+ }
1188
+ this.skipNewlines();
1189
+ // Parse case body until ;;
1190
+ while (this.peek().type !== 'CASE_PATTERN_END' &&
1191
+ this.peek().type !== 'ESAC' &&
1192
+ this.peek().type !== 'EOF') {
1193
+ const node = this.parseCompoundList();
1194
+ if (node) {
1195
+ body.push(node);
1196
+ }
1197
+ else {
1198
+ break;
1199
+ }
1200
+ this.skipNewlines();
1201
+ if (this.peek().type === 'SEMICOLON') {
1202
+ this.advance();
1203
+ this.skipNewlines();
1204
+ }
1205
+ }
1206
+ // Consume ;;
1207
+ if (this.peek().type === 'CASE_PATTERN_END') {
1208
+ this.advance();
1209
+ }
1210
+ this.skipNewlines();
1211
+ }
1212
+ else {
1213
+ break;
1214
+ }
1215
+ }
1216
+ // Expect 'esac'
1217
+ if (this.peek().type !== 'ESAC') {
1218
+ this.errors.push({
1219
+ message: "Missing 'esac' to close case statement",
1220
+ line: startToken.line,
1221
+ column: startToken.column,
1222
+ suggestion: "Add 'esac' at the end",
1223
+ });
1224
+ }
1225
+ else {
1226
+ this.advance();
1227
+ }
1228
+ return { type: 'CompoundCommand', kind: 'case', body };
1229
+ }
1230
+ parseTestCommand() {
1231
+ const token = this.peek();
1232
+ // [[ test ]]
1233
+ if (token.type === 'DOUBLE_LBRACKET') {
1234
+ const startToken = this.advance();
1235
+ const body = [];
1236
+ // Parse test expression - [[ ]] can contain && and || as operators within the test
1237
+ while (this.peek().type !== 'DOUBLE_RBRACKET' && this.peek().type !== 'EOF') {
1238
+ const currentToken = this.peek();
1239
+ // Handle && and || inside [[ ]]
1240
+ if (currentToken.type === 'AND' || currentToken.type === 'OR') {
1241
+ const opToken = this.advance();
1242
+ body.push({ type: 'Command', name: { type: 'Word', value: opToken.value }, prefix: [], args: [], redirects: [] });
1243
+ }
1244
+ else {
1245
+ const word = this.parseWord();
1246
+ if (word) {
1247
+ body.push({ type: 'Command', name: word, prefix: [], args: [], redirects: [] });
1248
+ }
1249
+ else {
1250
+ break;
1251
+ }
1252
+ }
1253
+ }
1254
+ if (this.peek().type !== 'DOUBLE_RBRACKET') {
1255
+ this.errors.push({
1256
+ message: 'Unclosed [[ test: missing ]]',
1257
+ line: startToken.line,
1258
+ column: startToken.column,
1259
+ suggestion: 'Add ]] to close the test',
1260
+ });
1261
+ }
1262
+ else {
1263
+ this.advance();
1264
+ }
1265
+ return { type: 'CompoundCommand', kind: 'arithmetic', body };
1266
+ }
1267
+ // [ test ] - this is handled as a regular command
1268
+ return null;
1269
+ }
1270
+ parseCompoundCommand() {
1271
+ const token = this.peek();
1272
+ switch (token.type) {
1273
+ case 'IF':
1274
+ return this.parseIfStatement();
1275
+ case 'FOR':
1276
+ return this.parseForLoop();
1277
+ case 'WHILE':
1278
+ case 'UNTIL':
1279
+ return this.parseWhileLoop();
1280
+ case 'CASE':
1281
+ return this.parseCaseStatement();
1282
+ case 'LPAREN':
1283
+ return this.parseSubshell();
1284
+ case 'LBRACE':
1285
+ return this.parseBraceGroup();
1286
+ case 'DOUBLE_LBRACKET':
1287
+ return this.parseTestCommand();
1288
+ default:
1289
+ return null;
1290
+ }
1291
+ }
1292
+ parsePipeline() {
1293
+ const commands = [];
1294
+ let negated = false;
1295
+ // Check for negation (! at start of pipeline)
1296
+ if (this.peek().type === 'WORD' && this.peek().value === '!') {
1297
+ negated = true;
1298
+ this.advance(); // consume !
1299
+ this.skipWhitespaceTokens();
1300
+ }
1301
+ // Check for leading pipe (error)
1302
+ if (this.peek().type === 'PIPE') {
1303
+ this.errors.push({
1304
+ message: 'Pipe at start of command: command expected before |',
1305
+ line: this.peek().line,
1306
+ column: this.peek().column,
1307
+ suggestion: 'Add a command before the pipe',
1308
+ });
1309
+ this.advance(); // consume the pipe and continue
1310
+ }
1311
+ // Try compound command first
1312
+ const compound = this.parseCompoundCommand();
1313
+ if (compound) {
1314
+ // Check for pipe after compound
1315
+ this.skipNewlines();
1316
+ if (this.peek().type === 'PIPE') {
1317
+ // Compound commands in pipelines - for now, return as-is
1318
+ return compound;
1319
+ }
1320
+ return compound;
1321
+ }
1322
+ // Parse first simple command
1323
+ const firstCmd = this.parseSimpleCommand();
1324
+ if (!firstCmd) {
1325
+ return null;
1326
+ }
1327
+ commands.push(firstCmd);
1328
+ // Parse pipeline continuation
1329
+ while (this.peek().type === 'PIPE') {
1330
+ const pipeToken = this.advance();
1331
+ // Check for consecutive pipes
1332
+ if (this.peek().type === 'PIPE') {
1333
+ this.errors.push({
1334
+ message: 'Consecutive pipes: remove extra |',
1335
+ line: this.peek().line,
1336
+ column: this.peek().column,
1337
+ suggestion: 'Remove one of the consecutive pipes',
1338
+ });
1339
+ this.advance(); // consume extra pipe
1340
+ }
1341
+ this.skipNewlines();
1342
+ // Check for missing command after pipe
1343
+ if (this.isCommandTerminator(this.peek()) || this.peek().type === 'EOF') {
1344
+ this.errors.push({
1345
+ message: 'Pipe incomplete: missing command after |',
1346
+ line: pipeToken.line,
1347
+ column: pipeToken.column,
1348
+ suggestion: 'Add a command after the pipe',
1349
+ });
1350
+ break;
1351
+ }
1352
+ const nextCmd = this.parseSimpleCommand();
1353
+ if (nextCmd) {
1354
+ commands.push(nextCmd);
1355
+ }
1356
+ else {
1357
+ break;
1358
+ }
1359
+ }
1360
+ if (commands.length === 1 && !negated) {
1361
+ return commands[0];
1362
+ }
1363
+ return {
1364
+ type: 'Pipeline',
1365
+ negated,
1366
+ commands,
1367
+ };
1368
+ }
1369
+ parseAndOr() {
1370
+ let left = this.parsePipeline();
1371
+ if (!left) {
1372
+ return null;
1373
+ }
1374
+ while (this.peek().type === 'AND' || this.peek().type === 'OR') {
1375
+ const opToken = this.advance();
1376
+ const operator = opToken.type === 'AND' ? '&&' : '||';
1377
+ this.skipNewlines();
1378
+ // Check for missing command after && or ||
1379
+ if (this.isCommandTerminator(this.peek()) || this.peek().type === 'EOF') {
1380
+ this.errors.push({
1381
+ message: `Incomplete ${operator === '&&' ? 'AND' : 'OR'} list: missing command after ${operator}`,
1382
+ line: opToken.line,
1383
+ column: opToken.column,
1384
+ suggestion: `Add a command after ${operator}`,
1385
+ });
1386
+ return left;
1387
+ }
1388
+ const right = this.parsePipeline();
1389
+ if (!right) {
1390
+ return left;
1391
+ }
1392
+ left = {
1393
+ type: 'List',
1394
+ operator,
1395
+ left,
1396
+ right,
1397
+ };
1398
+ }
1399
+ return left;
1400
+ }
1401
+ /**
1402
+ * Parse a condition for if/while/until statements.
1403
+ * Unlike parseCompoundList, this doesn't report "unexpected semicolon" errors
1404
+ * since conditions are typically followed by semicolons in one-line syntax.
1405
+ */
1406
+ parseCondition() {
1407
+ this.skipNewlines();
1408
+ return this.parseAndOr();
1409
+ }
1410
+ parseCompoundList() {
1411
+ this.skipNewlines();
1412
+ // Check for unexpected token at start
1413
+ if (this.peek().type === 'SEMICOLON') {
1414
+ const token = this.peek();
1415
+ this.errors.push({
1416
+ message: 'Unexpected semicolon at start of command',
1417
+ line: token.line,
1418
+ column: token.column,
1419
+ suggestion: 'Remove the leading semicolon',
1420
+ });
1421
+ this.advance();
1422
+ this.skipNewlines();
1423
+ }
1424
+ return this.parseAndOr();
1425
+ }
1426
+ parse() {
1427
+ const body = [];
1428
+ while (this.peek().type !== 'EOF') {
1429
+ this.skipNewlines();
1430
+ if (this.peek().type === 'EOF') {
1431
+ break;
1432
+ }
1433
+ const node = this.parseCompoundList();
1434
+ if (node) {
1435
+ body.push(node);
1436
+ }
1437
+ // Consume terminators
1438
+ const nextToken = this.peek();
1439
+ if (nextToken.type === 'SEMICOLON' || nextToken.type === 'NEWLINE' || nextToken.type === 'BACKGROUND') {
1440
+ this.advance();
1441
+ }
1442
+ else if (nextToken.type !== 'EOF' && !node) {
1443
+ // Stuck, skip token to avoid infinite loop
1444
+ this.advance();
1445
+ }
1446
+ }
1447
+ return {
1448
+ type: 'Program',
1449
+ body,
1450
+ errors: this.errors.length > 0 ? this.errors : undefined,
1451
+ };
1452
+ }
1453
+ }
1454
+ // ============================================================================
1455
+ // Public API
1456
+ // ============================================================================
1457
+ /**
1458
+ * Parse a bash command string into an AST
1459
+ *
1460
+ * @example
1461
+ * ```typescript
1462
+ * const ast = parse('ls -la | grep foo')
1463
+ * // Returns Program with Pipeline containing two Commands
1464
+ * ```
1465
+ */
1466
+ export function parse(input) {
1467
+ // Handle empty or whitespace-only input
1468
+ const trimmed = input.trim();
1469
+ if (trimmed === '') {
1470
+ return {
1471
+ type: 'Program',
1472
+ body: [],
1473
+ errors: undefined,
1474
+ };
1475
+ }
1476
+ const lexer = new Lexer(input);
1477
+ const tokens = lexer.tokenize();
1478
+ const parser = new Parser(tokens, lexer.errors);
1479
+ return parser.parse();
1480
+ }
1481
+ /**
1482
+ * Check if input is syntactically valid bash
1483
+ */
1484
+ export function isValidSyntax(input) {
1485
+ const ast = parse(input);
1486
+ return !ast.errors || ast.errors.length === 0;
1487
+ }
1488
+ //# sourceMappingURL=parser.js.map