@smthrs/control 0.0.0-stage → 1.0.0-rc.4

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 (418) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/LICENSE +21 -0
  3. package/README.md +168 -2
  4. package/dist/cjs/ApprovalAuthority.d.ts +73 -0
  5. package/dist/cjs/ApprovalAuthority.d.ts.map +1 -0
  6. package/dist/cjs/ApprovalAuthority.js +62 -0
  7. package/dist/cjs/ApprovalAuthority.js.map +7 -0
  8. package/dist/cjs/Cancellation.d.ts +107 -0
  9. package/dist/cjs/Cancellation.d.ts.map +1 -0
  10. package/dist/cjs/Cancellation.js +72 -0
  11. package/dist/cjs/Cancellation.js.map +7 -0
  12. package/dist/cjs/Channels.d.ts +170 -0
  13. package/dist/cjs/Channels.d.ts.map +1 -0
  14. package/dist/cjs/Channels.js +278 -0
  15. package/dist/cjs/Channels.js.map +7 -0
  16. package/dist/cjs/Control.d.ts +202 -0
  17. package/dist/cjs/Control.d.ts.map +1 -0
  18. package/dist/cjs/Control.js +47 -0
  19. package/dist/cjs/Control.js.map +7 -0
  20. package/dist/cjs/ControlClient.d.ts +52 -0
  21. package/dist/cjs/ControlClient.d.ts.map +1 -0
  22. package/dist/cjs/ControlClient.js +191 -0
  23. package/dist/cjs/ControlClient.js.map +7 -0
  24. package/dist/cjs/ControlError.d.ts +318 -0
  25. package/dist/cjs/ControlError.d.ts.map +1 -0
  26. package/dist/cjs/ControlError.js +249 -0
  27. package/dist/cjs/ControlError.js.map +7 -0
  28. package/dist/cjs/ControlExecutor.d.ts +372 -0
  29. package/dist/cjs/ControlExecutor.d.ts.map +1 -0
  30. package/dist/cjs/ControlExecutor.js +123 -0
  31. package/dist/cjs/ControlExecutor.js.map +7 -0
  32. package/dist/cjs/ControlFacts.d.ts +454 -0
  33. package/dist/cjs/ControlFacts.d.ts.map +1 -0
  34. package/dist/cjs/ControlFacts.js +261 -0
  35. package/dist/cjs/ControlFacts.js.map +7 -0
  36. package/dist/cjs/ControlLive.d.ts +23 -0
  37. package/dist/cjs/ControlLive.d.ts.map +1 -0
  38. package/dist/cjs/ControlLive.js +1293 -0
  39. package/dist/cjs/ControlLive.js.map +7 -0
  40. package/dist/cjs/ControlRpcs.d.ts +1204 -0
  41. package/dist/cjs/ControlRpcs.d.ts.map +1 -0
  42. package/dist/cjs/ControlRpcs.js +247 -0
  43. package/dist/cjs/ControlRpcs.js.map +7 -0
  44. package/dist/cjs/ControlRuntime.d.ts +635 -0
  45. package/dist/cjs/ControlRuntime.d.ts.map +1 -0
  46. package/dist/cjs/ControlRuntime.js +744 -0
  47. package/dist/cjs/ControlRuntime.js.map +7 -0
  48. package/dist/cjs/ControlSchema.d.ts +2642 -0
  49. package/dist/cjs/ControlSchema.d.ts.map +1 -0
  50. package/dist/cjs/ControlSchema.js +634 -0
  51. package/dist/cjs/ControlSchema.js.map +7 -0
  52. package/dist/cjs/ControlServer.d.ts +51 -0
  53. package/dist/cjs/ControlServer.d.ts.map +1 -0
  54. package/dist/cjs/ControlServer.js +121 -0
  55. package/dist/cjs/ControlServer.js.map +7 -0
  56. package/dist/cjs/Credential.d.ts +136 -0
  57. package/dist/cjs/Credential.d.ts.map +1 -0
  58. package/dist/cjs/Credential.js +168 -0
  59. package/dist/cjs/Credential.js.map +7 -0
  60. package/dist/cjs/CredentialCipher.d.ts +90 -0
  61. package/dist/cjs/CredentialCipher.d.ts.map +1 -0
  62. package/dist/cjs/CredentialCipher.js +45 -0
  63. package/dist/cjs/CredentialCipher.js.map +7 -0
  64. package/dist/cjs/CredentialStore.d.ts +97 -0
  65. package/dist/cjs/CredentialStore.d.ts.map +1 -0
  66. package/dist/cjs/CredentialStore.js +81 -0
  67. package/dist/cjs/CredentialStore.js.map +7 -0
  68. package/dist/cjs/DispatchReader.d.ts +112 -0
  69. package/dist/cjs/DispatchReader.d.ts.map +1 -0
  70. package/dist/cjs/DispatchReader.js +45 -0
  71. package/dist/cjs/DispatchReader.js.map +7 -0
  72. package/dist/cjs/Health.d.ts +333 -0
  73. package/dist/cjs/Health.d.ts.map +1 -0
  74. package/dist/cjs/Health.js +311 -0
  75. package/dist/cjs/Health.js.map +7 -0
  76. package/dist/cjs/JevSessionChecker.d.ts +57 -0
  77. package/dist/cjs/JevSessionChecker.d.ts.map +1 -0
  78. package/dist/cjs/JevSessionChecker.js +113 -0
  79. package/dist/cjs/JevSessionChecker.js.map +7 -0
  80. package/dist/cjs/Lineage.d.ts +131 -0
  81. package/dist/cjs/Lineage.d.ts.map +1 -0
  82. package/dist/cjs/Lineage.js +81 -0
  83. package/dist/cjs/Lineage.js.map +7 -0
  84. package/dist/cjs/Migrations.d.ts +34 -0
  85. package/dist/cjs/Migrations.d.ts.map +1 -0
  86. package/dist/cjs/Migrations.js +60 -0
  87. package/dist/cjs/Migrations.js.map +7 -0
  88. package/dist/cjs/Monitor.d.ts +282 -0
  89. package/dist/cjs/Monitor.d.ts.map +1 -0
  90. package/dist/cjs/Monitor.js +283 -0
  91. package/dist/cjs/Monitor.js.map +7 -0
  92. package/dist/cjs/ScopedToken.d.ts +193 -0
  93. package/dist/cjs/ScopedToken.d.ts.map +1 -0
  94. package/dist/cjs/ScopedToken.js +135 -0
  95. package/dist/cjs/ScopedToken.js.map +7 -0
  96. package/dist/cjs/SqlControlRuntime.d.ts +161 -0
  97. package/dist/cjs/SqlControlRuntime.d.ts.map +1 -0
  98. package/dist/cjs/SqlControlRuntime.js +1522 -0
  99. package/dist/cjs/SqlControlRuntime.js.map +7 -0
  100. package/dist/cjs/SqlCredentialStore.d.ts +43 -0
  101. package/dist/cjs/SqlCredentialStore.d.ts.map +1 -0
  102. package/dist/cjs/SqlCredentialStore.js +113 -0
  103. package/dist/cjs/SqlCredentialStore.js.map +7 -0
  104. package/dist/cjs/Steering.d.ts +69 -0
  105. package/dist/cjs/Steering.d.ts.map +1 -0
  106. package/dist/cjs/Steering.js +49 -0
  107. package/dist/cjs/Steering.js.map +7 -0
  108. package/dist/cjs/SystemFlows.d.ts +223 -0
  109. package/dist/cjs/SystemFlows.d.ts.map +1 -0
  110. package/dist/cjs/SystemFlows.js +195 -0
  111. package/dist/cjs/SystemFlows.js.map +7 -0
  112. package/dist/cjs/WebCryptoCipher.d.ts +49 -0
  113. package/dist/cjs/WebCryptoCipher.d.ts.map +1 -0
  114. package/dist/cjs/WebCryptoCipher.js +129 -0
  115. package/dist/cjs/WebCryptoCipher.js.map +7 -0
  116. package/dist/cjs/WebhookChannel.d.ts +113 -0
  117. package/dist/cjs/WebhookChannel.d.ts.map +1 -0
  118. package/dist/cjs/WebhookChannel.js +98 -0
  119. package/dist/cjs/WebhookChannel.js.map +7 -0
  120. package/dist/cjs/index.d.ts +160 -0
  121. package/dist/cjs/index.d.ts.map +1 -0
  122. package/dist/cjs/index.js +91 -0
  123. package/dist/cjs/index.js.map +7 -0
  124. package/dist/cjs/internal/MutationBoundary.d.ts +27 -0
  125. package/dist/cjs/internal/MutationBoundary.d.ts.map +1 -0
  126. package/dist/cjs/internal/MutationBoundary.js +50 -0
  127. package/dist/cjs/internal/MutationBoundary.js.map +7 -0
  128. package/dist/cjs/internal/activeFibers.d.ts +12 -0
  129. package/dist/cjs/internal/activeFibers.d.ts.map +1 -0
  130. package/dist/cjs/internal/activeFibers.js +30 -0
  131. package/dist/cjs/internal/activeFibers.js.map +7 -0
  132. package/dist/cjs/internal/issues.d.ts +28 -0
  133. package/dist/cjs/internal/issues.d.ts.map +1 -0
  134. package/dist/cjs/internal/issues.js +34 -0
  135. package/dist/cjs/internal/issues.js.map +7 -0
  136. package/dist/cjs/internal/planning.d.ts +347 -0
  137. package/dist/cjs/internal/planning.d.ts.map +1 -0
  138. package/dist/cjs/internal/planning.js +137 -0
  139. package/dist/cjs/internal/planning.js.map +7 -0
  140. package/dist/cjs/internal/sqlSchemaErrors.d.ts +18 -0
  141. package/dist/cjs/internal/sqlSchemaErrors.d.ts.map +1 -0
  142. package/dist/cjs/internal/sqlSchemaErrors.js +40 -0
  143. package/dist/cjs/internal/sqlSchemaErrors.js.map +7 -0
  144. package/dist/cjs/migrations/0001_control_tables.d.ts +18 -0
  145. package/dist/cjs/migrations/0001_control_tables.d.ts.map +1 -0
  146. package/dist/cjs/migrations/0001_control_tables.js +115 -0
  147. package/dist/cjs/migrations/0001_control_tables.js.map +7 -0
  148. package/dist/cjs/migrations/0002_run_keys.d.ts +15 -0
  149. package/dist/cjs/migrations/0002_run_keys.d.ts.map +1 -0
  150. package/dist/cjs/migrations/0002_run_keys.js +44 -0
  151. package/dist/cjs/migrations/0002_run_keys.js.map +7 -0
  152. package/dist/cjs/migrations/0003_signal_commands.d.ts +13 -0
  153. package/dist/cjs/migrations/0003_signal_commands.d.ts.map +1 -0
  154. package/dist/cjs/migrations/0003_signal_commands.js +50 -0
  155. package/dist/cjs/migrations/0003_signal_commands.js.map +7 -0
  156. package/dist/cjs/migrations/0004_approval_decisions.d.ts +13 -0
  157. package/dist/cjs/migrations/0004_approval_decisions.d.ts.map +1 -0
  158. package/dist/cjs/migrations/0004_approval_decisions.js +45 -0
  159. package/dist/cjs/migrations/0004_approval_decisions.js.map +7 -0
  160. package/dist/cjs/migrations/0005_signal_principals.d.ts +18 -0
  161. package/dist/cjs/migrations/0005_signal_principals.d.ts.map +1 -0
  162. package/dist/cjs/migrations/0005_signal_principals.js +45 -0
  163. package/dist/cjs/migrations/0005_signal_principals.js.map +7 -0
  164. package/dist/cjs/migrations/0006_run_principals.d.ts +18 -0
  165. package/dist/cjs/migrations/0006_run_principals.d.ts.map +1 -0
  166. package/dist/cjs/migrations/0006_run_principals.js +49 -0
  167. package/dist/cjs/migrations/0006_run_principals.js.map +7 -0
  168. package/dist/cjs/migrations/0007_resume_consent.d.ts +18 -0
  169. package/dist/cjs/migrations/0007_resume_consent.d.ts.map +1 -0
  170. package/dist/cjs/migrations/0007_resume_consent.js +46 -0
  171. package/dist/cjs/migrations/0007_resume_consent.js.map +7 -0
  172. package/dist/cjs/package.json +1 -0
  173. package/dist/cjs/test/TestControl.d.ts +19 -0
  174. package/dist/cjs/test/TestControl.d.ts.map +1 -0
  175. package/dist/cjs/test/TestControl.js +62 -0
  176. package/dist/cjs/test/TestControl.js.map +7 -0
  177. package/dist/esm/ApprovalAuthority.d.ts +73 -0
  178. package/dist/esm/ApprovalAuthority.d.ts.map +1 -0
  179. package/dist/esm/ApprovalAuthority.js +72 -0
  180. package/dist/esm/ApprovalAuthority.js.map +1 -0
  181. package/dist/esm/Cancellation.d.ts +107 -0
  182. package/dist/esm/Cancellation.d.ts.map +1 -0
  183. package/dist/esm/Cancellation.js +116 -0
  184. package/dist/esm/Cancellation.js.map +1 -0
  185. package/dist/esm/Channels.d.ts +170 -0
  186. package/dist/esm/Channels.d.ts.map +1 -0
  187. package/dist/esm/Channels.js +312 -0
  188. package/dist/esm/Channels.js.map +1 -0
  189. package/dist/esm/Control.d.ts +202 -0
  190. package/dist/esm/Control.d.ts.map +1 -0
  191. package/dist/esm/Control.js +42 -0
  192. package/dist/esm/Control.js.map +1 -0
  193. package/dist/esm/ControlClient.d.ts +52 -0
  194. package/dist/esm/ControlClient.d.ts.map +1 -0
  195. package/dist/esm/ControlClient.js +217 -0
  196. package/dist/esm/ControlClient.js.map +1 -0
  197. package/dist/esm/ControlError.d.ts +318 -0
  198. package/dist/esm/ControlError.d.ts.map +1 -0
  199. package/dist/esm/ControlError.js +359 -0
  200. package/dist/esm/ControlError.js.map +1 -0
  201. package/dist/esm/ControlExecutor.d.ts +372 -0
  202. package/dist/esm/ControlExecutor.d.ts.map +1 -0
  203. package/dist/esm/ControlExecutor.js +212 -0
  204. package/dist/esm/ControlExecutor.js.map +1 -0
  205. package/dist/esm/ControlFacts.d.ts +454 -0
  206. package/dist/esm/ControlFacts.d.ts.map +1 -0
  207. package/dist/esm/ControlFacts.js +324 -0
  208. package/dist/esm/ControlFacts.js.map +1 -0
  209. package/dist/esm/ControlLive.d.ts +23 -0
  210. package/dist/esm/ControlLive.d.ts.map +1 -0
  211. package/dist/esm/ControlLive.js +1589 -0
  212. package/dist/esm/ControlLive.js.map +1 -0
  213. package/dist/esm/ControlRpcs.d.ts +1204 -0
  214. package/dist/esm/ControlRpcs.d.ts.map +1 -0
  215. package/dist/esm/ControlRpcs.js +299 -0
  216. package/dist/esm/ControlRpcs.js.map +1 -0
  217. package/dist/esm/ControlRuntime.d.ts +635 -0
  218. package/dist/esm/ControlRuntime.d.ts.map +1 -0
  219. package/dist/esm/ControlRuntime.js +808 -0
  220. package/dist/esm/ControlRuntime.js.map +1 -0
  221. package/dist/esm/ControlSchema.d.ts +2642 -0
  222. package/dist/esm/ControlSchema.d.ts.map +1 -0
  223. package/dist/esm/ControlSchema.js +1030 -0
  224. package/dist/esm/ControlSchema.js.map +1 -0
  225. package/dist/esm/ControlServer.d.ts +51 -0
  226. package/dist/esm/ControlServer.d.ts.map +1 -0
  227. package/dist/esm/ControlServer.js +145 -0
  228. package/dist/esm/ControlServer.js.map +1 -0
  229. package/dist/esm/Credential.d.ts +136 -0
  230. package/dist/esm/Credential.d.ts.map +1 -0
  231. package/dist/esm/Credential.js +190 -0
  232. package/dist/esm/Credential.js.map +1 -0
  233. package/dist/esm/CredentialCipher.d.ts +90 -0
  234. package/dist/esm/CredentialCipher.d.ts.map +1 -0
  235. package/dist/esm/CredentialCipher.js +56 -0
  236. package/dist/esm/CredentialCipher.js.map +1 -0
  237. package/dist/esm/CredentialStore.d.ts +97 -0
  238. package/dist/esm/CredentialStore.d.ts.map +1 -0
  239. package/dist/esm/CredentialStore.js +101 -0
  240. package/dist/esm/CredentialStore.js.map +1 -0
  241. package/dist/esm/DispatchReader.d.ts +112 -0
  242. package/dist/esm/DispatchReader.d.ts.map +1 -0
  243. package/dist/esm/DispatchReader.js +76 -0
  244. package/dist/esm/DispatchReader.js.map +1 -0
  245. package/dist/esm/Health.d.ts +333 -0
  246. package/dist/esm/Health.d.ts.map +1 -0
  247. package/dist/esm/Health.js +400 -0
  248. package/dist/esm/Health.js.map +1 -0
  249. package/dist/esm/JevSessionChecker.d.ts +57 -0
  250. package/dist/esm/JevSessionChecker.d.ts.map +1 -0
  251. package/dist/esm/JevSessionChecker.js +108 -0
  252. package/dist/esm/JevSessionChecker.js.map +1 -0
  253. package/dist/esm/Lineage.d.ts +131 -0
  254. package/dist/esm/Lineage.d.ts.map +1 -0
  255. package/dist/esm/Lineage.js +174 -0
  256. package/dist/esm/Lineage.js.map +1 -0
  257. package/dist/esm/Migrations.d.ts +34 -0
  258. package/dist/esm/Migrations.d.ts.map +1 -0
  259. package/dist/esm/Migrations.js +53 -0
  260. package/dist/esm/Migrations.js.map +1 -0
  261. package/dist/esm/Monitor.d.ts +282 -0
  262. package/dist/esm/Monitor.d.ts.map +1 -0
  263. package/dist/esm/Monitor.js +415 -0
  264. package/dist/esm/Monitor.js.map +1 -0
  265. package/dist/esm/ScopedToken.d.ts +193 -0
  266. package/dist/esm/ScopedToken.d.ts.map +1 -0
  267. package/dist/esm/ScopedToken.js +224 -0
  268. package/dist/esm/ScopedToken.js.map +1 -0
  269. package/dist/esm/SqlControlRuntime.d.ts +161 -0
  270. package/dist/esm/SqlControlRuntime.d.ts.map +1 -0
  271. package/dist/esm/SqlControlRuntime.js +1756 -0
  272. package/dist/esm/SqlControlRuntime.js.map +1 -0
  273. package/dist/esm/SqlCredentialStore.d.ts +43 -0
  274. package/dist/esm/SqlCredentialStore.d.ts.map +1 -0
  275. package/dist/esm/SqlCredentialStore.js +97 -0
  276. package/dist/esm/SqlCredentialStore.js.map +1 -0
  277. package/dist/esm/Steering.d.ts +69 -0
  278. package/dist/esm/Steering.d.ts.map +1 -0
  279. package/dist/esm/Steering.js +89 -0
  280. package/dist/esm/Steering.js.map +1 -0
  281. package/dist/esm/SystemFlows.d.ts +223 -0
  282. package/dist/esm/SystemFlows.d.ts.map +1 -0
  283. package/dist/esm/SystemFlows.js +198 -0
  284. package/dist/esm/SystemFlows.js.map +1 -0
  285. package/dist/esm/WebCryptoCipher.d.ts +49 -0
  286. package/dist/esm/WebCryptoCipher.d.ts.map +1 -0
  287. package/dist/esm/WebCryptoCipher.js +123 -0
  288. package/dist/esm/WebCryptoCipher.js.map +1 -0
  289. package/dist/esm/WebhookChannel.d.ts +113 -0
  290. package/dist/esm/WebhookChannel.d.ts.map +1 -0
  291. package/dist/esm/WebhookChannel.js +109 -0
  292. package/dist/esm/WebhookChannel.js.map +1 -0
  293. package/dist/esm/index.d.ts +160 -0
  294. package/dist/esm/index.d.ts.map +1 -0
  295. package/dist/esm/index.js +160 -0
  296. package/dist/esm/index.js.map +1 -0
  297. package/dist/esm/internal/MutationBoundary.d.ts +27 -0
  298. package/dist/esm/internal/MutationBoundary.d.ts.map +1 -0
  299. package/dist/esm/internal/MutationBoundary.js +40 -0
  300. package/dist/esm/internal/MutationBoundary.js.map +1 -0
  301. package/dist/esm/internal/activeFibers.d.ts +12 -0
  302. package/dist/esm/internal/activeFibers.d.ts.map +1 -0
  303. package/dist/esm/internal/activeFibers.js +17 -0
  304. package/dist/esm/internal/activeFibers.js.map +1 -0
  305. package/dist/esm/internal/issues.d.ts +28 -0
  306. package/dist/esm/internal/issues.d.ts.map +1 -0
  307. package/dist/esm/internal/issues.js +35 -0
  308. package/dist/esm/internal/issues.js.map +1 -0
  309. package/dist/esm/internal/planning.d.ts +347 -0
  310. package/dist/esm/internal/planning.d.ts.map +1 -0
  311. package/dist/esm/internal/planning.js +199 -0
  312. package/dist/esm/internal/planning.js.map +1 -0
  313. package/dist/esm/internal/sqlSchemaErrors.d.ts +18 -0
  314. package/dist/esm/internal/sqlSchemaErrors.d.ts.map +1 -0
  315. package/dist/esm/internal/sqlSchemaErrors.js +38 -0
  316. package/dist/esm/internal/sqlSchemaErrors.js.map +1 -0
  317. package/dist/esm/migrations/0001_control_tables.d.ts +18 -0
  318. package/dist/esm/migrations/0001_control_tables.d.ts.map +1 -0
  319. package/dist/esm/migrations/0001_control_tables.js +96 -0
  320. package/dist/esm/migrations/0001_control_tables.js.map +1 -0
  321. package/dist/esm/migrations/0002_run_keys.d.ts +15 -0
  322. package/dist/esm/migrations/0002_run_keys.d.ts.map +1 -0
  323. package/dist/esm/migrations/0002_run_keys.js +22 -0
  324. package/dist/esm/migrations/0002_run_keys.js.map +1 -0
  325. package/dist/esm/migrations/0003_signal_commands.d.ts +13 -0
  326. package/dist/esm/migrations/0003_signal_commands.d.ts.map +1 -0
  327. package/dist/esm/migrations/0003_signal_commands.js +26 -0
  328. package/dist/esm/migrations/0003_signal_commands.js.map +1 -0
  329. package/dist/esm/migrations/0004_approval_decisions.d.ts +13 -0
  330. package/dist/esm/migrations/0004_approval_decisions.d.ts.map +1 -0
  331. package/dist/esm/migrations/0004_approval_decisions.js +25 -0
  332. package/dist/esm/migrations/0004_approval_decisions.js.map +1 -0
  333. package/dist/esm/migrations/0005_signal_principals.d.ts +18 -0
  334. package/dist/esm/migrations/0005_signal_principals.d.ts.map +1 -0
  335. package/dist/esm/migrations/0005_signal_principals.js +27 -0
  336. package/dist/esm/migrations/0005_signal_principals.js.map +1 -0
  337. package/dist/esm/migrations/0006_run_principals.d.ts +18 -0
  338. package/dist/esm/migrations/0006_run_principals.d.ts.map +1 -0
  339. package/dist/esm/migrations/0006_run_principals.js +30 -0
  340. package/dist/esm/migrations/0006_run_principals.js.map +1 -0
  341. package/dist/esm/migrations/0007_resume_consent.d.ts +18 -0
  342. package/dist/esm/migrations/0007_resume_consent.d.ts.map +1 -0
  343. package/dist/esm/migrations/0007_resume_consent.js +27 -0
  344. package/dist/esm/migrations/0007_resume_consent.js.map +1 -0
  345. package/dist/esm/test/TestControl.d.ts +19 -0
  346. package/dist/esm/test/TestControl.d.ts.map +1 -0
  347. package/dist/esm/test/TestControl.js +30 -0
  348. package/dist/esm/test/TestControl.js.map +1 -0
  349. package/docs/README.md +189 -0
  350. package/docs/api.md +982 -0
  351. package/docs/concepts/authority.md +109 -0
  352. package/docs/concepts/cancellation.md +129 -0
  353. package/docs/concepts/lineage.md +132 -0
  354. package/docs/concepts/ownership.md +139 -0
  355. package/docs/concepts/projections.md +203 -0
  356. package/docs/concepts/receipts.md +128 -0
  357. package/docs/guides/approvals.md +284 -0
  358. package/docs/guides/cancel-and-resume.md +162 -0
  359. package/docs/guides/durable-storage.md +147 -0
  360. package/docs/guides/implement-an-executor.md +173 -0
  361. package/docs/guides/ingest-a-webhook.md +177 -0
  362. package/docs/guides/list-runs.md +160 -0
  363. package/docs/guides/monitor-runs.md +176 -0
  364. package/docs/guides/observe-health.md +147 -0
  365. package/docs/guides/postgres-tests.md +7 -0
  366. package/docs/guides/serve-over-rpc.md +220 -0
  367. package/docs/guides/signal-a-run.md +53 -0
  368. package/docs/guides/steer-a-run.md +138 -0
  369. package/docs/guides/store-credentials.md +164 -0
  370. package/docs/guides/testing.md +139 -0
  371. package/docs/guides/watch-a-run.md +154 -0
  372. package/docs/installation.md +106 -0
  373. package/docs/quickstart.md +163 -0
  374. package/docs/troubleshooting.md +208 -0
  375. package/package.json +405 -3
  376. package/src/ApprovalAuthority.ts +114 -0
  377. package/src/Cancellation.ts +172 -0
  378. package/src/Channels.ts +493 -0
  379. package/src/Control.ts +337 -0
  380. package/src/ControlClient.ts +319 -0
  381. package/src/ControlError.ts +378 -0
  382. package/src/ControlExecutor.ts +490 -0
  383. package/src/ControlFacts.ts +383 -0
  384. package/src/ControlLive.ts +2127 -0
  385. package/src/ControlRpcs.ts +443 -0
  386. package/src/ControlRuntime.ts +1601 -0
  387. package/src/ControlSchema.ts +1380 -0
  388. package/src/ControlServer.ts +182 -0
  389. package/src/Credential.ts +310 -0
  390. package/src/CredentialCipher.ts +110 -0
  391. package/src/CredentialStore.ts +152 -0
  392. package/src/DispatchReader.ts +122 -0
  393. package/src/Health.ts +591 -0
  394. package/src/JevSessionChecker.ts +127 -0
  395. package/src/Lineage.ts +203 -0
  396. package/src/Migrations.ts +56 -0
  397. package/src/Monitor.ts +600 -0
  398. package/src/ScopedToken.ts +306 -0
  399. package/src/SqlControlRuntime.ts +2478 -0
  400. package/src/SqlCredentialStore.ts +148 -0
  401. package/src/Steering.ts +96 -0
  402. package/src/SystemFlows.ts +225 -0
  403. package/src/WebCryptoCipher.ts +169 -0
  404. package/src/WebhookChannel.ts +166 -0
  405. package/src/index.ts +188 -0
  406. package/src/internal/MutationBoundary.ts +46 -0
  407. package/src/internal/activeFibers.ts +22 -0
  408. package/src/internal/issues.ts +40 -0
  409. package/src/internal/planning.ts +262 -0
  410. package/src/internal/sqlSchemaErrors.ts +37 -0
  411. package/src/migrations/0001_control_tables.ts +99 -0
  412. package/src/migrations/0002_run_keys.ts +23 -0
  413. package/src/migrations/0003_signal_commands.ts +27 -0
  414. package/src/migrations/0004_approval_decisions.ts +25 -0
  415. package/src/migrations/0005_signal_principals.ts +27 -0
  416. package/src/migrations/0006_run_principals.ts +31 -0
  417. package/src/migrations/0007_resume_consent.ts +28 -0
  418. package/src/test/TestControl.ts +47 -0
@@ -0,0 +1,123 @@
1
+ /**
2
+ * AES-256-GCM credential encryption over the Web Crypto API.
3
+ *
4
+ * One adapter serves both hosts the project supports: `globalThis.crypto.subtle`
5
+ * is the browser's own API and has been Node's since v19, so this module
6
+ * imports nothing from `node:*` and still runs unmodified on the server. A host
7
+ * without Web Crypto, such as an old runtime or a locked-down worker, fails
8
+ * with the typed `Unavailable`, never a defect. A key that is not 32
9
+ * base64-encoded bytes fails with `InvalidInput`. A record that does not open
10
+ * fails with `PersistenceError` on operation `credential.open`, which names
11
+ * whether the stored nonce was malformed or the ciphertext failed
12
+ * authentication (a different key, changed metadata, or tampered bytes).
13
+ *
14
+ * The key is host-managed and supplied at layer construction. It is held as a
15
+ * non-extractable `CryptoKey`, so it cannot be read back out of the cipher, and
16
+ * it never reaches `CredentialStore`.
17
+ *
18
+ * @since 0.1.0
19
+ */
20
+ import { canonicalize } from "@smthrs/canonical";
21
+ import { Effect, Layer, Redacted } from "effect";
22
+ import { InvalidInput, PersistenceError } from "./ControlError.js";
23
+ import * as CredentialCipher from "./CredentialCipher.js";
24
+ const algorithm = "AES-GCM";
25
+ const nonceBytes = 12;
26
+ const contextFormatVersion = 1;
27
+ const toBase64 = (bytes) => {
28
+ let binary = "";
29
+ for (const byte of bytes)
30
+ binary += String.fromCharCode(byte);
31
+ return btoa(binary);
32
+ };
33
+ const fromBase64 = (value) => {
34
+ const binary = atob(value);
35
+ const bytes = new Uint8Array(binary.length);
36
+ for (let index = 0; index < binary.length; index++)
37
+ bytes[index] = binary.charCodeAt(index);
38
+ return bytes;
39
+ };
40
+ const subtle = () => Effect.suspend(() => {
41
+ const available = globalThis.crypto;
42
+ return available?.subtle === undefined
43
+ ? Effect.fail(CredentialCipher.unavailable())
44
+ : Effect.succeed(available.subtle);
45
+ });
46
+ const randomNonce = () => Effect.suspend(() => {
47
+ const available = globalThis.crypto;
48
+ return available?.getRandomValues === undefined
49
+ ? Effect.fail(CredentialCipher.unavailable())
50
+ : Effect.succeed(available.getRandomValues(new Uint8Array(nonceBytes)));
51
+ });
52
+ const encodeContext = (context) => Effect.try({
53
+ try: () => {
54
+ // Canonical JSON names and escapes each field, so embedded delimiters
55
+ // cannot make two metadata tuples authenticate as the same bytes. The
56
+ // explicit format version makes a future byte contract distinguishable.
57
+ const document = canonicalize({
58
+ formatVersion: contextFormatVersion,
59
+ id: context.id,
60
+ name: context.name,
61
+ version: context.version
62
+ });
63
+ return new TextEncoder().encode(document);
64
+ },
65
+ catch: CredentialCipher.unavailable
66
+ });
67
+ const invalidKey = () => new InvalidInput({ issue: "Credential key must be 32 base64-encoded bytes" });
68
+ const unopenable = (reason, cause) => new PersistenceError({
69
+ operation: "credential.open",
70
+ message: reason === "malformed_nonce"
71
+ ? "Stored credential nonce is malformed"
72
+ : "Credential failed authentication: a different key, changed metadata, or tampered ciphertext",
73
+ cause
74
+ });
75
+ /**
76
+ * Imports the host key and constructs the cipher.
77
+ *
78
+ * @category constructors
79
+ * @since 0.1.0
80
+ */
81
+ export const make = (options) => Effect.gen(function* () {
82
+ const crypto = yield* subtle();
83
+ const raw = yield* Effect.try({
84
+ try: () => fromBase64(Redacted.value(options.key)),
85
+ catch: invalidKey
86
+ });
87
+ if (raw.length !== 32)
88
+ return yield* Effect.fail(invalidKey());
89
+ const key = yield* Effect.tryPromise({
90
+ try: () => crypto.importKey("raw", raw, algorithm, false, ["encrypt", "decrypt"]),
91
+ catch: CredentialCipher.unavailable
92
+ });
93
+ return CredentialCipher.make({
94
+ seal: Effect.fn("WebCryptoCipher.seal")(function* (plaintext, context) {
95
+ const additionalData = yield* encodeContext(context);
96
+ const nonce = yield* randomNonce();
97
+ const ciphertext = yield* Effect.tryPromise({
98
+ try: () => crypto.encrypt({ name: algorithm, iv: nonce, additionalData: additionalData }, key, new TextEncoder().encode(Redacted.value(plaintext))),
99
+ catch: CredentialCipher.unavailable
100
+ });
101
+ return { ciphertext: toBase64(new Uint8Array(ciphertext)), nonce: toBase64(nonce) };
102
+ }),
103
+ open: Effect.fn("WebCryptoCipher.open")(function* (sealed, context) {
104
+ const additionalData = yield* encodeContext(context);
105
+ const plaintext = yield* Effect.try({
106
+ try: () => fromBase64(sealed.nonce),
107
+ catch: (cause) => unopenable("malformed_nonce", cause)
108
+ }).pipe(Effect.flatMap((nonce) => Effect.tryPromise({
109
+ try: () => crypto.decrypt({ name: algorithm, iv: nonce, additionalData: additionalData }, key, fromBase64(sealed.ciphertext)),
110
+ catch: (cause) => unopenable("authentication_failed", cause)
111
+ })), Effect.tapError((error) => Effect.logWarning({ message: error.message, operation: error.operation, credentialId: context.id })));
112
+ return Redacted.make(new TextDecoder().decode(plaintext));
113
+ })
114
+ });
115
+ });
116
+ /**
117
+ * Provides AES-256-GCM credential encryption under a host-managed key.
118
+ *
119
+ * @category layers
120
+ * @since 0.1.0
121
+ */
122
+ export const layer = (options) => Layer.effect(CredentialCipher.CredentialCipher)(make(options));
123
+ //# sourceMappingURL=WebCryptoCipher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebCryptoCipher.js","sourceRoot":"","sources":["../../src/WebCryptoCipher.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * AES-256-GCM credential encryption over the Web Crypto API.\n *\n * One adapter serves both hosts the project supports: `globalThis.crypto.subtle`\n * is the browser's own API and has been Node's since v19, so this module\n * imports nothing from `node:*` and still runs unmodified on the server. A host\n * without Web Crypto, such as an old runtime or a locked-down worker, fails\n * with the typed `Unavailable`, never a defect. A key that is not 32\n * base64-encoded bytes fails with `InvalidInput`. A record that does not open\n * fails with `PersistenceError` on operation `credential.open`, which names\n * whether the stored nonce was malformed or the ciphertext failed\n * authentication (a different key, changed metadata, or tampered bytes).\n *\n * The key is host-managed and supplied at layer construction. It is held as a\n * non-extractable `CryptoKey`, so it cannot be read back out of the cipher, and\n * it never reaches `CredentialStore`.\n *\n * @since 0.1.0\n */\n\nimport { canonicalize } from \"@smthrs/canonical\"\nimport { Effect, Layer, Redacted } from \"effect\"\nimport { InvalidInput, PersistenceError, type Unavailable } from \"./ControlError.ts\"\nimport * as CredentialCipher from \"./CredentialCipher.ts\"\n\nconst algorithm = \"AES-GCM\"\nconst nonceBytes = 12\nconst contextFormatVersion = 1\n\nconst toBase64 = (bytes: Uint8Array): string => {\n let binary = \"\"\n for (const byte of bytes) binary += String.fromCharCode(byte)\n return btoa(binary)\n}\n\nconst fromBase64 = (value: string): Uint8Array<ArrayBuffer> => {\n const binary = atob(value)\n const bytes = new Uint8Array(binary.length)\n for (let index = 0; index < binary.length; index++) bytes[index] = binary.charCodeAt(index)\n return bytes\n}\n\nconst subtle = (): Effect.Effect<SubtleCrypto, Unavailable> =>\n Effect.suspend(() => {\n const available = globalThis.crypto as Crypto | undefined\n return available?.subtle === undefined\n ? Effect.fail(CredentialCipher.unavailable())\n : Effect.succeed(available.subtle)\n })\n\nconst randomNonce = (): Effect.Effect<Uint8Array<ArrayBuffer>, Unavailable> =>\n Effect.suspend(() => {\n const available = globalThis.crypto as Crypto | undefined\n return available?.getRandomValues === undefined\n ? Effect.fail(CredentialCipher.unavailable())\n : Effect.succeed(available.getRandomValues(new Uint8Array(nonceBytes)))\n })\n\nconst encodeContext = (context: CredentialCipher.Context): Effect.Effect<Uint8Array<ArrayBuffer>, Unavailable> =>\n Effect.try({\n try: () => {\n // Canonical JSON names and escapes each field, so embedded delimiters\n // cannot make two metadata tuples authenticate as the same bytes. The\n // explicit format version makes a future byte contract distinguishable.\n const document = canonicalize({\n formatVersion: contextFormatVersion,\n id: context.id,\n name: context.name,\n version: context.version\n })\n return new TextEncoder().encode(document)\n },\n catch: CredentialCipher.unavailable\n })\n\nconst invalidKey = (): InvalidInput => new InvalidInput({ issue: \"Credential key must be 32 base64-encoded bytes\" })\n\nconst unopenable = (reason: \"malformed_nonce\" | \"authentication_failed\", cause: unknown): PersistenceError =>\n new PersistenceError({\n operation: \"credential.open\",\n message: reason === \"malformed_nonce\"\n ? \"Stored credential nonce is malformed\"\n : \"Credential failed authentication: a different key, changed metadata, or tampered ciphertext\",\n cause\n })\n\n/**\n * Host-managed key material for the cipher.\n *\n * `key` is 32 raw bytes, base64-encoded, held redacted so it cannot be printed\n * or serialized by accident.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Options {\n readonly key: Redacted.Redacted<string>\n}\n\n/**\n * Imports the host key and constructs the cipher.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = (options: Options): Effect.Effect<CredentialCipher.Service, Unavailable | InvalidInput> =>\n Effect.gen(function*() {\n const crypto = yield* subtle()\n const raw = yield* Effect.try({\n try: () => fromBase64(Redacted.value(options.key)),\n catch: invalidKey\n })\n if (raw.length !== 32) return yield* Effect.fail(invalidKey())\n const key = yield* Effect.tryPromise({\n try: () => crypto.importKey(\"raw\", raw, algorithm, false, [\"encrypt\", \"decrypt\"]),\n catch: CredentialCipher.unavailable\n })\n\n return CredentialCipher.make({\n seal: Effect.fn(\"WebCryptoCipher.seal\")(function*(plaintext, context) {\n const additionalData = yield* encodeContext(context)\n const nonce = yield* randomNonce()\n const ciphertext = yield* Effect.tryPromise({\n try: () =>\n crypto.encrypt(\n { name: algorithm, iv: nonce, additionalData: additionalData },\n key,\n new TextEncoder().encode(Redacted.value(plaintext))\n ),\n catch: CredentialCipher.unavailable\n })\n return { ciphertext: toBase64(new Uint8Array(ciphertext)), nonce: toBase64(nonce) }\n }),\n open: Effect.fn(\"WebCryptoCipher.open\")(function*(sealed, context) {\n const additionalData = yield* encodeContext(context)\n const plaintext = yield* Effect.try({\n try: () => fromBase64(sealed.nonce),\n catch: (cause) => unopenable(\"malformed_nonce\", cause)\n }).pipe(\n Effect.flatMap((nonce) =>\n Effect.tryPromise({\n try: () =>\n crypto.decrypt(\n { name: algorithm, iv: nonce, additionalData: additionalData },\n key,\n fromBase64(sealed.ciphertext)\n ),\n catch: (cause) => unopenable(\"authentication_failed\", cause)\n })\n ),\n Effect.tapError((error) =>\n Effect.logWarning({ message: error.message, operation: error.operation, credentialId: context.id })\n )\n )\n return Redacted.make(new TextDecoder().decode(plaintext))\n })\n })\n })\n\n/**\n * Provides AES-256-GCM credential encryption under a host-managed key.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer = (\n options: Options\n): Layer.Layer<CredentialCipher.CredentialCipher, Unavailable | InvalidInput> =>\n Layer.effect(CredentialCipher.CredentialCipher)(make(options))\n"]}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Webhook channel construction over Effect's transport-neutral HTTP request.
3
+ *
4
+ * @since 0.1.0
5
+ */
6
+ import { Effect, type Redacted, Schema } from "effect";
7
+ import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";
8
+ import { type Channel, Channels, type InboundResult, type RawInbound } from "./Channels.ts";
9
+ import { InvalidInput, type Unauthorized } from "./ControlError.ts";
10
+ import type { IdempotencyKey } from "./ControlSchema.ts";
11
+ import type { CredentialRef } from "./Credential.ts";
12
+ /**
13
+ * Signature verifier injected by a webhook transport.
14
+ *
15
+ * The credential is intentionally a redacted reference. Resolution belongs at
16
+ * the host adapter boundary, never in webhook input or its persisted record.
17
+ *
18
+ * @category models
19
+ * @since 0.1.0
20
+ */
21
+ export type SignatureVerifier = (raw: RawInbound, credential: Redacted.Redacted<CredentialRef>) => Effect.Effect<void, Unauthorized>;
22
+ /**
23
+ * Configuration for a schema-declared webhook channel.
24
+ *
25
+ * @category models
26
+ * @since 0.1.0
27
+ */
28
+ export interface Config<A> {
29
+ readonly name: string;
30
+ readonly schema: Schema.Schema<A>;
31
+ readonly credential: Redacted.Redacted<CredentialRef>;
32
+ readonly fingerprintHeaders?: Channel<A>["fingerprintHeaders"];
33
+ readonly verify: SignatureVerifier;
34
+ readonly map: (payload: A) => Effect.Effect<InboundResult, InvalidInput>;
35
+ readonly project: Channel<A>["project"];
36
+ }
37
+ /**
38
+ * Largest webhook request body one mount reads by default, in bytes.
39
+ *
40
+ * A webhook is the one control-plane ingress a bearer holder reaches with an
41
+ * arbitrary payload, and `handler` used to materialize whatever arrived before
42
+ * anything looked at its size. The default is deliberately smaller than the
43
+ * 4 MiB mutation-identity budget in `internal/mutationBoundary.ts`: a body that
44
+ * cannot become a durable mutation is refused at the door rather than copied,
45
+ * decoded, and refused later.
46
+ *
47
+ * @category constants
48
+ * @since 1.0.0-rc.0
49
+ */
50
+ export declare const maximumBodyBytes: number;
51
+ /**
52
+ * Per-mount overrides for the webhook request handler.
53
+ *
54
+ * @category models
55
+ * @since 1.0.0-rc.0
56
+ */
57
+ export interface HandlerOptions {
58
+ /** Body ceiling for this mount. Defaults to `maximumBodyBytes`. */
59
+ readonly maximumBodyBytes?: number | undefined;
60
+ }
61
+ /**
62
+ * Builds a webhook channel. Verification receives raw bytes and headers before
63
+ * JSON parsing and schema decoding.
64
+ *
65
+ * @category constructors
66
+ * @since 0.1.0
67
+ */
68
+ export declare const make: <A>(config: Config<A>) => Channel<A>;
69
+ /**
70
+ * Reads an abstract Effect HTTP request and dispatches it through Channels.
71
+ * This is mountable by any Effect HTTP host; it contains no Node transport
72
+ * dependencies.
73
+ *
74
+ * The body is bounded twice. A `content-length` over the limit is refused
75
+ * before the body is read at all, so a declared flood costs nothing; the
76
+ * measured length is checked before retaining each streamed chunk. Reading
77
+ * stops at the first chunk exceeding the limit, including when the caller
78
+ * understates or omits the length. Both refusals are `InvalidInput` naming the
79
+ * two byte counts and no body content.
80
+ *
81
+ * @category handlers
82
+ * @since 0.1.0
83
+ */
84
+ export declare const handler: (channel: string, idempotencyKey: IdempotencyKey, options?: HandlerOptions | undefined) => Effect.Effect<{
85
+ readonly _tag: "Accepted";
86
+ readonly receiptId: string;
87
+ readonly runId?: string | undefined;
88
+ readonly inputConsumed?: boolean | undefined;
89
+ readonly inputBody?: string | undefined;
90
+ readonly handedTo?: {
91
+ readonly hostId: string;
92
+ readonly pid: number;
93
+ } | undefined;
94
+ } | {
95
+ readonly _tag: "AlreadyApplied";
96
+ readonly receiptId: string;
97
+ readonly runId?: string | undefined;
98
+ readonly inputConsumed?: boolean | undefined;
99
+ readonly inputBody?: string | undefined;
100
+ } | {
101
+ readonly _tag: "Parked";
102
+ readonly receiptId: string;
103
+ readonly planId: string;
104
+ readonly status: "waiting-approval";
105
+ } | {
106
+ readonly _tag: "Conflict";
107
+ readonly message: string;
108
+ } | {
109
+ readonly _tag: "Terminal";
110
+ readonly runId: string;
111
+ readonly status: "running" | "completed" | "failed" | "cancelled" | "accepted" | "parked" | "waiting-approval";
112
+ }, import("@smthrs/notifications/NotificationQueue").NotificationError | import("./ControlError.ts").RunNotFound | import("./ControlError.ts").PlanNotFound | import("./ControlError.ts").PlanDenied | import("./ControlError.ts").FlowNotFound | import("./ControlError.ts").PlanDigestMismatch | import("./ControlError.ts").EnvelopeMismatch | import("./ControlError.ts").ClaimLost | import("./ControlError.ts").CodeDrift | import("./ControlError.ts").AlreadyResolved | InvalidInput | Unauthorized | import("./ControlError.ts").Unavailable | import("./ControlError.ts").TransportError | import("./ControlError.ts").PersistenceError | import("./ControlError.ts").LaunchFailed | import("./ControlError.ts").NoMatchingWait | import("./ControlError.ts").CredentialConflict | import("effect/unstable/http/HttpServerError").HttpServerError, Channels | HttpServerRequest.HttpServerRequest>;
113
+ //# sourceMappingURL=WebhookChannel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebhookChannel.d.ts","sourceRoot":"","sources":["../../src/WebhookChannel.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AAC9D,OAAO,KAAK,iBAAiB,MAAM,wCAAwC,CAAA;AAC3E,OAAO,EAAE,KAAK,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAC3F,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,UAAU,EACf,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,KACzC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;AAEtC;;;;;GAKG;AACH,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAA;IAC9D,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAA;IAClC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;IACxE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;CACxC;AAID;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,QAAc,CAAA;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC/C;AAoBD;;;;;;GAMG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAmBpD,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,OAAO,GAClB,SAAS,MAAM,EACf,gBAAgB,cAAc,EAC9B,UAAU,cAAc,GAAG,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;42BA8BlC,CAAA"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Webhook channel construction over Effect's transport-neutral HTTP request.
3
+ *
4
+ * @since 0.1.0
5
+ */
6
+ import { Effect, Schema, Stream } from "effect";
7
+ import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";
8
+ import { Channels } from "./Channels.js";
9
+ import { InvalidInput } from "./ControlError.js";
10
+ const invalidInput = (issue) => new InvalidInput({ issue });
11
+ /**
12
+ * Largest webhook request body one mount reads by default, in bytes.
13
+ *
14
+ * A webhook is the one control-plane ingress a bearer holder reaches with an
15
+ * arbitrary payload, and `handler` used to materialize whatever arrived before
16
+ * anything looked at its size. The default is deliberately smaller than the
17
+ * 4 MiB mutation-identity budget in `internal/mutationBoundary.ts`: a body that
18
+ * cannot become a durable mutation is refused at the door rather than copied,
19
+ * decoded, and refused later.
20
+ *
21
+ * @category constants
22
+ * @since 1.0.0-rc.0
23
+ */
24
+ export const maximumBodyBytes = 1024 * 1024;
25
+ /**
26
+ * The declared body length, when the caller stated one this host can trust.
27
+ *
28
+ * Header names arrive lower-cased from Effect's HTTP layer, but a hand-built
29
+ * request is not obliged to, so the lookup folds case itself. An absent,
30
+ * non-numeric, fractional, or negative declaration is not evidence of
31
+ * anything: it falls through to the streaming measurement rather than being
32
+ * treated as zero.
33
+ */
34
+ const declaredLength = (headers) => {
35
+ for (const [name, value] of Object.entries(headers)) {
36
+ if (name.toLowerCase() !== "content-length" || value === undefined)
37
+ continue;
38
+ const parsed = Number(value.trim());
39
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : undefined;
40
+ }
41
+ return undefined;
42
+ };
43
+ /**
44
+ * Builds a webhook channel. Verification receives raw bytes and headers before
45
+ * JSON parsing and schema decoding.
46
+ *
47
+ * @category constructors
48
+ * @since 0.1.0
49
+ */
50
+ export const make = (config) => {
51
+ const decode = Schema.decodeUnknownEffect(config.schema);
52
+ return {
53
+ name: config.name,
54
+ schema: config.schema,
55
+ ...(config.fingerprintHeaders === undefined ? {} : { fingerprintHeaders: config.fingerprintHeaders }),
56
+ verify: Effect.fn("WebhookChannel.verify")((raw) => config.verify(raw, config.credential)),
57
+ decode: Effect.fn("WebhookChannel.decode")((raw) => Effect.try({
58
+ try: () => JSON.parse(new TextDecoder().decode(raw.body)),
59
+ catch: () => invalidInput("invalid webhook JSON")
60
+ }).pipe(Effect.flatMap((json) => decode(json)), Effect.mapError((cause) => cause instanceof InvalidInput ? cause : invalidInput(String(cause))))),
61
+ map: Effect.fn("WebhookChannel.map")((payload) => config.map(payload)),
62
+ project: config.project
63
+ };
64
+ };
65
+ /**
66
+ * Reads an abstract Effect HTTP request and dispatches it through Channels.
67
+ * This is mountable by any Effect HTTP host; it contains no Node transport
68
+ * dependencies.
69
+ *
70
+ * The body is bounded twice. A `content-length` over the limit is refused
71
+ * before the body is read at all, so a declared flood costs nothing; the
72
+ * measured length is checked before retaining each streamed chunk. Reading
73
+ * stops at the first chunk exceeding the limit, including when the caller
74
+ * understates or omits the length. Both refusals are `InvalidInput` naming the
75
+ * two byte counts and no body content.
76
+ *
77
+ * @category handlers
78
+ * @since 0.1.0
79
+ */
80
+ export const handler = (channel, idempotencyKey, options) => Effect.gen(function* () {
81
+ const limit = options?.maximumBodyBytes ?? maximumBodyBytes;
82
+ const request = yield* HttpServerRequest.HttpServerRequest;
83
+ const declared = declaredLength(request.headers);
84
+ if (declared !== undefined && declared > limit) {
85
+ return yield* invalidInput(`webhook body: declared ${declared} bytes exceeds the ${limit} byte limit`);
86
+ }
87
+ const chunks = [];
88
+ let size = 0;
89
+ yield* Stream.runForEach(request.stream, (chunk) => Effect.gen(function* () {
90
+ size += chunk.byteLength;
91
+ if (size > limit) {
92
+ return yield* invalidInput(`webhook body: ${size} bytes exceeds the ${limit} byte limit`);
93
+ }
94
+ if (chunk.byteLength > 0)
95
+ chunks.push(chunk.slice());
96
+ }));
97
+ const body = new Uint8Array(size);
98
+ let offset = 0;
99
+ for (const chunk of chunks) {
100
+ body.set(chunk, offset);
101
+ offset += chunk.byteLength;
102
+ }
103
+ const channels = yield* Channels;
104
+ return yield* channels.ingest({
105
+ channel,
106
+ raw: { body, headers: request.headers, idempotencyKey }
107
+ });
108
+ });
109
+ //# sourceMappingURL=WebhookChannel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebhookChannel.js","sourceRoot":"","sources":["../../src/WebhookChannel.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Webhook channel construction over Effect's transport-neutral HTTP request.\n *\n * @since 0.1.0\n */\n\nimport { Effect, type Redacted, Schema, Stream } from \"effect\"\nimport * as HttpServerRequest from \"effect/unstable/http/HttpServerRequest\"\nimport { type Channel, Channels, type InboundResult, type RawInbound } from \"./Channels.ts\"\nimport { InvalidInput, type Unauthorized } from \"./ControlError.ts\"\nimport type { IdempotencyKey } from \"./ControlSchema.ts\"\nimport type { CredentialRef } from \"./Credential.ts\"\n\n/**\n * Signature verifier injected by a webhook transport.\n *\n * The credential is intentionally a redacted reference. Resolution belongs at\n * the host adapter boundary, never in webhook input or its persisted record.\n *\n * @category models\n * @since 0.1.0\n */\nexport type SignatureVerifier = (\n raw: RawInbound,\n credential: Redacted.Redacted<CredentialRef>\n) => Effect.Effect<void, Unauthorized>\n\n/**\n * Configuration for a schema-declared webhook channel.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Config<A> {\n readonly name: string\n readonly schema: Schema.Schema<A>\n readonly credential: Redacted.Redacted<CredentialRef>\n readonly fingerprintHeaders?: Channel<A>[\"fingerprintHeaders\"]\n readonly verify: SignatureVerifier\n readonly map: (payload: A) => Effect.Effect<InboundResult, InvalidInput>\n readonly project: Channel<A>[\"project\"]\n}\n\nconst invalidInput = (issue: string): InvalidInput => new InvalidInput({ issue })\n\n/**\n * Largest webhook request body one mount reads by default, in bytes.\n *\n * A webhook is the one control-plane ingress a bearer holder reaches with an\n * arbitrary payload, and `handler` used to materialize whatever arrived before\n * anything looked at its size. The default is deliberately smaller than the\n * 4 MiB mutation-identity budget in `internal/mutationBoundary.ts`: a body that\n * cannot become a durable mutation is refused at the door rather than copied,\n * decoded, and refused later.\n *\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const maximumBodyBytes = 1024 * 1024\n\n/**\n * Per-mount overrides for the webhook request handler.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface HandlerOptions {\n /** Body ceiling for this mount. Defaults to `maximumBodyBytes`. */\n readonly maximumBodyBytes?: number | undefined\n}\n\n/**\n * The declared body length, when the caller stated one this host can trust.\n *\n * Header names arrive lower-cased from Effect's HTTP layer, but a hand-built\n * request is not obliged to, so the lookup folds case itself. An absent,\n * non-numeric, fractional, or negative declaration is not evidence of\n * anything: it falls through to the streaming measurement rather than being\n * treated as zero.\n */\nconst declaredLength = (headers: Readonly<Record<string, string | undefined>>): number | undefined => {\n for (const [name, value] of Object.entries(headers)) {\n if (name.toLowerCase() !== \"content-length\" || value === undefined) continue\n const parsed = Number(value.trim())\n return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : undefined\n }\n return undefined\n}\n\n/**\n * Builds a webhook channel. Verification receives raw bytes and headers before\n * JSON parsing and schema decoding.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = <A>(config: Config<A>): Channel<A> => {\n const decode = Schema.decodeUnknownEffect(config.schema)\n return {\n name: config.name,\n schema: config.schema,\n ...(config.fingerprintHeaders === undefined ? {} : { fingerprintHeaders: config.fingerprintHeaders }),\n verify: Effect.fn(\"WebhookChannel.verify\")((raw) => config.verify(raw, config.credential)),\n decode: Effect.fn(\"WebhookChannel.decode\")((raw) =>\n Effect.try({\n try: () => JSON.parse(new TextDecoder().decode(raw.body)),\n catch: () => invalidInput(\"invalid webhook JSON\")\n }).pipe(\n Effect.flatMap((json) => decode(json)),\n Effect.mapError((cause) => cause instanceof InvalidInput ? cause : invalidInput(String(cause)))\n ) as Effect.Effect<A, InvalidInput>\n ),\n map: Effect.fn(\"WebhookChannel.map\")((payload) => config.map(payload)),\n project: config.project\n }\n}\n\n/**\n * Reads an abstract Effect HTTP request and dispatches it through Channels.\n * This is mountable by any Effect HTTP host; it contains no Node transport\n * dependencies.\n *\n * The body is bounded twice. A `content-length` over the limit is refused\n * before the body is read at all, so a declared flood costs nothing; the\n * measured length is checked before retaining each streamed chunk. Reading\n * stops at the first chunk exceeding the limit, including when the caller\n * understates or omits the length. Both refusals are `InvalidInput` naming the\n * two byte counts and no body content.\n *\n * @category handlers\n * @since 0.1.0\n */\nexport const handler = (\n channel: string,\n idempotencyKey: IdempotencyKey,\n options?: HandlerOptions | undefined\n) =>\n Effect.gen(function*() {\n const limit = options?.maximumBodyBytes ?? maximumBodyBytes\n const request = yield* HttpServerRequest.HttpServerRequest\n const declared = declaredLength(request.headers)\n if (declared !== undefined && declared > limit) {\n return yield* invalidInput(`webhook body: declared ${declared} bytes exceeds the ${limit} byte limit`)\n }\n const chunks: Array<Uint8Array> = []\n let size = 0\n yield* Stream.runForEach(request.stream, (chunk) =>\n Effect.gen(function*() {\n size += chunk.byteLength\n if (size > limit) {\n return yield* invalidInput(`webhook body: ${size} bytes exceeds the ${limit} byte limit`)\n }\n if (chunk.byteLength > 0) chunks.push(chunk.slice())\n }))\n const body = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) {\n body.set(chunk, offset)\n offset += chunk.byteLength\n }\n const channels = yield* Channels\n return yield* channels.ingest({\n channel,\n raw: { body, headers: request.headers, idempotencyKey }\n })\n })\n"]}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Control-plane contracts and projections for the Smithers harness.
3
+ *
4
+ * This is the public surface of the control plane: the transport-independent
5
+ * `Control` service, the `ControlRuntime` port it writes through, the
6
+ * projections it reads back, and the durable adapter that binds both to a real
7
+ * database.
8
+ *
9
+ * `Control` is authority, not execution. Every mutation it accepts is
10
+ * idempotent, principal-stamped, and recorded in the journal beside the state
11
+ * change it caused, so "who asked for this, and when did it take effect?" is
12
+ * answered from persisted evidence rather than from a log line.
13
+ *
14
+ * @since 0.1.0
15
+ */
16
+ /**
17
+ * @category services
18
+ * @since 0.1.0
19
+ */
20
+ export * as Control from "./Control.ts";
21
+ /**
22
+ * @category services
23
+ * @since 1.0.0
24
+ */
25
+ export * as ApprovalAuthority from "./ApprovalAuthority.ts";
26
+ /**
27
+ * @category errors
28
+ * @since 0.1.0
29
+ */
30
+ export * as ControlError from "./ControlError.ts";
31
+ /**
32
+ * @category models
33
+ * @since 0.1.0
34
+ */
35
+ export * as ControlSchema from "./ControlSchema.ts";
36
+ /**
37
+ * @category projections
38
+ * @since 0.1.0
39
+ */
40
+ export * as Cancellation from "./Cancellation.ts";
41
+ /**
42
+ * @category projections
43
+ * @since 0.1.0
44
+ */
45
+ export * as Lineage from "./Lineage.ts";
46
+ /**
47
+ * @category projections
48
+ * @since 0.1.0
49
+ */
50
+ export * as Monitor from "./Monitor.ts";
51
+ /**
52
+ * @category projections
53
+ * @since 0.1.0
54
+ */
55
+ export * as Steering from "./Steering.ts";
56
+ /**
57
+ * @category services
58
+ * @since 0.1.0
59
+ */
60
+ export * as ControlRuntime from "./ControlRuntime.ts";
61
+ /**
62
+ * @category services
63
+ * @since 0.1.0
64
+ */
65
+ export * as ControlExecutor from "./ControlExecutor.ts";
66
+ /**
67
+ * @category services
68
+ * @since 1.0.0
69
+ */
70
+ export * as DispatchReader from "./DispatchReader.ts";
71
+ /**
72
+ * @category layers
73
+ * @since 0.1.0
74
+ */
75
+ export * as ControlLive from "./ControlLive.ts";
76
+ /**
77
+ * @category models
78
+ * @since 0.1.0
79
+ */
80
+ export * as SystemFlows from "./SystemFlows.ts";
81
+ /**
82
+ * @category rpc
83
+ * @since 0.1.0
84
+ */
85
+ export * as ControlRpcs from "./ControlRpcs.ts";
86
+ /**
87
+ * @category layers
88
+ * @since 0.1.0
89
+ */
90
+ export * as ControlServer from "./ControlServer.ts";
91
+ /**
92
+ * @category layers
93
+ * @since 0.1.0
94
+ */
95
+ export * as ControlClient from "./ControlClient.ts";
96
+ /**
97
+ * @category services
98
+ * @since 0.1.0
99
+ */
100
+ export * as Channels from "./Channels.ts";
101
+ /**
102
+ * @category channels
103
+ * @since 0.1.0
104
+ */
105
+ export * as WebhookChannel from "./WebhookChannel.ts";
106
+ /**
107
+ * @category services
108
+ * @since 0.1.0
109
+ */
110
+ export * as Credential from "./Credential.ts";
111
+ /**
112
+ * @category services
113
+ * @since 0.1.0
114
+ */
115
+ export * as CredentialCipher from "./CredentialCipher.ts";
116
+ /**
117
+ * @category services
118
+ * @since 0.1.0
119
+ */
120
+ export * as CredentialStore from "./CredentialStore.ts";
121
+ /**
122
+ * @category layers
123
+ * @since 0.1.0
124
+ */
125
+ export * as SqlCredentialStore from "./SqlCredentialStore.ts";
126
+ /**
127
+ * @category layers
128
+ * @since 0.1.0
129
+ */
130
+ export * as WebCryptoCipher from "./WebCryptoCipher.ts";
131
+ /**
132
+ * @category layers
133
+ * @since 0.1.0
134
+ */
135
+ export * as SqlControlRuntime from "./SqlControlRuntime.ts";
136
+ /**
137
+ * @category authentication
138
+ * @since 1.0.0
139
+ */
140
+ export * as ScopedToken from "./ScopedToken.ts";
141
+ /**
142
+ * @category migrations
143
+ * @since 0.1.0
144
+ */
145
+ export * as Migrations from "./Migrations.ts";
146
+ /**
147
+ * Observational health contracts.
148
+ * @category health
149
+ * @since 1.0.0
150
+ */
151
+ export * as Health from "./Health.ts";
152
+ /**
153
+ * The Jev-decided session checker a host binds as `jev.session`.
154
+ * @category health
155
+ * @since 1.0.0
156
+ */
157
+ export * as JevSessionChecker from "./JevSessionChecker.ts";
158
+ /** Versioned control lifecycle producer and projection contract. */
159
+ export * as ControlFacts from "./ControlFacts.ts";
160
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;GAGG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAEvC;;;GAGG;AACH,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D;;;GAGG;AACH,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA;AAEjD;;;GAGG;AACH,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAEnD;;;GAGG;AACH,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA;AAEjD;;;GAGG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAEvC;;;GAGG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAEvC;;;GAGG;AACH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAEzC;;;GAGG;AACH,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AAErD;;;GAGG;AACH,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAA;AAEvD;;;GAGG;AACH,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AAErD;;;GAGG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAE/C;;;GAGG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAE/C;;;GAGG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAE/C;;;GAGG;AACH,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAEnD;;;GAGG;AACH,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAEnD;;;GAGG;AACH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAEzC;;;GAGG;AACH,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AAErD;;;GAGG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAE7C;;;GAGG;AACH,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAA;AAEzD;;;GAGG;AACH,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAA;AAEvD;;;GAGG;AACH,OAAO,KAAK,kBAAkB,MAAM,yBAAyB,CAAA;AAE7D;;;GAGG;AACH,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAA;AAEvD;;;GAGG;AACH,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D;;;GAGG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAE/C;;;GAGG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAE7C;;;;GAIG;AACH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAErC;;;;GAIG;AACH,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D,oEAAoE;AACpE,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA"}