@smthrs/gateway 1.0.0-rc.5 → 1.0.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/cjs/GatewayRpcs.d.ts +2 -2
- package/dist/cjs/GatewayRpcs.d.ts.map +1 -1
- package/dist/cjs/GatewayRpcs.js +1 -1
- package/dist/cjs/GatewayRpcs.js.map +1 -1
- package/dist/cjs/GatewayServer.d.ts +4 -4
- package/dist/cjs/GatewayServer.d.ts.map +1 -1
- package/dist/cjs/GatewayServer.js +2 -2
- package/dist/cjs/GatewayServer.js.map +1 -1
- package/dist/cjs/HostBranchPresence.d.ts +2 -2
- package/dist/cjs/HostBranchPresence.d.ts.map +1 -1
- package/dist/cjs/HostBranchPresence.js +1 -1
- package/dist/cjs/HostBranchPresence.js.map +1 -1
- package/dist/cjs/RunTrace.d.ts +2 -2
- package/dist/cjs/RuntimeBridge.d.ts +5 -5
- package/dist/cjs/RuntimeBridge.d.ts.map +1 -1
- package/dist/cjs/RuntimeBridge.js +1 -1
- package/dist/cjs/RuntimeBridge.js.map +1 -1
- package/dist/cjs/bun/BunGateway.d.ts +1 -1
- package/dist/cjs/bun/BunGateway.d.ts.map +1 -1
- package/dist/cjs/bun/BunGateway.js.map +1 -1
- package/dist/cjs/internal/NativeGateway.d.ts +2 -2
- package/dist/cjs/internal/NativeGateway.d.ts.map +1 -1
- package/dist/cjs/internal/NativeGateway.js +2 -2
- package/dist/cjs/internal/NativeGateway.js.map +1 -1
- package/dist/cjs/node/NodeGateway.d.ts +1 -1
- package/dist/cjs/node/NodeGateway.d.ts.map +1 -1
- package/dist/esm/GatewayRpcs.d.ts +2 -2
- package/dist/esm/GatewayRpcs.d.ts.map +1 -1
- package/dist/esm/GatewayRpcs.js +1 -1
- package/dist/esm/GatewayRpcs.js.map +1 -1
- package/dist/esm/GatewayServer.d.ts +4 -4
- package/dist/esm/GatewayServer.d.ts.map +1 -1
- package/dist/esm/GatewayServer.js +3 -3
- package/dist/esm/GatewayServer.js.map +1 -1
- package/dist/esm/HostBranchPresence.d.ts +2 -2
- package/dist/esm/HostBranchPresence.d.ts.map +1 -1
- package/dist/esm/HostBranchPresence.js +1 -1
- package/dist/esm/HostBranchPresence.js.map +1 -1
- package/dist/esm/RunTrace.d.ts +2 -2
- package/dist/esm/RuntimeBridge.d.ts +5 -5
- package/dist/esm/RuntimeBridge.d.ts.map +1 -1
- package/dist/esm/RuntimeBridge.js +1 -1
- package/dist/esm/RuntimeBridge.js.map +1 -1
- package/dist/esm/bun/BunGateway.d.ts +1 -1
- package/dist/esm/bun/BunGateway.d.ts.map +1 -1
- package/dist/esm/bun/BunGateway.js.map +1 -1
- package/dist/esm/internal/NativeGateway.d.ts +2 -2
- package/dist/esm/internal/NativeGateway.d.ts.map +1 -1
- package/dist/esm/internal/NativeGateway.js +2 -2
- package/dist/esm/internal/NativeGateway.js.map +1 -1
- package/dist/esm/node/NodeGateway.d.ts +1 -1
- package/dist/esm/node/NodeGateway.d.ts.map +1 -1
- package/dist/vendor/adapters.json +952 -592
- package/dist/vendor/bun/BunAddressResolver.d.ts +15 -0
- package/dist/vendor/bun/BunAddressResolver.js +8 -0
- package/dist/vendor/bun/BunChildProcessSpawner.d.ts +1 -0
- package/dist/vendor/bun/BunChildProcessSpawner.js +6 -6
- package/dist/vendor/bun/BunClusterHttp.d.ts +13 -20
- package/dist/vendor/bun/BunClusterHttp.js +28 -28
- package/dist/vendor/bun/BunClusterSocket.d.ts +13 -8
- package/dist/vendor/bun/BunClusterSocket.js +9 -9
- package/dist/vendor/bun/BunCrypto.d.ts +1 -0
- package/dist/vendor/bun/BunCrypto.js +2 -2
- package/dist/vendor/bun/BunDatagramSocket.d.ts +227 -0
- package/dist/vendor/bun/BunDatagramSocket.js +11 -0
- package/dist/vendor/bun/BunDns.d.ts +72 -0
- package/dist/vendor/bun/BunDns.js +12 -0
- package/dist/vendor/bun/BunFileSystem.d.ts +1 -0
- package/dist/vendor/bun/BunFileSystem.js +3 -3
- package/dist/vendor/bun/BunHttpClient.d.ts +2 -1
- package/dist/vendor/bun/BunHttpClient.js +1 -1
- package/dist/vendor/bun/BunHttpPlatform.d.ts +2 -1
- package/dist/vendor/bun/BunHttpPlatform.js +5 -5
- package/dist/vendor/bun/BunHttpServer.d.ts +19 -10
- package/dist/vendor/bun/BunHttpServer.js +22 -22
- package/dist/vendor/bun/BunHttpServerRequest.d.ts +3 -1
- package/dist/vendor/bun/BunHttpServerRequest.js +1 -1
- package/dist/vendor/bun/BunMultipart.d.ts +4 -1
- package/dist/vendor/bun/BunMultipart.js +4 -4
- package/dist/vendor/bun/BunPath.d.ts +3 -0
- package/dist/vendor/bun/BunPath.js +2 -2
- package/dist/vendor/bun/BunRedis.d.ts +5 -1
- package/dist/vendor/bun/BunRedis.js +1 -1
- package/dist/vendor/bun/BunRuntime.d.ts +3 -0
- package/dist/vendor/bun/BunRuntime.js +2 -2
- package/dist/vendor/bun/BunServices.d.ts +4 -1
- package/dist/vendor/bun/BunServices.js +17 -17
- package/dist/vendor/bun/BunSink.d.ts +1 -0
- package/dist/vendor/bun/BunSink.js +2 -2
- package/dist/vendor/bun/BunSocket.d.ts +8 -3
- package/dist/vendor/bun/BunSocket.js +2 -2
- package/dist/vendor/bun/BunSocketServer.d.ts +1 -0
- package/dist/vendor/bun/BunSocketServer.js +3 -3
- package/dist/vendor/bun/BunStdio.d.ts +1 -0
- package/dist/vendor/bun/BunStdio.js +4 -4
- package/dist/vendor/bun/BunStream.d.ts +1 -0
- package/dist/vendor/bun/BunStream.js +3 -3
- package/dist/vendor/bun/BunTerminal.d.ts +6 -3
- package/dist/vendor/bun/BunTerminal.js +2 -2
- package/dist/vendor/bun/BunWorker.d.ts +3 -1
- package/dist/vendor/bun/BunWorker.js +1 -1
- package/dist/vendor/bun/BunWorkerRunner.d.ts +2 -1
- package/dist/vendor/bun/BunWorkerRunner.js +1 -1
- package/dist/vendor/bun/index.d.ts +38 -0
- package/dist/vendor/bun/index.js +63 -49
- package/dist/vendor/{chunk-GUPAV2YZ.js → chunk-27M5LSUN.js} +2 -2
- package/dist/vendor/{chunk-DZ2VOUDP.js → chunk-2BVPPQ3J.js} +27 -11
- package/dist/vendor/chunk-2IQO24DU.js +16 -0
- package/dist/vendor/{chunk-TFY7W6D4.js → chunk-2LNNNJEL.js} +1 -1
- package/dist/vendor/{chunk-ML2NAFH2.js → chunk-3VK56BXN.js} +5 -5
- package/dist/vendor/{chunk-HAEDVSSM.js → chunk-4FOGEXGP.js} +2 -2
- package/dist/vendor/{chunk-UHIDPUFF.js → chunk-4UBVC7DT.js} +7 -7
- package/dist/vendor/{chunk-QR557K66.js → chunk-6J5FUCQH.js} +2 -2
- package/dist/vendor/{chunk-2GCOZTTW.js → chunk-6PBGSFGC.js} +1 -1
- package/dist/vendor/{chunk-FUTCTCEG.js → chunk-6U5YBCZP.js} +2 -2
- package/dist/vendor/chunk-7GAMKC3O.js +374 -0
- package/dist/vendor/{chunk-XJWJXLXJ.js → chunk-A6MHC464.js} +2 -2
- package/dist/vendor/{chunk-V6LUGQ7O.js → chunk-AAGA43L5.js} +1 -1
- package/dist/vendor/chunk-AT3UQX35.js +43 -0
- package/dist/vendor/{chunk-HY7BBL3K.js → chunk-AXYO3S5F.js} +2 -2
- package/dist/vendor/chunk-BJ4Y76WR.js +20 -0
- package/dist/vendor/{chunk-WAII54GA.js → chunk-BKI6NSFH.js} +2 -2
- package/dist/vendor/{chunk-37CUR3PO.js → chunk-BMWOIEKS.js} +1 -1
- package/dist/vendor/{chunk-ZCHYLHJH.js → chunk-C3AAHJPG.js} +3 -4
- package/dist/vendor/{chunk-6C5H7S6B.js → chunk-C72RDTZK.js} +2 -2
- package/dist/vendor/{chunk-WECQDT36.js → chunk-CV6NGU7M.js} +12 -12
- package/dist/vendor/{chunk-XFEVTDVD.js → chunk-DIMYYXEQ.js} +2 -2
- package/dist/vendor/{chunk-TOHSFXMV.js → chunk-DK6W7NVZ.js} +71 -7
- package/dist/vendor/{chunk-DX4Z23DM.js → chunk-DKXPJGDE.js} +18 -18
- package/dist/vendor/{chunk-CES4OUM4.js → chunk-DLDCBZD5.js} +3 -3
- package/dist/vendor/{chunk-IFTTT6D5.js → chunk-EE65FWNU.js} +3 -3
- package/dist/vendor/{chunk-ULT6CWUI.js → chunk-ELNCZJWI.js} +2 -2
- package/dist/vendor/{chunk-BCK3NJOX.js → chunk-EUPZ62KV.js} +1 -1
- package/dist/vendor/chunk-F4CN7CQO.js +7 -0
- package/dist/vendor/{chunk-62R63L74.js → chunk-FI5AQ7TR.js} +17 -17
- package/dist/vendor/{chunk-3ARKKENZ.js → chunk-FUJO3TFS.js} +14 -8
- package/dist/vendor/{chunk-W4OASVX6.js → chunk-G6OHXUVC.js} +8 -7
- package/dist/vendor/{chunk-R4QUY7DH.js → chunk-GOLFR2OS.js} +2 -2
- package/dist/vendor/chunk-HYTFIYW5.js +16 -0
- package/dist/vendor/{chunk-Z7QPP4YH.js → chunk-IKEQPAB4.js} +2 -2
- package/dist/vendor/{chunk-H2Z2Y4QH.js → chunk-IPEZUWHG.js} +2 -2
- package/dist/vendor/{chunk-B4DHEMBB.js → chunk-JHJ5EULK.js} +1 -1
- package/dist/vendor/{chunk-TGXCX6W3.js → chunk-JOPLNUAM.js} +49 -29
- package/dist/vendor/{chunk-WVFOZ2UL.js → chunk-JTB5K4OJ.js} +1 -1
- package/dist/vendor/chunk-KE46F25E.js +274 -0
- package/dist/vendor/{chunk-Z6C7SO5Z.js → chunk-KQ6RGFAV.js} +34 -26
- package/dist/vendor/{chunk-6AQLBC2I.js → chunk-LR4CP5LM.js} +3 -3
- package/dist/vendor/chunk-LWMUYYMV.js +20 -0
- package/dist/vendor/{chunk-TGPIVY4A.js → chunk-MAY4VFMX.js} +3 -3
- package/dist/vendor/{chunk-XFZZYSJ4.js → chunk-MBWXCLXM.js} +2 -2
- package/dist/vendor/{chunk-RGECXXJS.js → chunk-MIICUBHU.js} +2 -2
- package/dist/vendor/{chunk-FPQF7CR5.js → chunk-NA54IH45.js} +11 -4
- package/dist/vendor/{chunk-XCYARUII.js → chunk-NLVEVV4N.js} +15 -15
- package/dist/vendor/{chunk-YDASSC75.js → chunk-OACTGVO3.js} +53 -33
- package/dist/vendor/{chunk-AVVVN75Z.js → chunk-OSTTDQDL.js} +55 -23
- package/dist/vendor/{chunk-XY3Q62EO.js → chunk-OTF7TIUT.js} +2 -2
- package/dist/vendor/{chunk-6UPBD47P.js → chunk-OUHKGVUY.js} +8 -8
- package/dist/vendor/{chunk-WHKMOC7Z.js → chunk-PFMOUFES.js} +2 -2
- package/dist/vendor/{chunk-OBWXQKVP.js → chunk-QSW2OXDQ.js} +6 -6
- package/dist/vendor/{chunk-UXBGYQR3.js → chunk-RGXARIVL.js} +2 -2
- package/dist/vendor/{chunk-3OSW7CT7.js → chunk-S2FQHQEN.js} +3 -2
- package/dist/vendor/{chunk-ZPIG2CUA.js → chunk-SIAD6ZM3.js} +9 -9
- package/dist/vendor/{chunk-X73E2UPI.js → chunk-SIHCCQG3.js} +2 -2
- package/dist/vendor/{chunk-LKCGQEE5.js → chunk-T2MZLWMC.js} +2 -2
- package/dist/vendor/chunk-TCB7NCNI.js +29 -0
- package/dist/vendor/{chunk-6DY5RBZD.js → chunk-TEBSASCB.js} +3 -3
- package/dist/vendor/{chunk-D64L6YND.js → chunk-THCEOHBC.js} +4 -3
- package/dist/vendor/{chunk-LIHLXXGQ.js → chunk-UFGZQOJD.js} +2 -2
- package/dist/vendor/{chunk-Z7QNPJEB.js → chunk-UFMF5EGC.js} +5 -5
- package/dist/vendor/{chunk-DS2R6WOM.js → chunk-VORW6EXI.js} +2 -2
- package/dist/vendor/{chunk-FOXN6JPJ.js → chunk-VRI5O4IJ.js} +1 -1
- package/dist/vendor/{chunk-KQDSNB7M.js → chunk-VRUFVTUD.js} +2 -2
- package/dist/vendor/{chunk-ZIQVYVMA.js → chunk-VYDIFBN4.js} +2 -2
- package/dist/vendor/chunk-WVSQBGVT.js +325 -0
- package/dist/vendor/{chunk-MSCLEMYX.js → chunk-ZMIMYPAY.js} +17 -17
- package/dist/vendor/cjs/bun/BunAddressResolver.d.ts +15 -0
- package/dist/vendor/cjs/bun/BunAddressResolver.js +1 -0
- package/dist/vendor/cjs/bun/BunChildProcessSpawner.d.ts +1 -0
- package/dist/vendor/cjs/bun/BunClusterHttp.d.ts +13 -20
- package/dist/vendor/cjs/bun/BunClusterSocket.d.ts +13 -8
- package/dist/vendor/cjs/bun/BunCrypto.d.ts +1 -0
- package/dist/vendor/cjs/bun/BunDatagramSocket.d.ts +227 -0
- package/dist/vendor/cjs/bun/BunDatagramSocket.js +1 -0
- package/dist/vendor/cjs/bun/BunDns.d.ts +72 -0
- package/dist/vendor/cjs/bun/BunDns.js +1 -0
- package/dist/vendor/cjs/bun/BunFileSystem.d.ts +1 -0
- package/dist/vendor/cjs/bun/BunHttpClient.d.ts +2 -1
- package/dist/vendor/cjs/bun/BunHttpPlatform.d.ts +2 -1
- package/dist/vendor/cjs/bun/BunHttpServer.d.ts +19 -10
- package/dist/vendor/cjs/bun/BunHttpServerRequest.d.ts +3 -1
- package/dist/vendor/cjs/bun/BunMultipart.d.ts +4 -1
- package/dist/vendor/cjs/bun/BunPath.d.ts +3 -0
- package/dist/vendor/cjs/bun/BunRedis.d.ts +5 -1
- package/dist/vendor/cjs/bun/BunRuntime.d.ts +3 -0
- package/dist/vendor/cjs/bun/BunServices.d.ts +4 -1
- package/dist/vendor/cjs/bun/BunSink.d.ts +1 -0
- package/dist/vendor/cjs/bun/BunSocket.d.ts +8 -3
- package/dist/vendor/cjs/bun/BunSocketServer.d.ts +1 -0
- package/dist/vendor/cjs/bun/BunStdio.d.ts +1 -0
- package/dist/vendor/cjs/bun/BunStream.d.ts +1 -0
- package/dist/vendor/cjs/bun/BunTerminal.d.ts +6 -3
- package/dist/vendor/cjs/bun/BunWorker.d.ts +3 -1
- package/dist/vendor/cjs/bun/BunWorkerRunner.d.ts +2 -1
- package/dist/vendor/cjs/bun/index.d.ts +38 -0
- package/dist/vendor/cjs/node/NodeAddressResolver.d.ts +16 -0
- package/dist/vendor/cjs/node/NodeAddressResolver.js +1 -0
- package/dist/vendor/cjs/node/NodeChildProcessSpawner.d.ts +1 -0
- package/dist/vendor/cjs/node/NodeClusterHttp.d.ts +13 -22
- package/dist/vendor/cjs/node/NodeClusterSocket.d.ts +14 -8
- package/dist/vendor/cjs/node/NodeCrypto.d.ts +1 -0
- package/dist/vendor/cjs/node/NodeDatagramSocket.d.ts +10 -0
- package/dist/vendor/cjs/node/NodeDatagramSocket.js +1 -0
- package/dist/vendor/cjs/node/NodeDns.d.ts +51 -0
- package/dist/vendor/cjs/node/NodeDns.js +1 -0
- package/dist/vendor/cjs/node/NodeFileSystem.d.ts +1 -0
- package/dist/vendor/cjs/node/NodeHttpClient.d.ts +21 -2
- package/dist/vendor/cjs/node/NodeHttpIncomingMessage.d.ts +5 -3
- package/dist/vendor/cjs/node/NodeHttpPlatform.d.ts +8 -6
- package/dist/vendor/cjs/node/NodeHttpServer.d.ts +21 -10
- package/dist/vendor/cjs/node/NodeHttpServerRequest.d.ts +4 -1
- package/dist/vendor/cjs/node/NodeMultipart.d.ts +5 -1
- package/dist/vendor/cjs/node/NodeMultipartParser.d.ts +9 -1
- package/dist/vendor/cjs/node/NodePath.d.ts +3 -0
- package/dist/vendor/cjs/node/NodeRedis.d.ts +5 -1
- package/dist/vendor/cjs/node/NodeRuntime.d.ts +3 -0
- package/dist/vendor/cjs/node/NodeServices.d.ts +4 -1
- package/dist/vendor/cjs/node/NodeSink.d.ts +1 -0
- package/dist/vendor/cjs/node/NodeSocket.d.ts +10 -3
- package/dist/vendor/cjs/node/NodeSocketServer.d.ts +1 -0
- package/dist/vendor/cjs/node/NodeStdio.d.ts +1 -0
- package/dist/vendor/cjs/node/NodeStream.d.ts +1 -0
- package/dist/vendor/cjs/node/NodeTerminal.d.ts +6 -3
- package/dist/vendor/cjs/node/NodeWorker.d.ts +3 -1
- package/dist/vendor/cjs/node/NodeWorkerRunner.d.ts +2 -1
- package/dist/vendor/cjs/node/Undici.d.ts +3 -0
- package/dist/vendor/cjs/node/index.d.ts +40 -0
- package/dist/vendor/cjs/node-shared/NodeAddressResolver.d.ts +13 -0
- package/dist/vendor/cjs/node-shared/NodeAddressResolver.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeChildProcessSpawner.d.ts +12 -3
- package/dist/vendor/cjs/node-shared/NodeClusterSocket.d.ts +17 -4
- package/dist/vendor/cjs/node-shared/NodeCrypto.d.ts +3 -0
- package/dist/vendor/cjs/node-shared/NodeDatagramSocket.d.ts +246 -0
- package/dist/vendor/cjs/node-shared/NodeDatagramSocket.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeDns.d.ts +221 -0
- package/dist/vendor/cjs/node-shared/NodeDns.js +1 -0
- package/dist/vendor/cjs/node-shared/NodeFileSystem.d.ts +1 -0
- package/dist/vendor/cjs/node-shared/NodeHttpCompression.d.ts +5 -1
- package/dist/vendor/cjs/node-shared/NodePath.d.ts +3 -0
- package/dist/vendor/cjs/node-shared/NodeRuntime.d.ts +4 -0
- package/dist/vendor/cjs/node-shared/NodeSink.d.ts +4 -0
- package/dist/vendor/cjs/node-shared/NodeSocket.d.ts +11 -1
- package/dist/vendor/cjs/node-shared/NodeSocketServer.d.ts +8 -1
- package/dist/vendor/cjs/node-shared/NodeStdio.d.ts +1 -0
- package/dist/vendor/cjs/node-shared/NodeStream.d.ts +15 -0
- package/dist/vendor/cjs/node-shared/NodeTerminal.d.ts +6 -4
- package/dist/vendor/cjs/node-shared/index.d.ts +29 -0
- package/dist/vendor/cjs/node-shared/internal/nodeChildProcessSpawner.d.ts +1 -1
- package/dist/vendor/cjs/platform-bun.d.ts +38 -0
- package/dist/vendor/cjs/platform-node-shared.d.ts +29 -0
- package/dist/vendor/cjs/platform-node.d.ts +40 -0
- package/dist/vendor/node/NodeAddressResolver.d.ts +16 -0
- package/dist/vendor/node/NodeAddressResolver.js +8 -0
- package/dist/vendor/node/NodeChildProcessSpawner.d.ts +1 -0
- package/dist/vendor/node/NodeChildProcessSpawner.js +6 -6
- package/dist/vendor/node/NodeClusterHttp.d.ts +13 -22
- package/dist/vendor/node/NodeClusterHttp.js +32 -32
- package/dist/vendor/node/NodeClusterSocket.d.ts +14 -8
- package/dist/vendor/node/NodeClusterSocket.js +15 -15
- package/dist/vendor/node/NodeCrypto.d.ts +1 -0
- package/dist/vendor/node/NodeCrypto.js +2 -2
- package/dist/vendor/node/NodeDatagramSocket.d.ts +10 -0
- package/dist/vendor/node/NodeDatagramSocket.js +12 -0
- package/dist/vendor/node/NodeDns.d.ts +51 -0
- package/dist/vendor/node/NodeDns.js +12 -0
- package/dist/vendor/node/NodeFileSystem.d.ts +1 -0
- package/dist/vendor/node/NodeFileSystem.js +3 -3
- package/dist/vendor/node/NodeHttpClient.d.ts +21 -2
- package/dist/vendor/node/NodeHttpClient.js +6 -6
- package/dist/vendor/node/NodeHttpIncomingMessage.d.ts +5 -3
- package/dist/vendor/node/NodeHttpIncomingMessage.js +4 -4
- package/dist/vendor/node/NodeHttpPlatform.d.ts +8 -6
- package/dist/vendor/node/NodeHttpPlatform.js +8 -8
- package/dist/vendor/node/NodeHttpServer.d.ts +21 -10
- package/dist/vendor/node/NodeHttpServer.js +26 -26
- package/dist/vendor/node/NodeHttpServerRequest.d.ts +4 -1
- package/dist/vendor/node/NodeHttpServerRequest.js +1 -1
- package/dist/vendor/node/NodeMultipart.d.ts +5 -1
- package/dist/vendor/node/NodeMultipart.js +5 -5
- package/dist/vendor/node/NodeMultipartParser.d.ts +9 -1
- package/dist/vendor/node/NodeMultipartParser.js +1 -1
- package/dist/vendor/node/NodePath.d.ts +3 -0
- package/dist/vendor/node/NodePath.js +2 -2
- package/dist/vendor/node/NodeRedis.d.ts +5 -1
- package/dist/vendor/node/NodeRedis.js +1 -1
- package/dist/vendor/node/NodeRuntime.d.ts +3 -0
- package/dist/vendor/node/NodeRuntime.js +2 -2
- package/dist/vendor/node/NodeServices.d.ts +4 -1
- package/dist/vendor/node/NodeServices.js +17 -17
- package/dist/vendor/node/NodeSink.d.ts +1 -0
- package/dist/vendor/node/NodeSink.js +2 -2
- package/dist/vendor/node/NodeSocket.d.ts +10 -3
- package/dist/vendor/node/NodeSocket.js +2 -2
- package/dist/vendor/node/NodeSocketServer.d.ts +1 -0
- package/dist/vendor/node/NodeSocketServer.js +3 -3
- package/dist/vendor/node/NodeStdio.d.ts +1 -0
- package/dist/vendor/node/NodeStdio.js +4 -4
- package/dist/vendor/node/NodeStream.d.ts +1 -0
- package/dist/vendor/node/NodeStream.js +3 -3
- package/dist/vendor/node/NodeTerminal.d.ts +6 -3
- package/dist/vendor/node/NodeTerminal.js +2 -2
- package/dist/vendor/node/NodeWorker.d.ts +3 -1
- package/dist/vendor/node/NodeWorker.js +1 -1
- package/dist/vendor/node/NodeWorkerRunner.d.ts +2 -1
- package/dist/vendor/node/NodeWorkerRunner.js +1 -1
- package/dist/vendor/node/Undici.d.ts +3 -0
- package/dist/vendor/node/Undici.js +1 -1
- package/dist/vendor/node/index.d.ts +40 -0
- package/dist/vendor/node/index.js +63 -48
- package/dist/vendor/node-shared/NodeAddressResolver.d.ts +13 -0
- package/dist/vendor/node-shared/NodeAddressResolver.js +7 -0
- package/dist/vendor/node-shared/NodeChildProcessSpawner.d.ts +12 -3
- package/dist/vendor/node-shared/NodeChildProcessSpawner.js +5 -5
- package/dist/vendor/node-shared/NodeClusterSocket.d.ts +17 -4
- package/dist/vendor/node-shared/NodeClusterSocket.js +3 -3
- package/dist/vendor/node-shared/NodeCrypto.d.ts +3 -0
- package/dist/vendor/node-shared/NodeCrypto.js +1 -1
- package/dist/vendor/node-shared/NodeDatagramSocket.d.ts +246 -0
- package/dist/vendor/node-shared/NodeDatagramSocket.js +11 -0
- package/dist/vendor/node-shared/NodeDns.d.ts +221 -0
- package/dist/vendor/node-shared/NodeDns.js +31 -0
- package/dist/vendor/node-shared/NodeFileSystem.d.ts +1 -0
- package/dist/vendor/node-shared/NodeFileSystem.js +2 -2
- package/dist/vendor/node-shared/NodeHttpCompression.d.ts +5 -1
- package/dist/vendor/node-shared/NodeHttpCompression.js +1 -1
- package/dist/vendor/node-shared/NodePath.d.ts +3 -0
- package/dist/vendor/node-shared/NodePath.js +1 -1
- package/dist/vendor/node-shared/NodeRuntime.d.ts +4 -0
- package/dist/vendor/node-shared/NodeRuntime.js +1 -1
- package/dist/vendor/node-shared/NodeSink.d.ts +4 -0
- package/dist/vendor/node-shared/NodeSink.js +1 -1
- package/dist/vendor/node-shared/NodeSocket.d.ts +11 -1
- package/dist/vendor/node-shared/NodeSocket.js +1 -1
- package/dist/vendor/node-shared/NodeSocketServer.d.ts +8 -1
- package/dist/vendor/node-shared/NodeSocketServer.js +2 -2
- package/dist/vendor/node-shared/NodeStdio.d.ts +1 -0
- package/dist/vendor/node-shared/NodeStdio.js +3 -3
- package/dist/vendor/node-shared/NodeStream.d.ts +15 -0
- package/dist/vendor/node-shared/NodeStream.js +2 -2
- package/dist/vendor/node-shared/NodeTerminal.d.ts +6 -4
- package/dist/vendor/node-shared/NodeTerminal.js +1 -1
- package/dist/vendor/node-shared/index.d.ts +29 -0
- package/dist/vendor/node-shared/index.js +34 -22
- package/dist/vendor/node-shared/internal/nodeChildProcessSpawner.d.ts +1 -1
- package/dist/vendor/node-shared/internal/nodeChildProcessSpawner.js +1 -1
- package/dist/vendor/node-shared/internal/utils.js +1 -1
- package/dist/vendor/platform-bun.d.ts +38 -0
- package/dist/vendor/platform-bun.js +63 -49
- package/dist/vendor/platform-node-shared.d.ts +29 -0
- package/dist/vendor/platform-node-shared.js +34 -22
- package/dist/vendor/platform-node.d.ts +40 -0
- package/dist/vendor/platform-node.js +63 -48
- package/docs/api.md +1 -1
- package/docs/concepts/trust-boundary.md +1 -1
- package/docs/guides/host-the-gateway.md +1 -1
- package/docs/guides/testing.md +3 -3
- package/docs/installation.md +3 -3
- package/docs/troubleshooting.md +3 -0
- package/package.json +22 -22
- package/src/GatewayRpcs.ts +1 -1
- package/src/GatewayServer.ts +3 -3
- package/src/HostBranchPresence.ts +1 -1
- package/src/RuntimeBridge.ts +1 -1
- package/src/bun/BunGateway.ts +2 -2
- package/src/internal/NativeGateway.ts +4 -4
- package/dist/vendor/chunk-3NV2UDBI.js +0 -7
- /package/dist/vendor/{chunk-BFBIHJ6X.js → chunk-2GF47BML.js} +0 -0
- /package/dist/vendor/{chunk-MH2J3BYB.js → chunk-BYFUYMRZ.js} +0 -0
- /package/dist/vendor/{chunk-MLFIZ5O2.js → chunk-ZUPIQLXU.js} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @smthrs/gateway
|
|
2
2
|
|
|
3
|
-
The gateway requires `effect`
|
|
3
|
+
The gateway requires `effect` in the range `^4.0.4` as its only peer. Node and Bun adapters are bundled privately, built from 4.0.4; hosts need no separate adapter install.
|
|
4
4
|
|
|
5
5
|
Release candidate scope, host requirements and compatibility review are defined in the [library support policy](https://github.com/smithersai/smithers/blob/main/RELEASE_SUPPORT.md).
|
|
6
6
|
|
|
@@ -16,10 +16,10 @@ The mounts, the bind and credential policy, the projections and their rows, the
|
|
|
16
16
|
|
|
17
17
|
Not on npm yet; see [Installation](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/flow/docs/installation.md#use-the-libraries).
|
|
18
18
|
|
|
19
|
-
Node 26.4.0 or later is required. `effect
|
|
19
|
+
Node 26.4.0 or later is required. `effect@^4.0.4` is a required peer.
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
pnpm add @smthrs/gateway effect
|
|
22
|
+
pnpm add @smthrs/gateway effect
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Public API
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { ControlError, ControlRpcs } from "@smthrs/control";
|
|
17
17
|
import { Schema } from "effect";
|
|
18
|
-
import { Rpc, RpcGroup } from "effect/
|
|
18
|
+
import { Rpc, RpcGroup } from "effect/rpc";
|
|
19
19
|
import { GatewayError } from "./GatewayError.ts";
|
|
20
20
|
import * as RunHistory from "./RunHistory.ts";
|
|
21
21
|
/**
|
|
@@ -720,7 +720,7 @@ export declare const GatewayRpcs: RpcGroup.RpcGroup<Rpc.Rpc<"Projection.Snapshot
|
|
|
720
720
|
readonly value: Schema.Int;
|
|
721
721
|
readonly offset: Schema.Int;
|
|
722
722
|
}>>;
|
|
723
|
-
}>, import("effect/
|
|
723
|
+
}>, import("effect/rpc/RpcSchema").Stream<Schema.Union<readonly [Schema.TaggedStruct<"snapshot-start", {
|
|
724
724
|
readonly selector: Schema.Union<readonly [Schema.TaggedStruct<"workspace-runs", {
|
|
725
725
|
readonly flowId: Schema.optionalKey<Schema.NonEmptyString>;
|
|
726
726
|
}>, Schema.TaggedStruct<"run-summary", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GatewayRpcs.d.ts","sourceRoot":"","sources":["../../src/GatewayRpcs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAiB,MAAM,iBAAiB,CAAA;AAC1E,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"GatewayRpcs.d.ts","sourceRoot":"","sources":["../../src/GatewayRpcs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAiB,MAAM,iBAAiB,CAAA;AAC1E,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,+CAAuC,CAAA;AAE5D;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB;;IAG9B;;;;;;;;;;;;OAYG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEH,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,mBAAmB,CAAC,IAAI,CAAA;AAEjE;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;EAE/B,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,OAAO,oBAAoB,CAAC,IAAI,CAAA;AAoCnE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA1FtB;;;;;;;;;;;;OAYG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yKAsHgC,CAAA"}
|
package/dist/cjs/GatewayRpcs.js
CHANGED
|
@@ -36,7 +36,7 @@ __export(GatewayRpcs_exports, {
|
|
|
36
36
|
module.exports = __toCommonJS(GatewayRpcs_exports);
|
|
37
37
|
var import_control = require("@smthrs/control");
|
|
38
38
|
var import_effect = require("effect");
|
|
39
|
-
var import_rpc = require("effect/
|
|
39
|
+
var import_rpc = require("effect/rpc");
|
|
40
40
|
var import_GatewayError = require("./GatewayError.js");
|
|
41
41
|
var GatewaySchema = __toESM(require("./GatewaySchema.js"), 1);
|
|
42
42
|
var RunHistory = __toESM(require("./RunHistory.js"), 1);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/GatewayRpcs.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * The gateway's own remote procedures: the served read path, and the one\n * composite mutation a product client cannot assemble safely on its own.\n *\n * Control mutations are not re-declared here. `@smthrs/control` `ControlRpcs`\n * is the mutation contract and the gateway mounts it unchanged at `/rpc`, so\n * there is exactly one wire definition of `Plan`, `Run`, `Approve`, `Deny`,\n * `Cancel`, `Signal`, `Steer`, `Resume`, `List`, and `Watch`.\n *\n * The group shares `ControlRpcs.ControlAuth`, so one bearer credential\n * authenticates both mounts and one server-stamped principal is recorded for\n * whatever it authorizes.\n *\n * @since 1.0.0\n */\n\nimport { ControlError, ControlRpcs, ControlSchema } from \"@smthrs/control\"\nimport { Schema } from \"effect\"\nimport { Rpc, RpcGroup } from \"effect/
|
|
4
|
+
"sourcesContent": ["/**\n * The gateway's own remote procedures: the served read path, and the one\n * composite mutation a product client cannot assemble safely on its own.\n *\n * Control mutations are not re-declared here. `@smthrs/control` `ControlRpcs`\n * is the mutation contract and the gateway mounts it unchanged at `/rpc`, so\n * there is exactly one wire definition of `Plan`, `Run`, `Approve`, `Deny`,\n * `Cancel`, `Signal`, `Steer`, `Resume`, `List`, and `Watch`.\n *\n * The group shares `ControlRpcs.ControlAuth`, so one bearer credential\n * authenticates both mounts and one server-stamped principal is recorded for\n * whatever it authorizes.\n *\n * @since 1.0.0\n */\n\nimport { ControlError, ControlRpcs, ControlSchema } from \"@smthrs/control\"\nimport { Schema } from \"effect\"\nimport { Rpc, RpcGroup } from \"effect/rpc\"\nimport { GatewayError } from \"./GatewayError.ts\"\nimport * as GatewaySchema from \"./GatewaySchema.ts\"\nimport * as RunHistory from \"./RunHistory.ts\"\n\n/**\n * The decision a client submits for one approval.\n *\n * @since 1.0.0\n * @category models\n */\nexport const Decision = Schema.Literals([\"approve\", \"deny\"])\n\n/**\n * The decision a client submits for one approval.\n *\n * @since 1.0.0\n * @category models\n */\nexport type Decision = typeof Decision.Type\n\n/**\n * One approval decision, submitted with the exact payload the run published.\n *\n * @since 1.0.0\n * @category models\n */\nexport const SubmitApprovalInput = Schema.Struct({\n ...ControlSchema.ApprovalPayload.fields,\n decision: Decision,\n /**\n * The answer to a question, for a gate that asks one.\n *\n * Most gates are a grant: approve or deny, and the decision IS the payload.\n * A `HumanTask` gate asks for a value \u2014 prose, a choice, a JSON object \u2014 and\n * the decision alone answers nothing. Present exactly when the row being\n * submitted is one of those, in which case `target.requestId` names the wait\n * point and this is what is delivered to it. A denial carries no answer: the\n * question is refused, not answered.\n *\n * Additive on purpose: a client that never asks a person anything submits\n * exactly what it submitted before.\n */\n answer: Schema.optional(Schema.Json)\n})\n\n/**\n * One approval decision, submitted with the exact payload the run published.\n *\n * @since 1.0.0\n * @category models\n */\nexport type SubmitApprovalInput = typeof SubmitApprovalInput.Type\n\n/**\n * What submitting an approval did.\n *\n * `decision` is the receipt for the grant or refusal. Control owns the\n * decision and its durable resume delegation as one domain command.\n *\n * @since 1.0.0\n * @category models\n */\nexport const SubmitApprovalOutput = Schema.Struct({\n decision: ControlSchema.Receipt\n})\n\n/**\n * What submitting an approval did.\n *\n * @since 1.0.0\n * @category models\n */\nexport type SubmitApprovalOutput = typeof SubmitApprovalOutput.Type\n\n/**\n * The failures `Approval.Submit` can answer with: exactly the union\n * `@smthrs/control` `Control.approve`, `Control.deny`, and \u2014 for a gate that\n * asks a question \u2014 `Control.signal` declare, because the handler is a\n * transport adapter over those commands and adds no failure of its own. A\n * member none of them raises would be a recovery branch no client's code could\n * ever reach.\n *\n * `NoMatchingWait` is the one an answer adds: the question was answered after\n * the run moved on, or somebody else answered it first. `CodeDrift` is a node\n * decision on a run whose flow changed since it started (#1807).\n */\nconst submitErrors = Schema.Union([\n ControlError.NoMatchingWait,\n ControlError.CodeDrift,\n ControlError.PlanDigestMismatch,\n ControlError.EnvelopeMismatch,\n ControlError.AlreadyResolved,\n ControlError.PlanNotFound,\n ControlError.RunNotFound,\n ControlError.InvalidInput,\n ControlError.Unauthorized,\n ControlError.PersistenceError,\n ControlError.Unavailable,\n ControlError.TransportError\n])\n\n/**\n * The failures `Run.Fork` and `Run.Verify` answer with: the history host's own\n * refusal, `Unauthorized` for an anonymous loopback caller, or `Unavailable`\n * from a gateway composed without a history host.\n */\nconst historyErrors = Schema.Union([RunHistory.HistoryRefused, ControlError.Unauthorized, ControlError.Unavailable])\n\n/**\n * The gateway read path, the composite approval mutation, and the run\n * history procedures.\n *\n * `Approval.Submit` is the transport form of Control's single decision\n * command. Control records the decision and durable resume delegation; the\n * gateway never composes a second mutation.\n *\n * @since 1.0.0\n * @category groups\n */\nexport const GatewayRpcs = RpcGroup.make(\n Rpc.make(\"Projection.Snapshot\", {\n defect: ControlRpcs.ControlDefect,\n payload: Schema.Struct({\n selector: GatewaySchema.ProjectionSelector,\n after: Schema.optional(GatewaySchema.ProjectionCursor)\n }),\n success: GatewaySchema.ProjectionSnapshot,\n error: GatewayError\n }),\n Rpc.make(\"Projection.Subscribe\", {\n defect: ControlRpcs.ControlDefect,\n payload: Schema.Struct({\n selector: GatewaySchema.ProjectionSelector,\n after: Schema.optional(GatewaySchema.ProjectionCursor)\n }),\n success: GatewaySchema.GatewayFrame,\n error: GatewayError,\n stream: true\n }),\n Rpc.make(\"Approval.Submit\", {\n defect: ControlRpcs.ControlDefect,\n payload: SubmitApprovalInput,\n success: SubmitApprovalOutput,\n error: submitErrors\n }),\n /* Branches a run at a recorded frame into a parked child, as `smthrs runs fork`. */\n Rpc.make(\"Run.Fork\", {\n defect: ControlRpcs.ControlDefect,\n payload: RunHistory.ForkInput,\n success: RunHistory.ForkOutput,\n error: historyErrors\n }),\n /* Reports what a resume under the current flow code would replay, as `smthrs runs verify`. */\n Rpc.make(\"Run.Verify\", {\n defect: ControlRpcs.ControlDefect,\n payload: RunHistory.VerifyInput,\n success: RunHistory.VerifyReport,\n error: historyErrors\n })\n).middleware(ControlRpcs.ControlAuth)\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,qBAAyD;AACzD,oBAAuB;AACvB,iBAA8B;AAC9B,0BAA6B;AAC7B,oBAA+B;AAC/B,iBAA4B;AAQrB,MAAM,WAAW,qBAAO,SAAS,CAAC,WAAW,MAAM,CAAC;AAgBpD,MAAM,sBAAsB,qBAAO,OAAO;AAAA,EAC/C,GAAG,6BAAc,gBAAgB;AAAA,EACjC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcV,QAAQ,qBAAO,SAAS,qBAAO,IAAI;AACrC,CAAC;AAmBM,MAAM,uBAAuB,qBAAO,OAAO;AAAA,EAChD,UAAU,6BAAc;AAC1B,CAAC;AAsBD,MAAM,eAAe,qBAAO,MAAM;AAAA,EAChC,4BAAa;AAAA,EACb,4BAAa;AAAA,EACb,4BAAa;AAAA,EACb,4BAAa;AAAA,EACb,4BAAa;AAAA,EACb,4BAAa;AAAA,EACb,4BAAa;AAAA,EACb,4BAAa;AAAA,EACb,4BAAa;AAAA,EACb,4BAAa;AAAA,EACb,4BAAa;AAAA,EACb,4BAAa;AACf,CAAC;AAOD,MAAM,gBAAgB,qBAAO,MAAM,CAAC,WAAW,gBAAgB,4BAAa,cAAc,4BAAa,WAAW,CAAC;AAa5G,MAAM,cAAc,oBAAS;AAAA,EAClC,eAAI,KAAK,uBAAuB;AAAA,IAC9B,QAAQ,2BAAY;AAAA,IACpB,SAAS,qBAAO,OAAO;AAAA,MACrB,UAAU,cAAc;AAAA,MACxB,OAAO,qBAAO,SAAS,cAAc,gBAAgB;AAAA,IACvD,CAAC;AAAA,IACD,SAAS,cAAc;AAAA,IACvB,OAAO;AAAA,EACT,CAAC;AAAA,EACD,eAAI,KAAK,wBAAwB;AAAA,IAC/B,QAAQ,2BAAY;AAAA,IACpB,SAAS,qBAAO,OAAO;AAAA,MACrB,UAAU,cAAc;AAAA,MACxB,OAAO,qBAAO,SAAS,cAAc,gBAAgB;AAAA,IACvD,CAAC;AAAA,IACD,SAAS,cAAc;AAAA,IACvB,OAAO;AAAA,IACP,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,eAAI,KAAK,mBAAmB;AAAA,IAC1B,QAAQ,2BAAY;AAAA,IACpB,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO;AAAA,EACT,CAAC;AAAA;AAAA,EAED,eAAI,KAAK,YAAY;AAAA,IACnB,QAAQ,2BAAY;AAAA,IACpB,SAAS,WAAW;AAAA,IACpB,SAAS,WAAW;AAAA,IACpB,OAAO;AAAA,EACT,CAAC;AAAA;AAAA,EAED,eAAI,KAAK,cAAc;AAAA,IACrB,QAAQ,2BAAY;AAAA,IACpB,SAAS,WAAW;AAAA,IACpB,SAAS,WAAW;AAAA,IACpB,OAAO;AAAA,EACT,CAAC;AACH,EAAE,WAAW,2BAAY,WAAW;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
import { Control } from "@smthrs/control/Control";
|
|
28
28
|
import * as SyncServer from "@smthrs/sync/SyncServer";
|
|
29
29
|
import { Effect, Layer, Schema } from "effect";
|
|
30
|
-
import { HttpRouter } from "effect/
|
|
31
|
-
import { RpcSerialization, RpcServer } from "effect/
|
|
30
|
+
import { HttpRouter } from "effect/http";
|
|
31
|
+
import { RpcSerialization, RpcServer } from "effect/rpc";
|
|
32
32
|
import { GatewayError } from "./GatewayError.ts";
|
|
33
33
|
import { Projections } from "./Projections.ts";
|
|
34
34
|
import * as RuntimeBridge from "./RuntimeBridge.ts";
|
|
@@ -73,7 +73,7 @@ export declare const layerHealth: (health: Health) => Layer.Layer<never, never,
|
|
|
73
73
|
* @since 1.0.0
|
|
74
74
|
* @category layers
|
|
75
75
|
*/
|
|
76
|
-
export declare const layerHandlers: Layer.Layer<import("effect/
|
|
76
|
+
export declare const layerHandlers: Layer.Layer<import("effect/rpc/Rpc").Handler<"Projection.Snapshot"> | import("effect/rpc/Rpc").Handler<"Projection.Subscribe"> | import("effect/rpc/Rpc").Handler<"Approval.Submit"> | import("effect/rpc/Rpc").Handler<"Run.Fork"> | import("effect/rpc/Rpc").Handler<"Run.Verify">, never, Control | Projections>;
|
|
77
77
|
/**
|
|
78
78
|
* The kind a `Watch` keepalive carries.
|
|
79
79
|
*
|
|
@@ -371,7 +371,7 @@ export declare const carriesRpcRequest: (serialization: RpcSerialization.RpcSeri
|
|
|
371
371
|
* Enforces local Host/Origin policy and edge authentication before reading a
|
|
372
372
|
* body or upgrading a socket, then validates RPC body size and framing.
|
|
373
373
|
*
|
|
374
|
-
* `effect/
|
|
374
|
+
* `effect/rpc` hands every decoded message to the server loop, and a
|
|
375
375
|
* body that decodes to something else — `{}`, `[]`, prose, nothing at all —
|
|
376
376
|
* reached it as a message with no tag and died there, so the gateway answered
|
|
377
377
|
* `500 Internal Server Error` with an empty body (release validation). That is the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GatewayServer.d.ts","sourceRoot":"","sources":["../../src/GatewayServer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AAOjD,OAAO,KAAK,UAAU,MAAM,yBAAyB,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAU,MAAM,EAAsB,MAAM,QAAQ,CAAA;AAE1E,OAAO,EAAW,UAAU,EAAyC,MAAM,
|
|
1
|
+
{"version":3,"file":"GatewayServer.d.ts","sourceRoot":"","sources":["../../src/GatewayServer.ts"],"names":[],"mappings":"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"}
|
|
@@ -64,8 +64,8 @@ var import_SyncRpcs = require("@smthrs/sync/SyncRpcs");
|
|
|
64
64
|
var SyncServer = __toESM(require("@smthrs/sync/SyncServer"), 1);
|
|
65
65
|
var import_effect = require("effect");
|
|
66
66
|
var ByteSize = __toESM(require("effect/ByteSize"), 1);
|
|
67
|
-
var import_http = require("effect/
|
|
68
|
-
var import_rpc = require("effect/
|
|
67
|
+
var import_http = require("effect/http");
|
|
68
|
+
var import_rpc = require("effect/rpc");
|
|
69
69
|
var import_GatewayError = require("./GatewayError.js");
|
|
70
70
|
var import_GatewayRpcs = require("./GatewayRpcs.js");
|
|
71
71
|
var GatewaySchema = __toESM(require("./GatewaySchema.js"), 1);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/GatewayServer.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * The assembled workspace gateway: one HTTP surface carrying the control\n * plane, the sync read path, the served projections, and a health probe.\n *\n * Mounts:\n *\n * | Path | Protocol | Serves |\n * | --- | --- | --- |\n * | `POST /rpc` | RPC over HTTP | `@smthrs/control` `ControlRpcs` |\n * | `/rpc/ws` | RPC over WebSocket | `ControlRpcs`, including a kept-alive `Watch` |\n * | `POST /projections` | RPC over HTTP | `GatewayRpcs` |\n * | `/projections/ws` | RPC over WebSocket | `GatewayRpcs`, including `Projection.Subscribe` |\n * | `POST /sync` | RPC over HTTP | `@smthrs/sync` `SyncRpcs` |\n * | `/sync/ws` | RPC over WebSocket | `SyncRpcs` |\n * | `GET /health` | JSON | `GatewaySchema.GatewayHealth` plus the package version |\n * | `POST /auth/ticket` | JSON | A single-use {@link Ticket} that opens one protected socket |\n *\n * `/health` is deliberately unauthenticated: a supervisor decides whether to\n * keep or replace a gateway process by asking which workspace it belongs to,\n * and a probe that needed a credential could not answer that question about a\n * gateway it did not start. The response carries identity only: the\n * workspace hash, the gateway id, the protocol version, and the package\n * version. It never carries a token, a run, or a path.\n *\n * @since 1.0.0\n */\n\nimport { Control } from \"@smthrs/control/Control\"\nimport * as ControlError from \"@smthrs/control/ControlError\"\nimport * as ControlExecutor from \"@smthrs/control/ControlExecutor\"\nimport { ControlPrincipal } from \"@smthrs/control/ControlRpcs\"\nimport type * as ControlSchema from \"@smthrs/control/ControlSchema\"\nimport * as ControlServer from \"@smthrs/control/ControlServer\"\nimport { SyncRpcs } from \"@smthrs/sync/SyncRpcs\"\nimport * as SyncServer from \"@smthrs/sync/SyncServer\"\nimport { Effect, Layer, Option, Schema, Stream, type Types } from \"effect\"\nimport * as ByteSize from \"effect/ByteSize\"\nimport { Headers, HttpRouter, HttpServerRequest, HttpServerResponse } from \"effect/unstable/http\"\nimport { RpcSerialization, RpcServer } from \"effect/unstable/rpc\"\nimport { GatewayError, settingRefusal } from \"./GatewayError.ts\"\nimport { GatewayRpcs } from \"./GatewayRpcs.ts\"\nimport * as GatewaySchema from \"./GatewaySchema.ts\"\nimport * as HostBranchPresence from \"./HostBranchPresence.ts\"\nimport { heartbeatIntervalMillis, Projections } from \"./Projections.ts\"\nimport * as RunHistory from \"./RunHistory.ts\"\nimport * as RuntimeBridge from \"./RuntimeBridge.ts\"\n\n/**\n * What `GET /health` answers: the `GatewaySchema.GatewayHealth` identity plus\n * the version of the package serving it.\n *\n * @since 1.0.0\n * @category models\n */\nexport const Health = Schema.Struct({\n ...GatewaySchema.GatewayHealth.fields,\n version: Schema.String,\n runtimeBridge: Schema.optional(RuntimeBridge.Identity)\n})\n\n/**\n * What `GET /health` answers.\n *\n * @since 1.0.0\n * @category models\n */\nexport type Health = typeof Health.Type\n\nconst encodeHealth = Schema.encodeUnknownSync(Health)\n\n/**\n * Mounts the unauthenticated health probe.\n *\n * @param health the identity this process serves under\n * @since 1.0.0\n * @category layers\n */\nexport const layerHealth = (health: Health): Layer.Layer<never, never, HttpRouter.HttpRouter> =>\n HttpRouter.add(\"GET\", \"/health\", HttpServerResponse.jsonUnsafe(encodeHealth(health)))\n\n/**\n * The gateway's own RPC handlers over the read path and the control plane.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerHandlers = GatewayRpcs.toLayer(\n Effect.gen(function*() {\n const projections = yield* Projections\n const control = yield* Control\n // Only a host that owns the project's stores can fork or verify its runs.\n const history = yield* Effect.serviceOption(RunHistory.RunHistory)\n const historyHost = Option.match(history, {\n onNone: () => Effect.fail(new ControlError.Unavailable({ feature: \"run history\", ticket: \"smithers#3242\" })),\n onSome: Effect.succeed\n })\n // Forking writes a durable run and verifying drives the project's flow\n // code, so neither is an anonymous loopback read.\n const operator = Effect.gen(function*() {\n const principal = yield* ControlPrincipal\n if (principal.id === \"loopback\" && principal.kind === \"anonymous\") {\n return yield* new ControlError.Unauthorized({ message: \"An operator credential is required\" })\n }\n })\n return GatewayRpcs.of({\n \"Projection.Snapshot\": Effect.fn(\"Gateway.snapshot\")(\n ({ selector, after }) => projections.snapshot(selector, after),\n Effect.tapCause(ControlServer.logDefect)\n ),\n \"Projection.Subscribe\": ({ after, selector }) =>\n Stream.tapCause(projections.subscribe(selector, after), ControlServer.logDefect),\n /* One operator command. Control owns the atomic decision plus durable\n * resume delegation; the gateway is only a transport adapter. */\n \"Approval.Submit\": Effect.fn(\"Gateway.submitApproval\")((input) =>\n Effect.gen(function*() {\n // The identity the shared `ControlAuth` middleware authenticated, as\n // `ControlServer` stamps it on `Approve` and `Deny`. This mount is\n // the same decision under a different payload, so it is answerable\n // to the same operator, and a decision journaled under the\n // composition's default operator names the wrong one.\n const principal = yield* ControlPrincipal\n // Answering a human wait uses signal rather than the approval policy.\n // Do not let that alternate mutation path bypass loopback authentication.\n if (principal.id === \"loopback\" && principal.kind === \"anonymous\") {\n return yield* new ControlError.Unauthorized({ message: \"An operator credential is required\" })\n }\n const payload = {\n target: input.target,\n scope: input.scope,\n idempotencyKey: input.idempotencyKey,\n principal\n }\n // A gate that asked a question is answered, not granted, and the\n // PAYLOAD says which kind it is: a human wait carries its durable\n // wait token as the target's digest, and nothing else does. Reading\n // it here rather than trusting the client to have sent `answer` is\n // the difference between a clear refusal and the one an operator\n // actually saw \u2014 a client that forgot the answer fell through to\n // `Control.approve`, which looked for a registered approval token,\n // found none, and reported `/control/RunNotFound` for a run that was\n // listed, rendered, and waiting (workspace 4bb93306, run-1).\n //\n // `target.runId` is the run the person opened and `wait.name` the\n // wait point's own name; `Control.signal` routes the pair into the\n // run tree, to whichever execution is holding it.\n const wait = ControlExecutor.answerableWait(input.target)\n if (wait !== undefined && input.target._tag === \"Node\") {\n // Neither half of a grant answers a question. A denial has nowhere\n // to go \u2014 the run is parked on a value, and refusing to supply one\n // leaves it parked until its own attempt budget or deadline ends\n // it \u2014 and an approval with no value supplies none.\n if (input.decision === \"deny\") {\n return yield* new ControlError.InvalidInput({\n issue: `decision: \"${wait.name}\" asks a question, which is answered rather than denied`\n })\n }\n if (input.answer === undefined) {\n return yield* new ControlError.InvalidInput({\n issue: `answer: \"${wait.name}\" asks a question, so a decision alone does not answer it`\n })\n }\n const decision = yield* control.signal({\n runId: input.target.runId,\n signal: { name: wait.name, payload: input.answer },\n idempotencyKey: input.idempotencyKey,\n principal\n })\n return { decision }\n }\n const decision = input.decision === \"approve\"\n ? yield* control.approve(payload)\n : yield* control.deny(payload)\n return { decision }\n }), Effect.tapCause(ControlServer.logDefect)),\n \"Run.Fork\": Effect.fn(\"Gateway.fork\")(\n (input) => Effect.andThen(operator, Effect.flatMap(historyHost, (host) => host.fork(input))),\n Effect.tapCause(ControlServer.logDefect)\n ),\n \"Run.Verify\": Effect.fn(\"Gateway.verify\")(\n (input) => Effect.andThen(operator, Effect.flatMap(historyHost, (host) => host.verify(input))),\n Effect.tapCause(ControlServer.logDefect)\n )\n })\n })\n)\n\n/**\n * The kind a `Watch` keepalive carries.\n *\n * `ControlRpcs.Watch` answers `ControlSchema.ControlEvent` and has no frame of\n * its own for a keepalive, so the gateway publishes one as an event whose kind\n * no emitter uses. A fold that does not know the kind ignores it, which is\n * what every fold in this package and in `@smthrs/cli` `Forensics` already\n * does with an unknown kind.\n *\n * @since 1.0.0\n * @category models\n */\nexport const watchHeartbeatKind = \"control.gateway.heartbeat\"\n\n/**\n * Merges a keepalive into a followed `Watch` stream.\n *\n * A followed stream is silent while a run is thinking, and a relay cuts an\n * idle tunnel at 600 s. The Effect RPC client\n * sends its own `Ping` every 5 s, but a non-Effect consumer behind a relay\n * sends nothing, so the keepalive has to come from the server.\n *\n * The keepalive repeats the last delivered sequence and run-scoped cursor,\n * including an expansion offset, so it cannot advance or rewind a checkpoint.\n * It carries the watched run id so a client routing by run keeps routing.\n * A snapshot read (`follow: false`) is left alone: it has to end.\n */\nconst keptAlive = (\n millis: number,\n filter: ControlSchema.WatchFilter,\n events: Stream.Stream<ControlSchema.ControlEvent, ControlError.ControlError>\n): Stream.Stream<ControlSchema.ControlEvent, ControlError.ControlError> => {\n if (filter.follow === false) return events\n let cursor = filter.afterCursor ??\n (filter.afterSequence === undefined ? undefined : { sequence: filter.afterSequence })\n let sequence = cursor?.sequence ?? 0\n const tracked = Stream.map(events, (event) => {\n sequence = event.sequence\n cursor = event.cursor ?? { sequence }\n return event\n })\n const beats = Stream.tick(millis).pipe(\n Stream.drop(1),\n Stream.mapEffect(() =>\n Effect.map(\n Effect.clockWith((clock) => clock.currentTimeMillis),\n (occurredAt): ControlSchema.ControlEvent => ({\n sequence,\n ...(filter.runId === undefined || cursor === undefined ? {} : { cursor }),\n kind: watchHeartbeatKind,\n ...(filter.runId === undefined ? {} : { runId: filter.runId }),\n occurredAt,\n payload: null\n })\n )\n )\n )\n return Stream.merge(tracked, beats, { haltStrategy: \"left\" })\n}\n\n/**\n * The control service the `/rpc` mounts read through: the ambient one, with\n * the keepalive merged into `watch`.\n *\n * Wrapping the service rather than re-declaring the handlers keeps\n * `@smthrs/control` `ControlServer` the single definition of what every\n * procedure does, including the principal it stamps on every mutation that\n * records who asked. Only `watch` behaves differently, and only in what it\n * adds.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerKeepAlive = (\n millis: number = heartbeatIntervalMillis\n): Layer.Layer<Control, never, Control> =>\n Layer.effect(Control)(\n Effect.map(Control, (control) =>\n Control.of({ ...control, watch: (filter) => keptAlive(millis, filter, control.watch(filter)) }))\n )\n\n/**\n * The read path the `/projections` mounts serve through: the ambient one, with\n * a followed subscription's keepalive re-timed to the bind's cadence.\n *\n * `Projections.make` already merges a `heartbeat` frame at the cadence it was\n * built with. A host that supplies `Projections.layer` and sets the cadence at\n * the bind expects one option to govern both keepalives, so this replaces the\n * service's beats with beats at the bind's cadence rather than adding a second\n * channel. Snapshot reads are left alone: they have to end.\n *\n * @param millis how often an idle followed subscription emits a keepalive\n * @since 1.0.0\n * @category layers\n */\nexport const layerProjectionsKeepAlive = (millis: number): Layer.Layer<Projections, never, Projections> =>\n Layer.effect(Projections)(\n Effect.map(Projections, (projections) =>\n Projections.of({\n ...projections,\n subscribe: (selector, after) => keptAliveSubscription(millis, projections.subscribe(selector, after))\n }))\n )\n\n/** Replaces a subscription's keepalive channel with one at the given cadence. */\nconst keptAliveSubscription = <F extends GatewaySchema.GatewayFrame>(\n millis: number,\n frames: Stream.Stream<F, GatewayError>\n): Stream.Stream<F | GatewaySchema.HeartbeatFrame, GatewayError> => {\n // The first tick is dropped for the same reason `Projections` drops it: an\n // immediate keepalive would arrive before the snapshot it keeps alive.\n const beats: Stream.Stream<GatewaySchema.HeartbeatFrame> = Stream.tick(millis).pipe(\n Stream.drop(1),\n Stream.mapEffect(() =>\n Effect.map(\n Effect.clockWith((clock) => clock.currentTimeMillis),\n (atMs): GatewaySchema.HeartbeatFrame => ({ _tag: \"heartbeat\", atMs })\n )\n )\n )\n return Stream.merge(\n Stream.filter(frames, (frame) => frame._tag !== \"heartbeat\"),\n beats,\n { haltStrategy: \"left\" }\n )\n}\n\n/**\n * Mounts the control plane on `POST /rpc` and `/rpc/ws`, with the keepalive.\n *\n * @param millis how often an idle followed watch emits one, defaulting to\n * `Projections.heartbeatIntervalMillis`\n * @since 1.0.0\n * @category layers\n */\nexport const layerControlHttp = (millis?: number | undefined) =>\n ControlServer.layerHttp.pipe(Layer.provide(layerKeepAlive(millis)))\n\nconst gateway = RpcServer.layer(GatewayRpcs, { disableFatalDefects: true })\n\nconst sync = RpcServer.layer(SyncRpcs, { disableFatalDefects: true })\n\n/**\n * Mounts the gateway's projections on `POST /projections` and\n * `/projections/ws`.\n *\n * Both protocols are mounted together because a client reads a snapshot over\n * the request/response path and follows deltas over the socket, and pointing\n * those at two different servers would let them disagree.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerProjectionsHttp = Layer.mergeAll(\n gateway.pipe(\n Layer.provide(layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolHttp({ path: \"/projections\" })),\n Layer.fresh\n ),\n gateway.pipe(\n Layer.provide(layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolWebsocket({ path: \"/projections/ws\" })),\n Layer.fresh\n )\n)\n\n/**\n * Mounts the sync read path on `POST /sync` and `/sync/ws`.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerSyncHttp = Layer.mergeAll(\n sync.pipe(\n Layer.provide(SyncServer.layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolHttp({ path: \"/sync\" })),\n Layer.fresh\n ),\n sync.pipe(\n Layer.provide(SyncServer.layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolWebsocket({ path: \"/sync/ws\" })),\n Layer.fresh\n )\n)\n\n/**\n * The POST mounts that carry RPC request messages and nothing else.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const rpcPaths: ReadonlyArray<string> = [\"/rpc\", \"/projections\", \"/sync\", \"/branch\"]\n\n/**\n * POST mounts whose request bodies are bounded before their handlers read.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const boundedPostPaths: ReadonlyArray<string> = [\n ...rpcPaths,\n \"/runtime/v1/command\",\n \"/runtime/v1/observe\"\n]\n\n/**\n * RPC paths whose upgrade or request must pass edge authentication.\n *\n * `POST /rpc` is deliberately not one of them. The control mount authenticates\n * in band through `@smthrs/control` `ControlRpcs.ControlAuth`, whose declared\n * error is `ControlError.Unauthorized`, and that typed control error is the\n * refusal the control plane publishes: \"Missing, malformed, empty, and\n * incorrect credentials all return the same typed `Unauthorized` control\n * error\" (`packages/smithers/gateway/docs/guides/serve-beyond-loopback.md`). An edge 401 answered\n * ahead of the mount, and `ControlClient` filters a non-2xx status, so every\n * refusal reached a caller as a `TransportError`, the one error class\n * `@smthrs/control` reserves for a request that failed *before* a declared\n * control response reached it. It also made the gateway refuse a call\n * differently from `NodeControl.layerServer` hosting the very same\n * `ControlRpcs`. The body limit below still applies to `/rpc`, so an\n * unauthenticated caller buys one bounded body read and nothing else.\n *\n * The `/rpc/ws` upgrade stays here. A socket is a resource the server holds\n * open, the RPC middleware can only refuse frames on a socket that already\n * exists, and a refused handshake has no RPC channel to answer a typed error\n * on, so its refusal is a transport fact either way.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const protectedPaths: ReadonlyArray<string> = [\n \"/projections\",\n \"/branch\",\n \"/sync\",\n \"/rpc/ws\",\n \"/projections/ws\",\n \"/sync/ws\",\n \"/runtime/v1/command\",\n \"/runtime/v1/observe\"\n]\n\n/**\n * The mount a request target reaches, spelled the way {@link rpcPaths} and\n * {@link protectedPaths} spell it.\n *\n * The guard runs before the router and has to reach the router's verdict, or\n * an alias walks past the credential check and the body limit and is answered\n * by the mount anyway. Measured against `HttpRouter` on this tree, all of\n * `/%72pc`, `/rpc;transport-parameter`, `/rpc/`, `//rpc`, `///rpc`, `/rpc//`,\n * `/RPC`, `/./rpc`, and `/foo/../rpc` reach the `/rpc` handler, while\n * `/rpc%2f`, `/rpc%3Bp`, `/%2frpc`, and `/rpc%20` do not. This is not only\n * about crafted aliases: `@smthrs/control` `ControlClient`'s HTTP protocol\n * posts every call to `/rpc/`, so a literal comparison skipped the body limit\n * on the path the product's own client uses.\n *\n * Each segment is therefore taken without its `;` parameter, empty segments are\n * dropped, and what is left is decoded with `decodeURI` and compared without\n * regard to case. `decodeURI` is the decoder rather than `decodeURIComponent`\n * because it leaves a reserved character encoded, which is what keeps\n * `/rpc%2f` a different path here exactly as it is to the router. An invalid\n * escape is left as written, since it names no mount either way.\n *\n * Dot segments are resolved by `URL` before the split. The normalization errs\n * toward matching: a spelling this resolves to a mount that the router then\n * refuses is answered 401 rather than 404, which costs an unauthenticated\n * caller nothing it was entitled to.\n *\n * @param url the request target, origin-relative or absolute\n * @since 1.0.0\n * @category constructors\n */\nexport const routedPath = (url: string): string => {\n // An empty leading segment is a protocol-relative URL to `URL`, so `//rpc`\n // would parse as the host `rpc` and lose the path entirely. Anchoring an\n // origin-relative target on the origin keeps every segment. An absolute\n // target that does not parse at all names no mount, so it is compared as\n // written and reaches the router as the unknown route it is.\n const target = url.startsWith(\"/\") ? `http://gateway.invalid${url}` : url\n const parsed = URL.parse(target, \"http://gateway.invalid\")\n if (parsed === null) return url\n const segments: Array<string> = []\n for (const segment of parsed.pathname.split(\"/\")) {\n const parameter = segment.indexOf(\";\")\n const named = parameter < 0 ? segment : segment.slice(0, parameter)\n if (named === \"\") continue\n try {\n segments.push(decodeURI(named))\n } catch {\n segments.push(named)\n }\n }\n return `/${segments.join(\"/\")}`.toLowerCase()\n}\n\n/**\n * The mount that exchanges a bearer credential for a WebSocket {@link Ticket}.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const ticketPath = \"/auth/ticket\"\n\n/**\n * The protected socket mounts a {@link Ticket} opens.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const ticketPaths: ReadonlyArray<string> = [\"/rpc/ws\", \"/projections/ws\", \"/sync/ws\"]\n\n/**\n * How long an unredeemed {@link Ticket} stays valid (thirty seconds).\n *\n * @since 1.0.0\n * @category constants\n */\nexport const defaultTicketMillis = 30_000\n\n/** Outstanding tickets kept at once; the oldest is dropped past this. */\nconst maxOutstandingTickets = 1024\n\n/**\n * What `POST /auth/ticket` answers.\n *\n * A browser `WebSocket` cannot set an `Authorization` header, so a browser\n * client first posts its bearer to `/auth/ticket`, then opens a protected\n * socket as `<mount>?ticket=<ticket>` within `expiresInMs`. The first upgrade\n * that presents the ticket consumes it, whether or not the upgrade completes;\n * a replay or a late one is refused 401. The socket then runs under the\n * bearer the ticket was issued for, so every frame authenticates as it would\n * with the header.\n *\n * @since 1.0.0\n * @category models\n */\nexport const Ticket = Schema.Struct({\n ticket: Schema.String,\n expiresInMs: Schema.Number\n})\n\n/**\n * What `POST /auth/ticket` answers.\n *\n * @since 1.0.0\n * @category models\n */\nexport type Ticket = typeof Ticket.Type\n\nconst encodeTicket = Schema.encodeUnknownSync(Ticket)\n\n/** 256 random bits, hex-encoded so a query string carries them unescaped. */\nconst newTicket = (): string =>\n Array.from(crypto.getRandomValues(new Uint8Array(32)), (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\")\n\n/** The `ticket` query parameter of a request target, when it has one. */\nconst presentedTicket = (url: string): string | undefined => {\n const parsed = URL.parse(url, \"http://gateway.invalid\")\n const ticket = parsed?.searchParams.get(\"ticket\")\n return ticket === null || ticket === undefined || ticket === \"\" ? undefined : ticket\n}\n\n/**\n * Default maximum request body accepted by an RPC mount (one MiB).\n *\n * @category constants\n * @since 1.0.0\n */\nexport const defaultMaxRequestBodyBytes = 1024 * 1024\n\n/**\n * Ingress policy enforced before an RPC transport parses a request.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface IngressOptions {\n /** Accepted Host header names, without ports. Defaults to loopback names. */\n readonly allowedHosts?: ReadonlyArray<string> | undefined\n readonly maxRequestBodyBytes?: number | undefined\n /** Confine requests to loopback Host values and browser origins. */\n readonly loopbackOnly?: boolean | undefined\n /**\n * Edge authentication for {@link protectedPaths}, told which mount the\n * request reaches. When set, `POST /auth/ticket` exchanges a credential it\n * accepts for a {@link Ticket}, and a redeemed ticket's credential is judged\n * again for the mount its upgrade reaches.\n */\n readonly authorize?: (\n headers: Readonly<Record<string, string>>,\n path: string\n ) => Effect.Effect<boolean>\n /** How long an unredeemed ticket stays valid. Default {@link defaultTicketMillis}. */\n readonly ticketMillis?: number | undefined\n}\n\nconst encodeGatewayError = Schema.encodeUnknownSync(GatewayError)\n\n/** Host parsing stays strict so URL normalization cannot admit a rebinding name. */\nconst requestAuthority = (host: string | undefined): URL | undefined => {\n if (host === undefined || !/^(?:[a-zA-Z0-9.-]+|\\[[a-fA-F0-9:]+\\])(?::[0-9]+)?$/.test(host)) return undefined\n const parsed = URL.parse(`http://${host}`)\n return parsed === null ? undefined : parsed\n}\n\nconst browserRequestRefusal = (\n headers: Readonly<Record<string, string>>,\n allowedHosts: ReadonlyArray<string>,\n loopbackOnly: boolean\n): { readonly error: GatewayError; readonly status: number } | undefined => {\n const authority = requestAuthority(headers.host)\n if (\n authority === undefined || !allowedHosts.includes(authority.hostname) ||\n (loopbackOnly && !loopbackHost.test(headers.host!))\n ) return { error: invalidHostRequest(), status: 421 }\n const origin = headers.origin\n // CLI clients do not send Origin. Browsers must prove they came from this\n // exact authority, including the port, on HTTP requests AND WS upgrades.\n if (origin === undefined) return undefined\n const parsed = URL.parse(origin)\n return parsed !== null && (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") &&\n parsed.origin === origin && parsed.host === authority.host\n ? undefined\n : { error: invalidOriginRequest(), status: 403 }\n}\n\n/** One refusal on the wire: the typed error, under the status it answers. */\nconst refuse = (error: GatewayError, status: number) =>\n HttpServerResponse.jsonUnsafe(encodeGatewayError(error), { status })\n\nconst malformedRequest = (path: string) =>\n new GatewayError({ code: \"malformed_request\", message: `POST ${path} carries no RPC request message` })\n\nconst unauthorizedRequest = () =>\n new GatewayError({ code: \"unauthorized\", message: \"A valid bearer credential is required\" })\n\nconst invalidHostRequest = () =>\n new GatewayError({ code: \"invalid_host\", message: \"This gateway does not allow the supplied Host value\" })\n\nconst invalidOriginRequest = () =>\n new GatewayError({ code: \"invalid_origin\", message: \"The browser Origin must match an allowed gateway Host\" })\n\nconst requestTooLarge = (path: string, maxBytes: number) =>\n new GatewayError({\n code: \"request_too_large\",\n message: `POST ${path} exceeds the ${maxBytes}-byte request limit`\n })\n\n/** The message `@effect/platform-node-shared` `NodeStream` gives a size overflow. */\nconst maxBytesExceeded = \"maxBytes exceeded\"\n\n/**\n * The names that mean this machine only, as a bind address is spelled.\n *\n * This is a list of spellings a person types, not a subnet test: `127.0.0.2`\n * is loopback to the kernel and is not on it. The Node host's\n * `isLoopbackHost`, the Host-header names below, and the ingress regex all\n * derive from this one list so widening it is one edit.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const loopbackHostNames: ReadonlyArray<string> = [\"127.0.0.1\", \"localhost\", \"::1\"]\n\n/**\n * {@link loopbackHostNames} as a Host header spells them: an IPv6 address is\n * bracketed as a URL authority. This is the default `allowedHosts`.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const loopbackHostHeaderNames: ReadonlyArray<string> = loopbackHostNames.map((name) =>\n name.includes(\":\") ? `[${name}]` : name\n)\n\nconst escapeRegExp = (text: string) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\nconst loopbackHost = new RegExp(`^(?:${loopbackHostHeaderNames.map(escapeRegExp).join(\"|\")})(?::\\\\d{1,5})?$`, \"i\")\n\n/**\n * Whether a failed request-body read hit the configured size limit rather than\n * failing for another reason.\n *\n * `@effect/platform-node` raises the limit as an `HttpServerError` whose\n * `reason` is a `RequestParseError` carrying the `Error(\"maxBytes exceeded\")`\n * that `@effect/platform-node-shared` `NodeStream.toString` throws\n * (`NodeHttpIncomingMessage.text` passes `MaxBodySize` in as `maxBytes`, and\n * `NodeHttpServer`'s `ServerRequestImpl` wraps whatever it throws). Nothing on\n * the wire distinguishes it otherwise, so the check is that exact shape and a\n * version bump of those packages is the thing to re-read it against.\n *\n * Every other read failure, a transport reset among them, is not a size\n * overflow. Answering 413 for all of them told a client to shrink a request\n * that was never too big.\n *\n * @param error the failure a body read produced\n * @since 1.0.0\n * @category predicates\n */\nexport const exceededBodyLimit = (error: unknown): boolean => {\n const reason = typeof error === \"object\" && error !== null\n ? (error as { readonly reason?: unknown }).reason\n : undefined\n const cause = typeof reason === \"object\" && reason !== null\n ? (reason as { readonly cause?: unknown }).cause\n : undefined\n return cause instanceof Error && cause.message === maxBytesExceeded\n}\n\n/**\n * The refusal a failed request-body read earns, and the status it answers\n * under.\n *\n * A body over the limit is 413 `request_too_large`. Every other read failure,\n * a truncated body or a reset transport among them, is 400\n * `malformed_request`: the request cannot be retried smaller, because its size\n * was never the problem.\n *\n * @param path the mount the request was made to\n * @param maxBytes the configured limit\n * @param error the failure the body read produced\n * @since 1.0.0\n * @category constructors\n */\nexport const bodyRefusal = (\n path: string,\n maxBytes: number,\n error: unknown\n): { readonly error: GatewayError; readonly status: number } =>\n exceededBodyLimit(error)\n ? { error: requestTooLarge(path, maxBytes), status: 413 }\n : {\n error: new GatewayError({\n code: \"malformed_request\",\n message: `POST ${path} carries a body the server could not read`\n }),\n status: 400\n }\n\n/**\n * Whether a request body carries at least one RPC message the server can act\n * on.\n *\n * The transport's own parser answers, not a second reading of the wire\n * format: whatever `RpcSerialization` the host composed decodes the body, and\n * the answer is no only when that decode throws or yields no tagged message.\n * A body naming a procedure that does not exist is a yes \u2014 that is an\n * RPC-level defect the protocol itself reports.\n *\n * A binary framing is always a yes. Its body is not text, so there is nothing\n * to read here, and the mount owns it.\n *\n * @since 1.0.0\n * @category predicates\n */\nexport const carriesRpcRequest = (\n serialization: RpcSerialization.RpcSerialization[\"Service\"],\n body: string\n): boolean => {\n if (!serialization.contentType.includes(\"json\")) return true\n try {\n const decoded = serialization.makeUnsafe().decode(body)\n return decoded.length > 0 && decoded.every((message: unknown) =>\n typeof message === \"object\" && message !== null &&\n typeof (message as { readonly _tag?: unknown })._tag === \"string\"\n )\n } catch {\n return false\n }\n}\n\n/**\n * Enforces local Host/Origin policy and edge authentication before reading a\n * body or upgrading a socket, then validates RPC body size and framing.\n *\n * `effect/unstable/rpc` hands every decoded message to the server loop, and a\n * body that decodes to something else \u2014 `{}`, `[]`, prose, nothing at all \u2014\n * reached it as a message with no tag and died there, so the gateway answered\n * `500 Internal Server Error` with an empty body (release validation). That is the\n * wrong half of the contract twice over: it tells an operator the gateway\n * broke, and it tells a client to retry a request that can never succeed.\n *\n * The body reader is cached per request, so reading the body here does not\n * consume the body the mount reads. It is read through the reader the mount\n * uses, `text` under a JSON serialization and `arrayBuffer` under a binary\n * one, so the bytes the mount decodes are the bytes the client sent.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerIngress = (options: IngressOptions = {}) => {\n const refusal = settingRefusal(\"The gateway request body limit\", options.maxRequestBodyBytes) ??\n settingRefusal(\"The gateway ticket lifetime\", options.ticketMillis)\n if (refusal !== undefined) return Layer.effectDiscard(Effect.fail(refusal))\n const maxBytes = options.maxRequestBodyBytes ?? defaultMaxRequestBodyBytes\n const allowedHosts = options.allowedHosts ?? loopbackHostHeaderNames\n const ticketMillis = options.ticketMillis ?? defaultTicketMillis\n return HttpRouter.middleware(\n Effect.gen(function*() {\n const serialization = yield* RpcSerialization.RpcSerialization\n const binary = !serialization.contentType.includes(\"json\")\n /** Outstanding tickets: the Authorization header each was issued under, and when it lapses. */\n const tickets = new Map<string, { readonly authorization: string; readonly expiresAt: number }>()\n const issue = (authorization: string, now: number): Ticket => {\n for (const [ticket, entry] of tickets) {\n if (entry.expiresAt <= now) tickets.delete(ticket)\n }\n if (tickets.size >= maxOutstandingTickets) tickets.delete(tickets.keys().next().value!)\n const ticket = newTicket()\n tickets.set(ticket, { authorization, expiresAt: now + ticketMillis })\n return { ticket, expiresInMs: ticketMillis }\n }\n // Lookup and delete run without yielding, so of two upgrades presenting\n // one ticket exactly one gets its header.\n const redeem = (url: string, now: number): string | undefined => {\n const ticket = presentedTicket(url)\n const entry = ticket === undefined ? undefined : tickets.get(ticket)\n if (entry === undefined) return undefined\n tickets.delete(ticket!)\n return entry.expiresAt > now ? entry.authorization : undefined\n }\n return (httpEffect: Effect.Effect<HttpServerResponse.HttpServerResponse, Types.unhandled>) =>\n Effect.gen(function*() {\n const request = yield* HttpServerRequest.HttpServerRequest\n const browserRefusal = browserRequestRefusal(request.headers, allowedHosts, options.loopbackOnly === true)\n if (browserRefusal !== undefined) return refuse(browserRefusal.error, browserRefusal.status)\n const path = routedPath(request.url)\n if (path === ticketPath && request.method === \"POST\" && options.authorize !== undefined) {\n const authorization = request.headers.authorization\n if (authorization === undefined || !(yield* options.authorize(request.headers, path))) {\n return refuse(unauthorizedRequest(), 401)\n }\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis)\n return HttpServerResponse.jsonUnsafe(encodeTicket(issue(authorization, now)), {\n headers: { \"cache-control\": \"no-store\" }\n })\n }\n if (protectedPaths.includes(path) && options.authorize !== undefined) {\n const authorized = yield* options.authorize(request.headers, path)\n if (!authorized) {\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis)\n const authorization = ticketPaths.includes(path) ? redeem(request.url, now) : undefined\n if (authorization === undefined) return refuse(unauthorizedRequest(), 401)\n // A ticket stands for the credential it was issued under, which\n // may not reach every mount a ticket can open.\n const headers = Headers.set(request.headers, \"authorization\", authorization)\n if (!(yield* options.authorize(headers, path))) return refuse(unauthorizedRequest(), 401)\n // The socket protocol stamps the upgrade's headers on every\n // frame, so the redeemed bearer authenticates each one.\n return yield* Effect.provideService(\n httpEffect,\n HttpServerRequest.HttpServerRequest,\n request.modify({ headers })\n )\n }\n }\n if (request.method !== \"POST\" || !boundedPostPaths.includes(path)) return yield* httpEffect\n // A declared length is a hint that saves reading a body already\n // known to be too big. It is never trusted the other way: a body\n // that declares less than it sends is still measured by the read,\n // and a chunked body declares nothing at all.\n const declaredLength = Number(request.headers[\"content-length\"])\n if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {\n return refuse(requestTooLarge(path, maxBytes), 413)\n }\n // The body is read through the reader the mount will use. The Node\n // adapter caches whichever reader ran first and derives the other\n // from it, so a text read here would hand a binary mount a UTF-8\n // re-encoding of its MessagePack bytes, with every invalid sequence\n // replaced. A binary body is bounded here and framed by the mount.\n const read = yield* (binary ? Effect.as(request.arrayBuffer, undefined) : request.text).pipe(\n Effect.provideService(HttpServerRequest.MaxBodySize, ByteSize.bytes(maxBytes)),\n Effect.match({\n onFailure: (error): { readonly body: string | undefined; readonly error: unknown } => ({\n body: undefined,\n error\n }),\n onSuccess: (body) => ({ body, error: undefined })\n })\n )\n if (read.error !== undefined) {\n const answer = bodyRefusal(path, maxBytes, read.error)\n return refuse(answer.error, answer.status)\n }\n if (read.body === undefined) return yield* httpEffect\n return !rpcPaths.includes(path) || carriesRpcRequest(serialization, read.body)\n ? yield* httpEffect\n : refuse(malformedRequest(path), 400)\n })\n }),\n { global: true }\n )\n}\n\n/**\n * How an assembled gateway is configured.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface LayerOptions {\n /**\n * How often an idle followed `Watch` on `/rpc/ws` and an idle followed\n * `Projection.Subscribe` on `/projections/ws` emit a keepalive. Unset, the\n * `Watch` keepalive runs at `Projections.heartbeatIntervalMillis` and the\n * supplied `Projections` service keeps the cadence it was built with.\n */\n readonly heartbeatMillis?: number | undefined\n /** The ingress policy the RPC mounts run behind. */\n readonly ingress?: IngressOptions | undefined\n /** Enables the authenticated Go-to-Control runtime bridge. */\n readonly runtimeBridge?: RuntimeBridge.Config | undefined\n}\n\n/**\n * The whole gateway surface, as one application layer a host serves.\n *\n * The caller supplies the transport serialization, the authentication\n * middleware for both RPC groups, and the services the mounts read through\n * (`Control`, `Projections`, `SyncServer`). `@smthrs/gateway/node/NodeGateway`\n * is the Node host that binds this to a socket.\n *\n * A keepalive cadence or a body limit that is not a positive safe integer is\n * refused here, before anything binds, through the layer's typed\n * `GatewayError` channel.\n *\n * @param health the identity `GET /health` answers with\n * @param options the keepalive cadence and the ingress policy\n * @since 1.0.0\n * @category layers\n */\nexport const layer = (health: Health, options: LayerOptions = {}) => {\n const refusal = settingRefusal(\"The gateway keepalive cadence\", options.heartbeatMillis)\n if (refusal !== undefined) return Layer.effectDiscard(Effect.fail(refusal))\n return Layer.mergeAll(\n layerControlHttp(options.heartbeatMillis),\n options.heartbeatMillis === undefined\n ? layerProjectionsHttp\n : layerProjectionsHttp.pipe(Layer.provide(layerProjectionsKeepAlive(options.heartbeatMillis))),\n layerSyncHttp,\n layerHealth(health),\n ...(options.runtimeBridge === undefined\n ? []\n : [RuntimeBridge.layer(options.runtimeBridge), HostBranchPresence.layer(options.runtimeBridge)]),\n layerIngress(options.ingress)\n )\n}\n"],
|
|
4
|
+
"sourcesContent": ["/**\n * The assembled workspace gateway: one HTTP surface carrying the control\n * plane, the sync read path, the served projections, and a health probe.\n *\n * Mounts:\n *\n * | Path | Protocol | Serves |\n * | --- | --- | --- |\n * | `POST /rpc` | RPC over HTTP | `@smthrs/control` `ControlRpcs` |\n * | `/rpc/ws` | RPC over WebSocket | `ControlRpcs`, including a kept-alive `Watch` |\n * | `POST /projections` | RPC over HTTP | `GatewayRpcs` |\n * | `/projections/ws` | RPC over WebSocket | `GatewayRpcs`, including `Projection.Subscribe` |\n * | `POST /sync` | RPC over HTTP | `@smthrs/sync` `SyncRpcs` |\n * | `/sync/ws` | RPC over WebSocket | `SyncRpcs` |\n * | `GET /health` | JSON | `GatewaySchema.GatewayHealth` plus the package version |\n * | `POST /auth/ticket` | JSON | A single-use {@link Ticket} that opens one protected socket |\n *\n * `/health` is deliberately unauthenticated: a supervisor decides whether to\n * keep or replace a gateway process by asking which workspace it belongs to,\n * and a probe that needed a credential could not answer that question about a\n * gateway it did not start. The response carries identity only: the\n * workspace hash, the gateway id, the protocol version, and the package\n * version. It never carries a token, a run, or a path.\n *\n * @since 1.0.0\n */\n\nimport { Control } from \"@smthrs/control/Control\"\nimport * as ControlError from \"@smthrs/control/ControlError\"\nimport * as ControlExecutor from \"@smthrs/control/ControlExecutor\"\nimport { ControlPrincipal } from \"@smthrs/control/ControlRpcs\"\nimport type * as ControlSchema from \"@smthrs/control/ControlSchema\"\nimport * as ControlServer from \"@smthrs/control/ControlServer\"\nimport { SyncRpcs } from \"@smthrs/sync/SyncRpcs\"\nimport * as SyncServer from \"@smthrs/sync/SyncServer\"\nimport { Effect, Layer, Option, Schema, Stream, type Types } from \"effect\"\nimport * as ByteSize from \"effect/ByteSize\"\nimport { Headers, HttpRouter, HttpServerRequest, HttpServerResponse } from \"effect/http\"\nimport { RpcSerialization, RpcServer } from \"effect/rpc\"\nimport { GatewayError, settingRefusal } from \"./GatewayError.ts\"\nimport { GatewayRpcs } from \"./GatewayRpcs.ts\"\nimport * as GatewaySchema from \"./GatewaySchema.ts\"\nimport * as HostBranchPresence from \"./HostBranchPresence.ts\"\nimport { heartbeatIntervalMillis, Projections } from \"./Projections.ts\"\nimport * as RunHistory from \"./RunHistory.ts\"\nimport * as RuntimeBridge from \"./RuntimeBridge.ts\"\n\n/**\n * What `GET /health` answers: the `GatewaySchema.GatewayHealth` identity plus\n * the version of the package serving it.\n *\n * @since 1.0.0\n * @category models\n */\nexport const Health = Schema.Struct({\n ...GatewaySchema.GatewayHealth.fields,\n version: Schema.String,\n runtimeBridge: Schema.optional(RuntimeBridge.Identity)\n})\n\n/**\n * What `GET /health` answers.\n *\n * @since 1.0.0\n * @category models\n */\nexport type Health = typeof Health.Type\n\nconst encodeHealth = Schema.encodeUnknownSync(Health)\n\n/**\n * Mounts the unauthenticated health probe.\n *\n * @param health the identity this process serves under\n * @since 1.0.0\n * @category layers\n */\nexport const layerHealth = (health: Health): Layer.Layer<never, never, HttpRouter.HttpRouter> =>\n HttpRouter.add(\"GET\", \"/health\", HttpServerResponse.jsonUnsafe(encodeHealth(health)))\n\n/**\n * The gateway's own RPC handlers over the read path and the control plane.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerHandlers = GatewayRpcs.toLayer(\n Effect.gen(function*() {\n const projections = yield* Projections\n const control = yield* Control\n // Only a host that owns the project's stores can fork or verify its runs.\n const history = yield* Effect.serviceOption(RunHistory.RunHistory)\n const historyHost = Option.match(history, {\n onNone: () => Effect.fail(new ControlError.Unavailable({ feature: \"run history\", ticket: \"smithers#3242\" })),\n onSome: Effect.succeed\n })\n // Forking writes a durable run and verifying drives the project's flow\n // code, so neither is an anonymous loopback read.\n const operator = Effect.gen(function*() {\n const principal = yield* ControlPrincipal\n if (principal.id === \"loopback\" && principal.kind === \"anonymous\") {\n return yield* new ControlError.Unauthorized({ message: \"An operator credential is required\" })\n }\n })\n return GatewayRpcs.of({\n \"Projection.Snapshot\": Effect.fn(\"Gateway.snapshot\")(\n ({ selector, after }) => projections.snapshot(selector, after),\n Effect.tapCause(ControlServer.logDefect)\n ),\n \"Projection.Subscribe\": ({ after, selector }) =>\n Stream.tapCause(projections.subscribe(selector, after), ControlServer.logDefect),\n /* One operator command. Control owns the atomic decision plus durable\n * resume delegation; the gateway is only a transport adapter. */\n \"Approval.Submit\": Effect.fn(\"Gateway.submitApproval\")((input) =>\n Effect.gen(function*() {\n // The identity the shared `ControlAuth` middleware authenticated, as\n // `ControlServer` stamps it on `Approve` and `Deny`. This mount is\n // the same decision under a different payload, so it is answerable\n // to the same operator, and a decision journaled under the\n // composition's default operator names the wrong one.\n const principal = yield* ControlPrincipal\n // Answering a human wait uses signal rather than the approval policy.\n // Do not let that alternate mutation path bypass loopback authentication.\n if (principal.id === \"loopback\" && principal.kind === \"anonymous\") {\n return yield* new ControlError.Unauthorized({ message: \"An operator credential is required\" })\n }\n const payload = {\n target: input.target,\n scope: input.scope,\n idempotencyKey: input.idempotencyKey,\n principal\n }\n // A gate that asked a question is answered, not granted, and the\n // PAYLOAD says which kind it is: a human wait carries its durable\n // wait token as the target's digest, and nothing else does. Reading\n // it here rather than trusting the client to have sent `answer` is\n // the difference between a clear refusal and the one an operator\n // actually saw \u2014 a client that forgot the answer fell through to\n // `Control.approve`, which looked for a registered approval token,\n // found none, and reported `/control/RunNotFound` for a run that was\n // listed, rendered, and waiting (workspace 4bb93306, run-1).\n //\n // `target.runId` is the run the person opened and `wait.name` the\n // wait point's own name; `Control.signal` routes the pair into the\n // run tree, to whichever execution is holding it.\n const wait = ControlExecutor.answerableWait(input.target)\n if (wait !== undefined && input.target._tag === \"Node\") {\n // Neither half of a grant answers a question. A denial has nowhere\n // to go \u2014 the run is parked on a value, and refusing to supply one\n // leaves it parked until its own attempt budget or deadline ends\n // it \u2014 and an approval with no value supplies none.\n if (input.decision === \"deny\") {\n return yield* new ControlError.InvalidInput({\n issue: `decision: \"${wait.name}\" asks a question, which is answered rather than denied`\n })\n }\n if (input.answer === undefined) {\n return yield* new ControlError.InvalidInput({\n issue: `answer: \"${wait.name}\" asks a question, so a decision alone does not answer it`\n })\n }\n const decision = yield* control.signal({\n runId: input.target.runId,\n signal: { name: wait.name, payload: input.answer },\n idempotencyKey: input.idempotencyKey,\n principal\n })\n return { decision }\n }\n const decision = input.decision === \"approve\"\n ? yield* control.approve(payload)\n : yield* control.deny(payload)\n return { decision }\n }), Effect.tapCause(ControlServer.logDefect)),\n \"Run.Fork\": Effect.fn(\"Gateway.fork\")(\n (input) => Effect.andThen(operator, Effect.flatMap(historyHost, (host) => host.fork(input))),\n Effect.tapCause(ControlServer.logDefect)\n ),\n \"Run.Verify\": Effect.fn(\"Gateway.verify\")(\n (input) => Effect.andThen(operator, Effect.flatMap(historyHost, (host) => host.verify(input))),\n Effect.tapCause(ControlServer.logDefect)\n )\n })\n })\n)\n\n/**\n * The kind a `Watch` keepalive carries.\n *\n * `ControlRpcs.Watch` answers `ControlSchema.ControlEvent` and has no frame of\n * its own for a keepalive, so the gateway publishes one as an event whose kind\n * no emitter uses. A fold that does not know the kind ignores it, which is\n * what every fold in this package and in `@smthrs/cli` `Forensics` already\n * does with an unknown kind.\n *\n * @since 1.0.0\n * @category models\n */\nexport const watchHeartbeatKind = \"control.gateway.heartbeat\"\n\n/**\n * Merges a keepalive into a followed `Watch` stream.\n *\n * A followed stream is silent while a run is thinking, and a relay cuts an\n * idle tunnel at 600 s. The Effect RPC client\n * sends its own `Ping` every 5 s, but a non-Effect consumer behind a relay\n * sends nothing, so the keepalive has to come from the server.\n *\n * The keepalive repeats the last delivered sequence and run-scoped cursor,\n * including an expansion offset, so it cannot advance or rewind a checkpoint.\n * It carries the watched run id so a client routing by run keeps routing.\n * A snapshot read (`follow: false`) is left alone: it has to end.\n */\nconst keptAlive = (\n millis: number,\n filter: ControlSchema.WatchFilter,\n events: Stream.Stream<ControlSchema.ControlEvent, ControlError.ControlError>\n): Stream.Stream<ControlSchema.ControlEvent, ControlError.ControlError> => {\n if (filter.follow === false) return events\n let cursor = filter.afterCursor ??\n (filter.afterSequence === undefined ? undefined : { sequence: filter.afterSequence })\n let sequence = cursor?.sequence ?? 0\n const tracked = Stream.map(events, (event) => {\n sequence = event.sequence\n cursor = event.cursor ?? { sequence }\n return event\n })\n const beats = Stream.tick(millis).pipe(\n Stream.drop(1),\n Stream.mapEffect(() =>\n Effect.map(\n Effect.clockWith((clock) => clock.currentTimeMillis),\n (occurredAt): ControlSchema.ControlEvent => ({\n sequence,\n ...(filter.runId === undefined || cursor === undefined ? {} : { cursor }),\n kind: watchHeartbeatKind,\n ...(filter.runId === undefined ? {} : { runId: filter.runId }),\n occurredAt,\n payload: null\n })\n )\n )\n )\n return Stream.merge(tracked, beats, { haltStrategy: \"left\" })\n}\n\n/**\n * The control service the `/rpc` mounts read through: the ambient one, with\n * the keepalive merged into `watch`.\n *\n * Wrapping the service rather than re-declaring the handlers keeps\n * `@smthrs/control` `ControlServer` the single definition of what every\n * procedure does, including the principal it stamps on every mutation that\n * records who asked. Only `watch` behaves differently, and only in what it\n * adds.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerKeepAlive = (\n millis: number = heartbeatIntervalMillis\n): Layer.Layer<Control, never, Control> =>\n Layer.effect(Control)(\n Effect.map(Control, (control) =>\n Control.of({ ...control, watch: (filter) => keptAlive(millis, filter, control.watch(filter)) }))\n )\n\n/**\n * The read path the `/projections` mounts serve through: the ambient one, with\n * a followed subscription's keepalive re-timed to the bind's cadence.\n *\n * `Projections.make` already merges a `heartbeat` frame at the cadence it was\n * built with. A host that supplies `Projections.layer` and sets the cadence at\n * the bind expects one option to govern both keepalives, so this replaces the\n * service's beats with beats at the bind's cadence rather than adding a second\n * channel. Snapshot reads are left alone: they have to end.\n *\n * @param millis how often an idle followed subscription emits a keepalive\n * @since 1.0.0\n * @category layers\n */\nexport const layerProjectionsKeepAlive = (millis: number): Layer.Layer<Projections, never, Projections> =>\n Layer.effect(Projections)(\n Effect.map(Projections, (projections) =>\n Projections.of({\n ...projections,\n subscribe: (selector, after) => keptAliveSubscription(millis, projections.subscribe(selector, after))\n }))\n )\n\n/** Replaces a subscription's keepalive channel with one at the given cadence. */\nconst keptAliveSubscription = <F extends GatewaySchema.GatewayFrame>(\n millis: number,\n frames: Stream.Stream<F, GatewayError>\n): Stream.Stream<F | GatewaySchema.HeartbeatFrame, GatewayError> => {\n // The first tick is dropped for the same reason `Projections` drops it: an\n // immediate keepalive would arrive before the snapshot it keeps alive.\n const beats: Stream.Stream<GatewaySchema.HeartbeatFrame> = Stream.tick(millis).pipe(\n Stream.drop(1),\n Stream.mapEffect(() =>\n Effect.map(\n Effect.clockWith((clock) => clock.currentTimeMillis),\n (atMs): GatewaySchema.HeartbeatFrame => ({ _tag: \"heartbeat\", atMs })\n )\n )\n )\n return Stream.merge(\n Stream.filter(frames, (frame) => frame._tag !== \"heartbeat\"),\n beats,\n { haltStrategy: \"left\" }\n )\n}\n\n/**\n * Mounts the control plane on `POST /rpc` and `/rpc/ws`, with the keepalive.\n *\n * @param millis how often an idle followed watch emits one, defaulting to\n * `Projections.heartbeatIntervalMillis`\n * @since 1.0.0\n * @category layers\n */\nexport const layerControlHttp = (millis?: number | undefined) =>\n ControlServer.layerHttp.pipe(Layer.provide(layerKeepAlive(millis)))\n\nconst gateway = RpcServer.layer(GatewayRpcs, { disableFatalDefects: true })\n\nconst sync = RpcServer.layer(SyncRpcs, { disableFatalDefects: true })\n\n/**\n * Mounts the gateway's projections on `POST /projections` and\n * `/projections/ws`.\n *\n * Both protocols are mounted together because a client reads a snapshot over\n * the request/response path and follows deltas over the socket, and pointing\n * those at two different servers would let them disagree.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerProjectionsHttp = Layer.mergeAll(\n gateway.pipe(\n Layer.provide(layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolHttp({ path: \"/projections\" })),\n Layer.fresh\n ),\n gateway.pipe(\n Layer.provide(layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolWebsocket({ path: \"/projections/ws\" })),\n Layer.fresh\n )\n)\n\n/**\n * Mounts the sync read path on `POST /sync` and `/sync/ws`.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerSyncHttp = Layer.mergeAll(\n sync.pipe(\n Layer.provide(SyncServer.layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolHttp({ path: \"/sync\" })),\n Layer.fresh\n ),\n sync.pipe(\n Layer.provide(SyncServer.layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolWebsocket({ path: \"/sync/ws\" })),\n Layer.fresh\n )\n)\n\n/**\n * The POST mounts that carry RPC request messages and nothing else.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const rpcPaths: ReadonlyArray<string> = [\"/rpc\", \"/projections\", \"/sync\", \"/branch\"]\n\n/**\n * POST mounts whose request bodies are bounded before their handlers read.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const boundedPostPaths: ReadonlyArray<string> = [\n ...rpcPaths,\n \"/runtime/v1/command\",\n \"/runtime/v1/observe\"\n]\n\n/**\n * RPC paths whose upgrade or request must pass edge authentication.\n *\n * `POST /rpc` is deliberately not one of them. The control mount authenticates\n * in band through `@smthrs/control` `ControlRpcs.ControlAuth`, whose declared\n * error is `ControlError.Unauthorized`, and that typed control error is the\n * refusal the control plane publishes: \"Missing, malformed, empty, and\n * incorrect credentials all return the same typed `Unauthorized` control\n * error\" (`packages/smithers/gateway/docs/guides/serve-beyond-loopback.md`). An edge 401 answered\n * ahead of the mount, and `ControlClient` filters a non-2xx status, so every\n * refusal reached a caller as a `TransportError`, the one error class\n * `@smthrs/control` reserves for a request that failed *before* a declared\n * control response reached it. It also made the gateway refuse a call\n * differently from `NodeControl.layerServer` hosting the very same\n * `ControlRpcs`. The body limit below still applies to `/rpc`, so an\n * unauthenticated caller buys one bounded body read and nothing else.\n *\n * The `/rpc/ws` upgrade stays here. A socket is a resource the server holds\n * open, the RPC middleware can only refuse frames on a socket that already\n * exists, and a refused handshake has no RPC channel to answer a typed error\n * on, so its refusal is a transport fact either way.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const protectedPaths: ReadonlyArray<string> = [\n \"/projections\",\n \"/branch\",\n \"/sync\",\n \"/rpc/ws\",\n \"/projections/ws\",\n \"/sync/ws\",\n \"/runtime/v1/command\",\n \"/runtime/v1/observe\"\n]\n\n/**\n * The mount a request target reaches, spelled the way {@link rpcPaths} and\n * {@link protectedPaths} spell it.\n *\n * The guard runs before the router and has to reach the router's verdict, or\n * an alias walks past the credential check and the body limit and is answered\n * by the mount anyway. Measured against `HttpRouter` on this tree, all of\n * `/%72pc`, `/rpc;transport-parameter`, `/rpc/`, `//rpc`, `///rpc`, `/rpc//`,\n * `/RPC`, `/./rpc`, and `/foo/../rpc` reach the `/rpc` handler, while\n * `/rpc%2f`, `/rpc%3Bp`, `/%2frpc`, and `/rpc%20` do not. This is not only\n * about crafted aliases: `@smthrs/control` `ControlClient`'s HTTP protocol\n * posts every call to `/rpc/`, so a literal comparison skipped the body limit\n * on the path the product's own client uses.\n *\n * Each segment is therefore taken without its `;` parameter, empty segments are\n * dropped, and what is left is decoded with `decodeURI` and compared without\n * regard to case. `decodeURI` is the decoder rather than `decodeURIComponent`\n * because it leaves a reserved character encoded, which is what keeps\n * `/rpc%2f` a different path here exactly as it is to the router. An invalid\n * escape is left as written, since it names no mount either way.\n *\n * Dot segments are resolved by `URL` before the split. The normalization errs\n * toward matching: a spelling this resolves to a mount that the router then\n * refuses is answered 401 rather than 404, which costs an unauthenticated\n * caller nothing it was entitled to.\n *\n * @param url the request target, origin-relative or absolute\n * @since 1.0.0\n * @category constructors\n */\nexport const routedPath = (url: string): string => {\n // An empty leading segment is a protocol-relative URL to `URL`, so `//rpc`\n // would parse as the host `rpc` and lose the path entirely. Anchoring an\n // origin-relative target on the origin keeps every segment. An absolute\n // target that does not parse at all names no mount, so it is compared as\n // written and reaches the router as the unknown route it is.\n const target = url.startsWith(\"/\") ? `http://gateway.invalid${url}` : url\n const parsed = URL.parse(target, \"http://gateway.invalid\")\n if (parsed === null) return url\n const segments: Array<string> = []\n for (const segment of parsed.pathname.split(\"/\")) {\n const parameter = segment.indexOf(\";\")\n const named = parameter < 0 ? segment : segment.slice(0, parameter)\n if (named === \"\") continue\n try {\n segments.push(decodeURI(named))\n } catch {\n segments.push(named)\n }\n }\n return `/${segments.join(\"/\")}`.toLowerCase()\n}\n\n/**\n * The mount that exchanges a bearer credential for a WebSocket {@link Ticket}.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const ticketPath = \"/auth/ticket\"\n\n/**\n * The protected socket mounts a {@link Ticket} opens.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const ticketPaths: ReadonlyArray<string> = [\"/rpc/ws\", \"/projections/ws\", \"/sync/ws\"]\n\n/**\n * How long an unredeemed {@link Ticket} stays valid (thirty seconds).\n *\n * @since 1.0.0\n * @category constants\n */\nexport const defaultTicketMillis = 30_000\n\n/** Outstanding tickets kept at once; the oldest is dropped past this. */\nconst maxOutstandingTickets = 1024\n\n/**\n * What `POST /auth/ticket` answers.\n *\n * A browser `WebSocket` cannot set an `Authorization` header, so a browser\n * client first posts its bearer to `/auth/ticket`, then opens a protected\n * socket as `<mount>?ticket=<ticket>` within `expiresInMs`. The first upgrade\n * that presents the ticket consumes it, whether or not the upgrade completes;\n * a replay or a late one is refused 401. The socket then runs under the\n * bearer the ticket was issued for, so every frame authenticates as it would\n * with the header.\n *\n * @since 1.0.0\n * @category models\n */\nexport const Ticket = Schema.Struct({\n ticket: Schema.String,\n expiresInMs: Schema.Number\n})\n\n/**\n * What `POST /auth/ticket` answers.\n *\n * @since 1.0.0\n * @category models\n */\nexport type Ticket = typeof Ticket.Type\n\nconst encodeTicket = Schema.encodeUnknownSync(Ticket)\n\n/** 256 random bits, hex-encoded so a query string carries them unescaped. */\nconst newTicket = (): string =>\n Array.from(crypto.getRandomValues(new Uint8Array(32)), (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\")\n\n/** The `ticket` query parameter of a request target, when it has one. */\nconst presentedTicket = (url: string): string | undefined => {\n const parsed = URL.parse(url, \"http://gateway.invalid\")\n const ticket = parsed?.searchParams.get(\"ticket\")\n return ticket === null || ticket === undefined || ticket === \"\" ? undefined : ticket\n}\n\n/**\n * Default maximum request body accepted by an RPC mount (one MiB).\n *\n * @category constants\n * @since 1.0.0\n */\nexport const defaultMaxRequestBodyBytes = 1024 * 1024\n\n/**\n * Ingress policy enforced before an RPC transport parses a request.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface IngressOptions {\n /** Accepted Host header names, without ports. Defaults to loopback names. */\n readonly allowedHosts?: ReadonlyArray<string> | undefined\n readonly maxRequestBodyBytes?: number | undefined\n /** Confine requests to loopback Host values and browser origins. */\n readonly loopbackOnly?: boolean | undefined\n /**\n * Edge authentication for {@link protectedPaths}, told which mount the\n * request reaches. When set, `POST /auth/ticket` exchanges a credential it\n * accepts for a {@link Ticket}, and a redeemed ticket's credential is judged\n * again for the mount its upgrade reaches.\n */\n readonly authorize?: (\n headers: Readonly<Record<string, string>>,\n path: string\n ) => Effect.Effect<boolean>\n /** How long an unredeemed ticket stays valid. Default {@link defaultTicketMillis}. */\n readonly ticketMillis?: number | undefined\n}\n\nconst encodeGatewayError = Schema.encodeUnknownSync(GatewayError)\n\n/** Host parsing stays strict so URL normalization cannot admit a rebinding name. */\nconst requestAuthority = (host: string | undefined): URL | undefined => {\n if (host === undefined || !/^(?:[a-zA-Z0-9.-]+|\\[[a-fA-F0-9:]+\\])(?::[0-9]+)?$/.test(host)) return undefined\n const parsed = URL.parse(`http://${host}`)\n return parsed === null ? undefined : parsed\n}\n\nconst browserRequestRefusal = (\n headers: Readonly<Record<string, string>>,\n allowedHosts: ReadonlyArray<string>,\n loopbackOnly: boolean\n): { readonly error: GatewayError; readonly status: number } | undefined => {\n const authority = requestAuthority(headers.host)\n if (\n authority === undefined || !allowedHosts.includes(authority.hostname) ||\n (loopbackOnly && !loopbackHost.test(headers.host!))\n ) return { error: invalidHostRequest(), status: 421 }\n const origin = headers.origin\n // CLI clients do not send Origin. Browsers must prove they came from this\n // exact authority, including the port, on HTTP requests AND WS upgrades.\n if (origin === undefined) return undefined\n const parsed = URL.parse(origin)\n return parsed !== null && (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") &&\n parsed.origin === origin && parsed.host === authority.host\n ? undefined\n : { error: invalidOriginRequest(), status: 403 }\n}\n\n/** One refusal on the wire: the typed error, under the status it answers. */\nconst refuse = (error: GatewayError, status: number) =>\n HttpServerResponse.jsonUnsafe(encodeGatewayError(error), { status })\n\nconst malformedRequest = (path: string) =>\n new GatewayError({ code: \"malformed_request\", message: `POST ${path} carries no RPC request message` })\n\nconst unauthorizedRequest = () =>\n new GatewayError({ code: \"unauthorized\", message: \"A valid bearer credential is required\" })\n\nconst invalidHostRequest = () =>\n new GatewayError({ code: \"invalid_host\", message: \"This gateway does not allow the supplied Host value\" })\n\nconst invalidOriginRequest = () =>\n new GatewayError({ code: \"invalid_origin\", message: \"The browser Origin must match an allowed gateway Host\" })\n\nconst requestTooLarge = (path: string, maxBytes: number) =>\n new GatewayError({\n code: \"request_too_large\",\n message: `POST ${path} exceeds the ${maxBytes}-byte request limit`\n })\n\n/** The message `@effect/platform-node-shared` `NodeStream` gives a size overflow. */\nconst maxBytesExceeded = \"maxBytes exceeded\"\n\n/**\n * The names that mean this machine only, as a bind address is spelled.\n *\n * This is a list of spellings a person types, not a subnet test: `127.0.0.2`\n * is loopback to the kernel and is not on it. The Node host's\n * `isLoopbackHost`, the Host-header names below, and the ingress regex all\n * derive from this one list so widening it is one edit.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const loopbackHostNames: ReadonlyArray<string> = [\"127.0.0.1\", \"localhost\", \"::1\"]\n\n/**\n * {@link loopbackHostNames} as a Host header spells them: an IPv6 address is\n * bracketed as a URL authority. This is the default `allowedHosts`.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const loopbackHostHeaderNames: ReadonlyArray<string> = loopbackHostNames.map((name) =>\n name.includes(\":\") ? `[${name}]` : name\n)\n\nconst escapeRegExp = (text: string) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\nconst loopbackHost = new RegExp(`^(?:${loopbackHostHeaderNames.map(escapeRegExp).join(\"|\")})(?::\\\\d{1,5})?$`, \"i\")\n\n/**\n * Whether a failed request-body read hit the configured size limit rather than\n * failing for another reason.\n *\n * `@effect/platform-node` raises the limit as an `HttpServerError` whose\n * `reason` is a `RequestParseError` carrying the `Error(\"maxBytes exceeded\")`\n * that `@effect/platform-node-shared` `NodeStream.toString` throws\n * (`NodeHttpIncomingMessage.text` passes `MaxBodySize` in as `maxBytes`, and\n * `NodeHttpServer`'s `ServerRequestImpl` wraps whatever it throws). Nothing on\n * the wire distinguishes it otherwise, so the check is that exact shape and a\n * version bump of those packages is the thing to re-read it against.\n *\n * Every other read failure, a transport reset among them, is not a size\n * overflow. Answering 413 for all of them told a client to shrink a request\n * that was never too big.\n *\n * @param error the failure a body read produced\n * @since 1.0.0\n * @category predicates\n */\nexport const exceededBodyLimit = (error: unknown): boolean => {\n const reason = typeof error === \"object\" && error !== null\n ? (error as { readonly reason?: unknown }).reason\n : undefined\n const cause = typeof reason === \"object\" && reason !== null\n ? (reason as { readonly cause?: unknown }).cause\n : undefined\n return cause instanceof Error && cause.message === maxBytesExceeded\n}\n\n/**\n * The refusal a failed request-body read earns, and the status it answers\n * under.\n *\n * A body over the limit is 413 `request_too_large`. Every other read failure,\n * a truncated body or a reset transport among them, is 400\n * `malformed_request`: the request cannot be retried smaller, because its size\n * was never the problem.\n *\n * @param path the mount the request was made to\n * @param maxBytes the configured limit\n * @param error the failure the body read produced\n * @since 1.0.0\n * @category constructors\n */\nexport const bodyRefusal = (\n path: string,\n maxBytes: number,\n error: unknown\n): { readonly error: GatewayError; readonly status: number } =>\n exceededBodyLimit(error)\n ? { error: requestTooLarge(path, maxBytes), status: 413 }\n : {\n error: new GatewayError({\n code: \"malformed_request\",\n message: `POST ${path} carries a body the server could not read`\n }),\n status: 400\n }\n\n/**\n * Whether a request body carries at least one RPC message the server can act\n * on.\n *\n * The transport's own parser answers, not a second reading of the wire\n * format: whatever `RpcSerialization` the host composed decodes the body, and\n * the answer is no only when that decode throws or yields no tagged message.\n * A body naming a procedure that does not exist is a yes \u2014 that is an\n * RPC-level defect the protocol itself reports.\n *\n * A binary framing is always a yes. Its body is not text, so there is nothing\n * to read here, and the mount owns it.\n *\n * @since 1.0.0\n * @category predicates\n */\nexport const carriesRpcRequest = (\n serialization: RpcSerialization.RpcSerialization[\"Service\"],\n body: string\n): boolean => {\n if (!serialization.contentType.includes(\"json\")) return true\n try {\n const decoded = serialization.makeUnsafe().decode(body)\n return decoded.length > 0 && decoded.every((message: unknown) =>\n typeof message === \"object\" && message !== null &&\n typeof (message as { readonly _tag?: unknown })._tag === \"string\"\n )\n } catch {\n return false\n }\n}\n\n/**\n * Enforces local Host/Origin policy and edge authentication before reading a\n * body or upgrading a socket, then validates RPC body size and framing.\n *\n * `effect/rpc` hands every decoded message to the server loop, and a\n * body that decodes to something else \u2014 `{}`, `[]`, prose, nothing at all \u2014\n * reached it as a message with no tag and died there, so the gateway answered\n * `500 Internal Server Error` with an empty body (release validation). That is the\n * wrong half of the contract twice over: it tells an operator the gateway\n * broke, and it tells a client to retry a request that can never succeed.\n *\n * The body reader is cached per request, so reading the body here does not\n * consume the body the mount reads. It is read through the reader the mount\n * uses, `text` under a JSON serialization and `arrayBuffer` under a binary\n * one, so the bytes the mount decodes are the bytes the client sent.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerIngress = (options: IngressOptions = {}) => {\n const refusal = settingRefusal(\"The gateway request body limit\", options.maxRequestBodyBytes) ??\n settingRefusal(\"The gateway ticket lifetime\", options.ticketMillis)\n if (refusal !== undefined) return Layer.effectDiscard(Effect.fail(refusal))\n const maxBytes = options.maxRequestBodyBytes ?? defaultMaxRequestBodyBytes\n const allowedHosts = options.allowedHosts ?? loopbackHostHeaderNames\n const ticketMillis = options.ticketMillis ?? defaultTicketMillis\n return HttpRouter.middleware(\n Effect.gen(function*() {\n const serialization = yield* RpcSerialization.RpcSerialization\n const binary = !serialization.contentType.includes(\"json\")\n /** Outstanding tickets: the Authorization header each was issued under, and when it lapses. */\n const tickets = new Map<string, { readonly authorization: string; readonly expiresAt: number }>()\n const issue = (authorization: string, now: number): Ticket => {\n for (const [ticket, entry] of tickets) {\n if (entry.expiresAt <= now) tickets.delete(ticket)\n }\n if (tickets.size >= maxOutstandingTickets) tickets.delete(tickets.keys().next().value!)\n const ticket = newTicket()\n tickets.set(ticket, { authorization, expiresAt: now + ticketMillis })\n return { ticket, expiresInMs: ticketMillis }\n }\n // Lookup and delete run without yielding, so of two upgrades presenting\n // one ticket exactly one gets its header.\n const redeem = (url: string, now: number): string | undefined => {\n const ticket = presentedTicket(url)\n const entry = ticket === undefined ? undefined : tickets.get(ticket)\n if (entry === undefined) return undefined\n tickets.delete(ticket!)\n return entry.expiresAt > now ? entry.authorization : undefined\n }\n return (httpEffect: Effect.Effect<HttpServerResponse.HttpServerResponse, Types.unhandled>) =>\n Effect.gen(function*() {\n const request = yield* HttpServerRequest.HttpServerRequest\n const browserRefusal = browserRequestRefusal(request.headers, allowedHosts, options.loopbackOnly === true)\n if (browserRefusal !== undefined) return refuse(browserRefusal.error, browserRefusal.status)\n const path = routedPath(request.url)\n if (path === ticketPath && request.method === \"POST\" && options.authorize !== undefined) {\n const authorization = request.headers.authorization\n if (authorization === undefined || !(yield* options.authorize(request.headers, path))) {\n return refuse(unauthorizedRequest(), 401)\n }\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis)\n return HttpServerResponse.jsonUnsafe(encodeTicket(issue(authorization, now)), {\n headers: { \"cache-control\": \"no-store\" }\n })\n }\n if (protectedPaths.includes(path) && options.authorize !== undefined) {\n const authorized = yield* options.authorize(request.headers, path)\n if (!authorized) {\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis)\n const authorization = ticketPaths.includes(path) ? redeem(request.url, now) : undefined\n if (authorization === undefined) return refuse(unauthorizedRequest(), 401)\n // A ticket stands for the credential it was issued under, which\n // may not reach every mount a ticket can open.\n const headers = Headers.set(request.headers, \"authorization\", authorization)\n if (!(yield* options.authorize(headers, path))) return refuse(unauthorizedRequest(), 401)\n // The socket protocol stamps the upgrade's headers on every\n // frame, so the redeemed bearer authenticates each one.\n return yield* Effect.provideService(\n httpEffect,\n HttpServerRequest.HttpServerRequest,\n request.modify({ headers })\n )\n }\n }\n if (request.method !== \"POST\" || !boundedPostPaths.includes(path)) return yield* httpEffect\n // A declared length is a hint that saves reading a body already\n // known to be too big. It is never trusted the other way: a body\n // that declares less than it sends is still measured by the read,\n // and a chunked body declares nothing at all.\n const declaredLength = Number(request.headers[\"content-length\"])\n if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {\n return refuse(requestTooLarge(path, maxBytes), 413)\n }\n // The body is read through the reader the mount will use. The Node\n // adapter caches whichever reader ran first and derives the other\n // from it, so a text read here would hand a binary mount a UTF-8\n // re-encoding of its MessagePack bytes, with every invalid sequence\n // replaced. A binary body is bounded here and framed by the mount.\n const read = yield* (binary ? Effect.as(request.arrayBuffer, undefined) : request.text).pipe(\n Effect.provideService(HttpServerRequest.MaxBodySize, ByteSize.bytes(maxBytes)),\n Effect.match({\n onFailure: (error): { readonly body: string | undefined; readonly error: unknown } => ({\n body: undefined,\n error\n }),\n onSuccess: (body) => ({ body, error: undefined })\n })\n )\n if (read.error !== undefined) {\n const answer = bodyRefusal(path, maxBytes, read.error)\n return refuse(answer.error, answer.status)\n }\n if (read.body === undefined) return yield* httpEffect\n return !rpcPaths.includes(path) || carriesRpcRequest(serialization, read.body)\n ? yield* httpEffect\n : refuse(malformedRequest(path), 400)\n })\n }),\n { global: true }\n )\n}\n\n/**\n * How an assembled gateway is configured.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface LayerOptions {\n /**\n * How often an idle followed `Watch` on `/rpc/ws` and an idle followed\n * `Projection.Subscribe` on `/projections/ws` emit a keepalive. Unset, the\n * `Watch` keepalive runs at `Projections.heartbeatIntervalMillis` and the\n * supplied `Projections` service keeps the cadence it was built with.\n */\n readonly heartbeatMillis?: number | undefined\n /** The ingress policy the RPC mounts run behind. */\n readonly ingress?: IngressOptions | undefined\n /** Enables the authenticated Go-to-Control runtime bridge. */\n readonly runtimeBridge?: RuntimeBridge.Config | undefined\n}\n\n/**\n * The whole gateway surface, as one application layer a host serves.\n *\n * The caller supplies the transport serialization, the authentication\n * middleware for both RPC groups, and the services the mounts read through\n * (`Control`, `Projections`, `SyncServer`). `@smthrs/gateway/node/NodeGateway`\n * is the Node host that binds this to a socket.\n *\n * A keepalive cadence or a body limit that is not a positive safe integer is\n * refused here, before anything binds, through the layer's typed\n * `GatewayError` channel.\n *\n * @param health the identity `GET /health` answers with\n * @param options the keepalive cadence and the ingress policy\n * @since 1.0.0\n * @category layers\n */\nexport const layer = (health: Health, options: LayerOptions = {}) => {\n const refusal = settingRefusal(\"The gateway keepalive cadence\", options.heartbeatMillis)\n if (refusal !== undefined) return Layer.effectDiscard(Effect.fail(refusal))\n return Layer.mergeAll(\n layerControlHttp(options.heartbeatMillis),\n options.heartbeatMillis === undefined\n ? layerProjectionsHttp\n : layerProjectionsHttp.pipe(Layer.provide(layerProjectionsKeepAlive(options.heartbeatMillis))),\n layerSyncHttp,\n layerHealth(health),\n ...(options.runtimeBridge === undefined\n ? []\n : [RuntimeBridge.layer(options.runtimeBridge), HostBranchPresence.layer(options.runtimeBridge)]),\n layerIngress(options.ingress)\n )\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["decision", "ticket"]
|
|
7
7
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { SyncError } from "@smthrs/sync/SyncError";
|
|
9
9
|
import { Layer } from "effect";
|
|
10
|
-
import { RpcServer } from "effect/
|
|
10
|
+
import { RpcServer } from "effect/rpc";
|
|
11
11
|
import type { Config } from "./RuntimeBridge.ts";
|
|
12
12
|
/**
|
|
13
13
|
* Mount only on authenticated native runtime hosts, never anonymous gateways.
|
|
@@ -15,5 +15,5 @@ import type { Config } from "./RuntimeBridge.ts";
|
|
|
15
15
|
* @category layers
|
|
16
16
|
* @since 1.0.0-rc.1
|
|
17
17
|
*/
|
|
18
|
-
export declare const layer: (config: Config) => Layer.Layer<RpcServer.Protocol, SyncError, import("effect/
|
|
18
|
+
export declare const layer: (config: Config) => Layer.Layer<RpcServer.Protocol, SyncError, import("effect/http/HttpRouter").HttpRouter | import("effect/rpc/RpcSerialization").RpcSerialization>;
|
|
19
19
|
//# sourceMappingURL=HostBranchPresence.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HostBranchPresence.d.ts","sourceRoot":"","sources":["../../src/HostBranchPresence.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAElD,OAAO,EAAU,KAAK,EAAoB,MAAM,QAAQ,CAAA;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"HostBranchPresence.d.ts","sourceRoot":"","sources":["../../src/HostBranchPresence.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAElD,OAAO,EAAU,KAAK,EAAoB,MAAM,QAAQ,CAAA;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,KAAK,GAAI,QAAQ,MAAM,qJAmD/B,CAAA"}
|
|
@@ -37,7 +37,7 @@ var BranchShare = __toESM(require("@smthrs/sync/BranchShare"), 1);
|
|
|
37
37
|
var import_SyncError = require("@smthrs/sync/SyncError");
|
|
38
38
|
var import_SyncRpcs = require("@smthrs/sync/SyncRpcs");
|
|
39
39
|
var import_effect = require("effect");
|
|
40
|
-
var import_rpc = require("effect/
|
|
40
|
+
var import_rpc = require("effect/rpc");
|
|
41
41
|
const layer = (config) => import_effect.Layer.unwrap(import_effect.Effect.gen(function* () {
|
|
42
42
|
const share = yield* BranchShare.makeHmac({
|
|
43
43
|
activeKid: "host",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/HostBranchPresence.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Host-only adapter of the existing branch protocol and lease roster.\n * The Go authorizer resolves branch, actor and socket session before forwarding.\n * The runtime bearer is required on every RPC; participant IDs confer no rights.\n *\n * @since 1.0.0\n */\n\nimport * as BranchPresence from \"@smthrs/sync/BranchPresence\"\nimport type { Announcement, LeaveRequest, RosterRequest } from \"@smthrs/sync/BranchProtocol\"\nimport { BranchRpcs } from \"@smthrs/sync/BranchRpcs\"\nimport * as BranchShare from \"@smthrs/sync/BranchShare\"\nimport { SyncError } from \"@smthrs/sync/SyncError\"\nimport { SyncAuth } from \"@smthrs/sync/SyncRpcs\"\nimport { Effect, Layer, Redacted, Stream } from \"effect\"\nimport { RpcServer } from \"effect/
|
|
4
|
+
"sourcesContent": ["/**\n * Host-only adapter of the existing branch protocol and lease roster.\n * The Go authorizer resolves branch, actor and socket session before forwarding.\n * The runtime bearer is required on every RPC; participant IDs confer no rights.\n *\n * @since 1.0.0\n */\n\nimport * as BranchPresence from \"@smthrs/sync/BranchPresence\"\nimport type { Announcement, LeaveRequest, RosterRequest } from \"@smthrs/sync/BranchProtocol\"\nimport { BranchRpcs } from \"@smthrs/sync/BranchRpcs\"\nimport * as BranchShare from \"@smthrs/sync/BranchShare\"\nimport { SyncError } from \"@smthrs/sync/SyncError\"\nimport { SyncAuth } from \"@smthrs/sync/SyncRpcs\"\nimport { Effect, Layer, Redacted, Stream } from \"effect\"\nimport { RpcServer } from \"effect/rpc\"\nimport type { Config } from \"./RuntimeBridge.ts\"\n\n/**\n * Mount only on authenticated native runtime hosts, never anonymous gateways.\n *\n * @category layers\n * @since 1.0.0-rc.1\n */\nexport const layer = (config: Config) =>\n Layer.unwrap(Effect.gen(function*() {\n const share = yield* BranchShare.makeHmac({\n activeKid: \"host\",\n keys: [{ kid: \"host\", secret: Redacted.make(crypto.randomUUID() + crypto.randomUUID()) }]\n })\n // The authorizing Go host re-checks all source readiness on every health\n // call. The handler below refuses missing assertions before the lease\n // registry, whose own startup window still applies. No readiness cache.\n const presence = yield* BranchPresence.makeMemory({\n sourcesReady: () => Effect.succeed(true)\n }).pipe(Effect.provideService(BranchShare.BranchShare, share))\n // Capability fields on this host-only mount are transport metadata, replaced\n // with an internal signed capability after the runtime bearer is verified.\n const authorize = <A extends RosterRequest>(request: A, actor = \"roster\") =>\n share.mint({\n branchId: request.branchId,\n capabilityId: actor,\n access: \"write\",\n ttlMs: 60_000\n }).pipe(Effect.map((capability) => ({ ...request, capability })))\n const unsupported = new SyncError({ code: \"unsupported\", message: \"This mount serves presence only\" })\n const handlers = BranchRpcs.toLayer(Effect.succeed(BranchRpcs.of({\n \"Branch.CreateBranch\": () => Effect.fail(unsupported),\n \"Branch.MintShare\": () => Effect.fail(unsupported),\n \"Branch.Submit\": () => Effect.fail(unsupported),\n \"Branch.Announce\": (request: Announcement) =>\n authorize(request, request.participantId).pipe(Effect.flatMap(presence.announce)),\n \"Branch.Leave\": (request: LeaveRequest) =>\n authorize(request, request.participantId).pipe(Effect.flatMap(presence.leave), Effect.as(null)),\n \"Branch.Roster\": (request) => authorize(request).pipe(Effect.flatMap(presence.list)),\n \"Branch.PresenceOn\": (request) =>\n authorize(request).pipe(Effect.flatMap((authorized) =>\n request.sourcesReady === true ? presence.presenceOn(authorized) : Effect.succeed(\"unknown\" as const)\n )),\n \"Branch.WatchRoster\": () =>\n Stream.fail(unsupported)\n })))\n const auth = Layer.succeed(SyncAuth)((effect, options) =>\n Effect.gen(function*() {\n yield* config.authenticate(options.headers).pipe(\n Effect.mapError(() => new SyncError({ code: \"unauthorized\", message: \"Host authentication required\" }))\n )\n return yield* effect\n })\n )\n return RpcServer.layer(BranchRpcs, { disableFatalDefects: true }).pipe(\n Layer.provide(handlers),\n Layer.provide(auth),\n Layer.provideMerge(RpcServer.layerProtocolHttp({ path: \"/branch\" }))\n )\n }))\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,qBAAgC;AAEhC,wBAA2B;AAC3B,kBAA6B;AAC7B,uBAA0B;AAC1B,sBAAyB;AACzB,oBAAgD;AAChD,iBAA0B;AASnB,MAAM,QAAQ,CAAC,WACpB,oBAAM,OAAO,qBAAO,IAAI,aAAY;AAClC,QAAM,QAAQ,OAAO,YAAY,SAAS;AAAA,IACxC,WAAW;AAAA,IACX,MAAM,CAAC,EAAE,KAAK,QAAQ,QAAQ,uBAAS,KAAK,OAAO,WAAW,IAAI,OAAO,WAAW,CAAC,EAAE,CAAC;AAAA,EAC1F,CAAC;AAID,QAAM,WAAW,OAAO,eAAe,WAAW;AAAA,IAChD,cAAc,MAAM,qBAAO,QAAQ,IAAI;AAAA,EACzC,CAAC,EAAE,KAAK,qBAAO,eAAe,YAAY,aAAa,KAAK,CAAC;AAG7D,QAAM,YAAY,CAA0B,SAAY,QAAQ,aAC9D,MAAM,KAAK;AAAA,IACT,UAAU,QAAQ;AAAA,IAClB,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,OAAO;AAAA,EACT,CAAC,EAAE,KAAK,qBAAO,IAAI,CAAC,gBAAgB,EAAE,GAAG,SAAS,WAAW,EAAE,CAAC;AAClE,QAAM,cAAc,IAAI,2BAAU,EAAE,MAAM,eAAe,SAAS,kCAAkC,CAAC;AACrG,QAAM,WAAW,6BAAW,QAAQ,qBAAO,QAAQ,6BAAW,GAAG;AAAA,IAC/D,uBAAuB,MAAM,qBAAO,KAAK,WAAW;AAAA,IACpD,oBAAoB,MAAM,qBAAO,KAAK,WAAW;AAAA,IACjD,iBAAiB,MAAM,qBAAO,KAAK,WAAW;AAAA,IAC9C,mBAAmB,CAAC,YAClB,UAAU,SAAS,QAAQ,aAAa,EAAE,KAAK,qBAAO,QAAQ,SAAS,QAAQ,CAAC;AAAA,IAClF,gBAAgB,CAAC,YACf,UAAU,SAAS,QAAQ,aAAa,EAAE,KAAK,qBAAO,QAAQ,SAAS,KAAK,GAAG,qBAAO,GAAG,IAAI,CAAC;AAAA,IAChG,iBAAiB,CAAC,YAAY,UAAU,OAAO,EAAE,KAAK,qBAAO,QAAQ,SAAS,IAAI,CAAC;AAAA,IACnF,qBAAqB,CAAC,YACpB,UAAU,OAAO,EAAE,KAAK,qBAAO;AAAA,MAAQ,CAAC,eACtC,QAAQ,iBAAiB,OAAO,SAAS,WAAW,UAAU,IAAI,qBAAO,QAAQ,SAAkB;AAAA,IACrG,CAAC;AAAA,IACH,sBAAsB,MACpB,qBAAO,KAAK,WAAW;AAAA,EAC3B,CAAC,CAAC,CAAC;AACH,QAAM,OAAO,oBAAM,QAAQ,wBAAQ;AAAA,IAAE,CAAC,QAAQ,YAC5C,qBAAO,IAAI,aAAY;AACrB,aAAO,OAAO,aAAa,QAAQ,OAAO,EAAE;AAAA,QAC1C,qBAAO,SAAS,MAAM,IAAI,2BAAU,EAAE,MAAM,gBAAgB,SAAS,+BAA+B,CAAC,CAAC;AAAA,MACxG;AACA,aAAO,OAAO;AAAA,IAChB,CAAC;AAAA,EACH;AACA,SAAO,qBAAU,MAAM,8BAAY,EAAE,qBAAqB,KAAK,CAAC,EAAE;AAAA,IAChE,oBAAM,QAAQ,QAAQ;AAAA,IACtB,oBAAM,QAAQ,IAAI;AAAA,IAClB,oBAAM,aAAa,qBAAU,kBAAkB,EAAE,MAAM,UAAU,CAAC,CAAC;AAAA,EACrE;AACF,CAAC,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/cjs/RunTrace.d.ts
CHANGED
|
@@ -628,7 +628,7 @@ export declare const monitorFromJournal: (run: TraceRun, records: ReadonlyArray<
|
|
|
628
628
|
graph: {
|
|
629
629
|
id: string;
|
|
630
630
|
label: string;
|
|
631
|
-
state: "failed" | "waiting" | "current" | "
|
|
631
|
+
state: "failed" | "waiting" | "current" | "done" | "next";
|
|
632
632
|
deps: string[];
|
|
633
633
|
}[];
|
|
634
634
|
}[];
|
|
@@ -717,7 +717,7 @@ export declare const monitorFromJournal: (run: TraceRun, records: ReadonlyArray<
|
|
|
717
717
|
graph: {
|
|
718
718
|
id: string;
|
|
719
719
|
label: string;
|
|
720
|
-
state: "failed" | "waiting" | "current" | "
|
|
720
|
+
state: "failed" | "waiting" | "current" | "done" | "next";
|
|
721
721
|
deps: string[];
|
|
722
722
|
}[];
|
|
723
723
|
}[];
|
|
@@ -13,7 +13,7 @@ import { type Service as ControlExecutorService } from "@smthrs/control/ControlE
|
|
|
13
13
|
import type { Principal } from "@smthrs/control/ControlSchema";
|
|
14
14
|
import * as Fault from "@smthrs/flow/Fault";
|
|
15
15
|
import { Effect, Layer, Schema } from "effect";
|
|
16
|
-
import { HttpRouter } from "effect/
|
|
16
|
+
import { HttpRouter } from "effect/http";
|
|
17
17
|
/**
|
|
18
18
|
* The only protocol version accepted by this host.
|
|
19
19
|
* @since 1.0.0
|
|
@@ -821,7 +821,7 @@ export declare const monitor: (control: Control["Service"], runId: string, at?:
|
|
|
821
821
|
graph: {
|
|
822
822
|
id: string;
|
|
823
823
|
label: string;
|
|
824
|
-
state: "failed" | "waiting" | "current" | "
|
|
824
|
+
state: "failed" | "waiting" | "current" | "done" | "next";
|
|
825
825
|
deps: string[];
|
|
826
826
|
}[];
|
|
827
827
|
}[];
|
|
@@ -910,7 +910,7 @@ export declare const monitor: (control: Control["Service"], runId: string, at?:
|
|
|
910
910
|
graph: {
|
|
911
911
|
id: string;
|
|
912
912
|
label: string;
|
|
913
|
-
state: "failed" | "waiting" | "current" | "
|
|
913
|
+
state: "failed" | "waiting" | "current" | "done" | "next";
|
|
914
914
|
deps: string[];
|
|
915
915
|
}[];
|
|
916
916
|
}[];
|
|
@@ -996,7 +996,7 @@ export declare const monitor: (control: Control["Service"], runId: string, at?:
|
|
|
996
996
|
graph: {
|
|
997
997
|
id: string;
|
|
998
998
|
label: string;
|
|
999
|
-
state: "failed" | "waiting" | "current" | "
|
|
999
|
+
state: "failed" | "waiting" | "current" | "done" | "next";
|
|
1000
1000
|
deps: string[];
|
|
1001
1001
|
}[];
|
|
1002
1002
|
}[];
|
|
@@ -1085,7 +1085,7 @@ export declare const monitor: (control: Control["Service"], runId: string, at?:
|
|
|
1085
1085
|
graph: {
|
|
1086
1086
|
id: string;
|
|
1087
1087
|
label: string;
|
|
1088
|
-
state: "failed" | "waiting" | "current" | "
|
|
1088
|
+
state: "failed" | "waiting" | "current" | "done" | "next";
|
|
1089
1089
|
deps: string[];
|
|
1090
1090
|
}[];
|
|
1091
1091
|
}[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuntimeBridge.d.ts","sourceRoot":"","sources":["../../src/RuntimeBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AACjD,OAAO,KAAK,YAAY,MAAM,8BAA8B,CAAA;AAC5D,OAAO,EAAmB,KAAK,OAAO,IAAI,sBAAsB,EAAE,MAAM,iCAAiC,CAAA;AACzG,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,+BAA+B,CAAA;AAS3E,OAAO,KAAK,KAAK,MAAM,oBAAoB,CAAA;AAC3C,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AACtD,OAAO,EAAE,UAAU,EAAyC,MAAM,
|
|
1
|
+
{"version":3,"file":"RuntimeBridge.d.ts","sourceRoot":"","sources":["../../src/RuntimeBridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -54,7 +54,7 @@ var import_ControlExecutor = require("@smthrs/control/ControlExecutor");
|
|
|
54
54
|
var import_ControlSchema = require("@smthrs/control/ControlSchema");
|
|
55
55
|
var Fault = __toESM(require("@smthrs/flow/Fault"), 1);
|
|
56
56
|
var import_effect = require("effect");
|
|
57
|
-
var import_http = require("effect/
|
|
57
|
+
var import_http = require("effect/http");
|
|
58
58
|
var import_GatewayProjection = require("./GatewayProjection.js");
|
|
59
59
|
var Projections = __toESM(require("./Projections.js"), 1);
|
|
60
60
|
var import_RunTrace = require("./RunTrace.js");
|