rivetkit 2.0.2 → 2.0.4-rc.1
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 -5
- package/dist/browser/client.d.ts +2485 -0
- package/dist/browser/client.js +5182 -0
- package/dist/browser/client.js.map +1 -0
- package/dist/browser/inspector/client.d.ts +130 -0
- package/dist/browser/inspector/client.js +2854 -0
- package/dist/browser/inspector/client.js.map +1 -0
- package/dist/browser/v3-DnYObHH3.d.ts +279 -0
- package/dist/inspector.tar.gz +0 -0
- package/dist/schemas/actor-inspector/v1.ts +784 -0
- package/dist/schemas/actor-inspector/v2.ts +796 -0
- package/dist/schemas/actor-inspector/v3.ts +899 -0
- package/dist/schemas/actor-persist/v1.ts +225 -0
- package/dist/schemas/actor-persist/v2.ts +268 -0
- package/dist/schemas/actor-persist/v3.ts +280 -0
- package/dist/schemas/actor-persist/v4.ts +406 -0
- package/dist/schemas/client-protocol/v1.ts +441 -0
- package/dist/schemas/client-protocol/v2.ts +438 -0
- package/dist/schemas/client-protocol/v3.ts +554 -0
- package/dist/schemas/file-system-driver/v1.ts +108 -0
- package/dist/schemas/file-system-driver/v2.ts +142 -0
- package/dist/schemas/file-system-driver/v3.ts +167 -0
- package/dist/schemas/persist/v1.ts +781 -0
- package/dist/schemas/transport/v1.ts +697 -0
- package/dist/tsup/actor/errors.cjs +106 -0
- package/dist/tsup/actor/errors.cjs.map +1 -0
- package/dist/tsup/actor/errors.d.cts +188 -0
- package/dist/tsup/actor/errors.d.ts +188 -0
- package/dist/tsup/actor/errors.js +106 -0
- package/dist/tsup/actor/errors.js.map +1 -0
- package/dist/tsup/actor-router-consts-D29T1Z-K.d.cts +24 -0
- package/dist/tsup/actor-router-consts-D29T1Z-K.d.ts +24 -0
- package/dist/tsup/chunk-325TLXJT.js +1060 -0
- package/dist/tsup/chunk-325TLXJT.js.map +1 -0
- package/dist/tsup/chunk-424PT5DM.js +23 -0
- package/dist/tsup/chunk-424PT5DM.js.map +1 -0
- package/dist/tsup/chunk-4JVIG3SS.cjs +6289 -0
- package/dist/tsup/chunk-4JVIG3SS.cjs.map +1 -0
- package/dist/tsup/chunk-6LJAZ5R4.cjs +96 -0
- package/dist/tsup/chunk-6LJAZ5R4.cjs.map +1 -0
- package/dist/tsup/chunk-6XU3FMCB.cjs +534 -0
- package/dist/tsup/chunk-6XU3FMCB.cjs.map +1 -0
- package/dist/tsup/chunk-7HTNH26M.js +509 -0
- package/dist/tsup/chunk-7HTNH26M.js.map +1 -0
- package/dist/tsup/chunk-AUVH72RE.cjs +5977 -0
- package/dist/tsup/chunk-AUVH72RE.cjs.map +1 -0
- package/dist/tsup/chunk-D4BYUPNQ.js +645 -0
- package/dist/tsup/chunk-D4BYUPNQ.js.map +1 -0
- package/dist/tsup/chunk-HDQ2JUQT.cjs +23 -0
- package/dist/tsup/chunk-HDQ2JUQT.cjs.map +1 -0
- package/dist/tsup/chunk-HHXX2VRM.js +6289 -0
- package/dist/tsup/chunk-HHXX2VRM.js.map +1 -0
- package/dist/tsup/chunk-JEAEA2PB.js +49 -0
- package/dist/tsup/chunk-JEAEA2PB.js.map +1 -0
- package/dist/tsup/chunk-JYSEG3VF.cjs +642 -0
- package/dist/tsup/chunk-JYSEG3VF.cjs.map +1 -0
- package/dist/tsup/chunk-K6DGYILQ.js +2657 -0
- package/dist/tsup/chunk-K6DGYILQ.js.map +1 -0
- package/dist/tsup/chunk-KJSYAUOM.js +96 -0
- package/dist/tsup/chunk-KJSYAUOM.js.map +1 -0
- package/dist/tsup/chunk-L47L3ZWJ.cjs +509 -0
- package/dist/tsup/chunk-L47L3ZWJ.cjs.map +1 -0
- package/dist/tsup/chunk-LXUQ667X.js +2006 -0
- package/dist/tsup/chunk-LXUQ667X.js.map +1 -0
- package/dist/tsup/chunk-MXNPAB5W.js +5977 -0
- package/dist/tsup/chunk-MXNPAB5W.js.map +1 -0
- package/dist/tsup/chunk-N4KRDJ56.js +72 -0
- package/dist/tsup/chunk-N4KRDJ56.js.map +1 -0
- package/dist/tsup/chunk-NIYZDWMW.cjs +2006 -0
- package/dist/tsup/chunk-NIYZDWMW.cjs.map +1 -0
- package/dist/tsup/chunk-PQZHDKRW.cjs +1060 -0
- package/dist/tsup/chunk-PQZHDKRW.cjs.map +1 -0
- package/dist/tsup/chunk-PVOE6BU7.cjs +1050 -0
- package/dist/tsup/chunk-PVOE6BU7.cjs.map +1 -0
- package/dist/tsup/chunk-Q4UD2GA4.cjs +1810 -0
- package/dist/tsup/chunk-Q4UD2GA4.cjs.map +1 -0
- package/dist/tsup/chunk-QUD664YZ.js +1810 -0
- package/dist/tsup/chunk-QUD664YZ.js.map +1 -0
- package/dist/tsup/chunk-RTOCTWME.js +1050 -0
- package/dist/tsup/chunk-RTOCTWME.js.map +1 -0
- package/dist/tsup/chunk-SAZZ4SB2.cjs +2657 -0
- package/dist/tsup/chunk-SAZZ4SB2.cjs.map +1 -0
- package/dist/tsup/chunk-SR3KQE7Q.cjs +72 -0
- package/dist/tsup/chunk-SR3KQE7Q.cjs.map +1 -0
- package/dist/tsup/chunk-V2GHLYC6.cjs +49 -0
- package/dist/tsup/chunk-V2GHLYC6.cjs.map +1 -0
- package/dist/tsup/chunk-V3WG7XTW.cjs +645 -0
- package/dist/tsup/chunk-V3WG7XTW.cjs.map +1 -0
- package/dist/tsup/chunk-VKVNIQRQ.js +257 -0
- package/dist/tsup/chunk-VKVNIQRQ.js.map +1 -0
- package/dist/tsup/chunk-WMPW7JYC.js +642 -0
- package/dist/tsup/chunk-WMPW7JYC.js.map +1 -0
- package/dist/tsup/chunk-Z7HNQ2WF.js +534 -0
- package/dist/tsup/chunk-Z7HNQ2WF.js.map +1 -0
- package/dist/tsup/chunk-ZFY5J2EP.cjs +257 -0
- package/dist/tsup/chunk-ZFY5J2EP.cjs.map +1 -0
- package/dist/tsup/client/mod.cjs +33 -0
- package/dist/tsup/client/mod.cjs.map +1 -0
- package/dist/tsup/client/mod.d.cts +64 -0
- package/dist/tsup/client/mod.d.ts +64 -0
- package/dist/tsup/client/mod.js +33 -0
- package/dist/tsup/client/mod.js.map +1 -0
- package/dist/tsup/common/log.cjs +21 -0
- package/dist/tsup/common/log.cjs.map +1 -0
- package/dist/tsup/common/log.d.cts +34 -0
- package/dist/tsup/common/log.d.ts +34 -0
- package/dist/tsup/common/log.js +21 -0
- package/dist/tsup/common/log.js.map +1 -0
- package/dist/tsup/common/websocket.cjs +10 -0
- package/dist/tsup/common/websocket.cjs.map +1 -0
- package/dist/tsup/common/websocket.d.cts +3 -0
- package/dist/tsup/common/websocket.d.ts +3 -0
- package/dist/tsup/common/websocket.js +10 -0
- package/dist/tsup/common/websocket.js.map +1 -0
- package/dist/tsup/config-BiNoIHRs.d.cts +80 -0
- package/dist/tsup/config-BiNoIHRs.d.ts +80 -0
- package/dist/tsup/config-P3XujgRr.d.ts +2594 -0
- package/dist/tsup/config-_gfywqqI.d.cts +2594 -0
- package/dist/tsup/context-Bxd8Cx4H.d.cts +75 -0
- package/dist/tsup/context-uNA4TRn3.d.ts +75 -0
- package/dist/tsup/db/drizzle/mod.cjs +49 -0
- package/dist/tsup/db/drizzle/mod.cjs.map +1 -0
- package/dist/tsup/db/drizzle/mod.d.cts +17 -0
- package/dist/tsup/db/drizzle/mod.d.ts +17 -0
- package/dist/tsup/db/drizzle/mod.js +49 -0
- package/dist/tsup/db/drizzle/mod.js.map +1 -0
- package/dist/tsup/db/mod.cjs +9 -0
- package/dist/tsup/db/mod.cjs.map +1 -0
- package/dist/tsup/db/mod.d.cts +9 -0
- package/dist/tsup/db/mod.d.ts +9 -0
- package/dist/tsup/db/mod.js +9 -0
- package/dist/tsup/db/mod.js.map +1 -0
- package/dist/tsup/driver-BcLvZcKl.d.cts +13 -0
- package/dist/tsup/driver-CPGHKXyh.d.ts +13 -0
- package/dist/tsup/driver-helpers/mod.cjs +53 -0
- package/dist/tsup/driver-helpers/mod.cjs.map +1 -0
- package/dist/tsup/driver-helpers/mod.d.cts +47 -0
- package/dist/tsup/driver-helpers/mod.d.ts +47 -0
- package/dist/tsup/driver-helpers/mod.js +53 -0
- package/dist/tsup/driver-helpers/mod.js.map +1 -0
- package/dist/tsup/driver-test-suite/mod.cjs +4974 -0
- package/dist/tsup/driver-test-suite/mod.cjs.map +1 -0
- package/dist/tsup/driver-test-suite/mod.d.cts +73 -0
- package/dist/tsup/driver-test-suite/mod.d.ts +73 -0
- package/dist/tsup/driver-test-suite/mod.js +4974 -0
- package/dist/tsup/driver-test-suite/mod.js.map +1 -0
- package/dist/tsup/inspector/mod.cjs +164 -0
- package/dist/tsup/inspector/mod.cjs.map +1 -0
- package/dist/tsup/inspector/mod.d.cts +130 -0
- package/dist/tsup/inspector/mod.d.ts +130 -0
- package/dist/tsup/inspector/mod.js +164 -0
- package/dist/tsup/inspector/mod.js.map +1 -0
- package/dist/tsup/keys-CydblqMh.d.cts +13 -0
- package/dist/tsup/keys-CydblqMh.d.ts +13 -0
- package/dist/tsup/mod.cjs +82 -0
- package/dist/tsup/mod.cjs.map +1 -0
- package/dist/tsup/mod.d.cts +126 -0
- package/dist/tsup/mod.d.ts +126 -0
- package/dist/tsup/mod.js +82 -0
- package/dist/tsup/mod.js.map +1 -0
- package/dist/tsup/serve-test-suite/mod.cjs +2601 -0
- package/dist/tsup/serve-test-suite/mod.cjs.map +1 -0
- package/dist/tsup/serve-test-suite/mod.d.cts +9 -0
- package/dist/tsup/serve-test-suite/mod.d.ts +9 -0
- package/dist/tsup/serve-test-suite/mod.js +2601 -0
- package/dist/tsup/serve-test-suite/mod.js.map +1 -0
- package/dist/tsup/test/mod.cjs +90 -0
- package/dist/tsup/test/mod.cjs.map +1 -0
- package/dist/tsup/test/mod.d.cts +26 -0
- package/dist/tsup/test/mod.d.ts +26 -0
- package/dist/tsup/test/mod.js +90 -0
- package/dist/tsup/test/mod.js.map +1 -0
- package/dist/tsup/utils-fwx3o3K9.d.cts +18 -0
- package/dist/tsup/utils-fwx3o3K9.d.ts +18 -0
- package/dist/tsup/utils.cjs +43 -0
- package/dist/tsup/utils.cjs.map +1 -0
- package/dist/tsup/utils.d.cts +148 -0
- package/dist/tsup/utils.d.ts +148 -0
- package/dist/tsup/utils.js +43 -0
- package/dist/tsup/utils.js.map +1 -0
- package/dist/tsup/v3-DnYObHH3.d.cts +279 -0
- package/dist/tsup/v3-DnYObHH3.d.ts +279 -0
- package/dist/tsup/workflow/mod.cjs +16 -0
- package/dist/tsup/workflow/mod.cjs.map +1 -0
- package/dist/tsup/workflow/mod.d.cts +25 -0
- package/dist/tsup/workflow/mod.d.ts +25 -0
- package/dist/tsup/workflow/mod.js +16 -0
- package/dist/tsup/workflow/mod.js.map +1 -0
- package/package.json +293 -5
- package/src/actor/config.ts +1221 -0
- package/src/actor/conn/driver.ts +61 -0
- package/src/actor/conn/drivers/http.ts +17 -0
- package/src/actor/conn/drivers/raw-request.ts +24 -0
- package/src/actor/conn/drivers/raw-websocket.ts +65 -0
- package/src/actor/conn/drivers/websocket.ts +144 -0
- package/src/actor/conn/mod.ts +288 -0
- package/src/actor/conn/persisted.ts +81 -0
- package/src/actor/conn/state-manager.ts +196 -0
- package/src/actor/contexts/action.ts +47 -0
- package/src/actor/contexts/base/actor.ts +347 -0
- package/src/actor/contexts/base/conn-init.ts +68 -0
- package/src/actor/contexts/base/conn.ts +73 -0
- package/src/actor/contexts/before-action-response.ts +42 -0
- package/src/actor/contexts/before-connect.ts +31 -0
- package/src/actor/contexts/connect.ts +42 -0
- package/src/actor/contexts/create-conn-state.ts +32 -0
- package/src/actor/contexts/create-vars.ts +39 -0
- package/src/actor/contexts/create.ts +39 -0
- package/src/actor/contexts/destroy.ts +42 -0
- package/src/actor/contexts/disconnect.ts +43 -0
- package/src/actor/contexts/index.ts +33 -0
- package/src/actor/contexts/request.ts +80 -0
- package/src/actor/contexts/run.ts +47 -0
- package/src/actor/contexts/sleep.ts +42 -0
- package/src/actor/contexts/state-change.ts +42 -0
- package/src/actor/contexts/wake.ts +42 -0
- package/src/actor/contexts/websocket.ts +80 -0
- package/src/actor/database.ts +13 -0
- package/src/actor/definition.ts +64 -0
- package/src/actor/driver.ts +114 -0
- package/src/actor/errors.ts +556 -0
- package/src/actor/instance/connection-manager.ts +574 -0
- package/src/actor/instance/event-manager.ts +314 -0
- package/src/actor/instance/keys.ts +146 -0
- package/src/actor/instance/kv.ts +241 -0
- package/src/actor/instance/mod.ts +1658 -0
- package/src/actor/instance/persisted.ts +67 -0
- package/src/actor/instance/queue-manager.ts +603 -0
- package/src/actor/instance/queue.ts +345 -0
- package/src/actor/instance/schedule-manager.ts +392 -0
- package/src/actor/instance/state-manager.ts +542 -0
- package/src/actor/instance/traces-driver.ts +128 -0
- package/src/actor/keys.test.ts +275 -0
- package/src/actor/keys.ts +89 -0
- package/src/actor/log.ts +6 -0
- package/src/actor/mod.ts +110 -0
- package/src/actor/protocol/old.ts +416 -0
- package/src/actor/protocol/serde.ts +222 -0
- package/src/actor/router-endpoints.ts +400 -0
- package/src/actor/router-websocket-endpoints.test.ts +54 -0
- package/src/actor/router-websocket-endpoints.ts +405 -0
- package/src/actor/router.ts +380 -0
- package/src/actor/schedule.ts +17 -0
- package/src/actor/schema.ts +291 -0
- package/src/actor/utils.test.ts +48 -0
- package/src/actor/utils.ts +158 -0
- package/src/client/actor-common.ts +32 -0
- package/src/client/actor-conn.ts +1262 -0
- package/src/client/actor-handle.ts +344 -0
- package/src/client/actor-query.ts +112 -0
- package/src/client/client.ts +558 -0
- package/src/client/config.ts +151 -0
- package/src/client/errors.ts +76 -0
- package/src/client/log.ts +5 -0
- package/src/client/mod.browser.ts +2 -0
- package/src/client/mod.ts +70 -0
- package/src/client/queue.ts +146 -0
- package/src/client/raw-utils.ts +149 -0
- package/src/client/test.ts +44 -0
- package/src/client/utils.ts +252 -0
- package/src/common/actor-router-consts.ts +59 -0
- package/src/common/cors.ts +57 -0
- package/src/common/eventsource-interface.ts +47 -0
- package/src/common/eventsource.ts +44 -0
- package/src/common/inline-websocket-adapter.ts +154 -0
- package/src/common/log-levels.ts +27 -0
- package/src/common/log.ts +229 -0
- package/src/common/logfmt.ts +221 -0
- package/src/common/network.ts +2 -0
- package/src/common/router.ts +174 -0
- package/src/common/utils.ts +339 -0
- package/src/common/websocket-interface.ts +7 -0
- package/src/common/websocket.ts +43 -0
- package/src/db/config.ts +100 -0
- package/src/db/drizzle/mod.ts +226 -0
- package/src/db/drizzle/sqlite-core.ts +22 -0
- package/src/db/mod.ts +125 -0
- package/src/db/shared.ts +92 -0
- package/src/db/sqlite-vfs.ts +12 -0
- package/src/devtools-loader/index.ts +33 -0
- package/src/devtools-loader/log.ts +5 -0
- package/src/driver-helpers/mod.ts +33 -0
- package/src/driver-helpers/utils.ts +54 -0
- package/src/driver-test-suite/log.ts +5 -0
- package/src/driver-test-suite/mod.ts +293 -0
- package/src/driver-test-suite/test-inline-client-driver.ts +307 -0
- package/src/driver-test-suite/tests/access-control.ts +218 -0
- package/src/driver-test-suite/tests/action-features.ts +203 -0
- package/src/driver-test-suite/tests/actor-conn-hibernation.ts +152 -0
- package/src/driver-test-suite/tests/actor-conn-state.ts +300 -0
- package/src/driver-test-suite/tests/actor-conn.ts +596 -0
- package/src/driver-test-suite/tests/actor-db-raw.ts +73 -0
- package/src/driver-test-suite/tests/actor-db.ts +477 -0
- package/src/driver-test-suite/tests/actor-destroy.ts +294 -0
- package/src/driver-test-suite/tests/actor-driver.ts +18 -0
- package/src/driver-test-suite/tests/actor-error-handling.ts +150 -0
- package/src/driver-test-suite/tests/actor-handle.ts +312 -0
- package/src/driver-test-suite/tests/actor-inline-client.ts +163 -0
- package/src/driver-test-suite/tests/actor-inspector.ts +264 -0
- package/src/driver-test-suite/tests/actor-kv.ts +65 -0
- package/src/driver-test-suite/tests/actor-metadata.ts +116 -0
- package/src/driver-test-suite/tests/actor-onstatechange.ts +95 -0
- package/src/driver-test-suite/tests/actor-queue.ts +325 -0
- package/src/driver-test-suite/tests/actor-run.ts +181 -0
- package/src/driver-test-suite/tests/actor-schedule.ts +97 -0
- package/src/driver-test-suite/tests/actor-sleep.ts +415 -0
- package/src/driver-test-suite/tests/actor-state.ts +54 -0
- package/src/driver-test-suite/tests/actor-stateless.ts +70 -0
- package/src/driver-test-suite/tests/actor-vars.ts +97 -0
- package/src/driver-test-suite/tests/actor-workflow.ts +118 -0
- package/src/driver-test-suite/tests/manager-driver.ts +388 -0
- package/src/driver-test-suite/tests/raw-http-direct-registry.ts +227 -0
- package/src/driver-test-suite/tests/raw-http-request-properties.ts +454 -0
- package/src/driver-test-suite/tests/raw-http.ts +359 -0
- package/src/driver-test-suite/tests/raw-websocket-direct-registry.ts +393 -0
- package/src/driver-test-suite/tests/raw-websocket.ts +513 -0
- package/src/driver-test-suite/tests/request-access.ts +240 -0
- package/src/driver-test-suite/utils.ts +80 -0
- package/src/drivers/default.ts +38 -0
- package/src/drivers/engine/actor-driver.ts +1027 -0
- package/src/drivers/engine/config.ts +43 -0
- package/src/drivers/engine/log.ts +5 -0
- package/src/drivers/engine/mod.ts +36 -0
- package/src/drivers/file-system/actor.ts +102 -0
- package/src/drivers/file-system/global-state.ts +1445 -0
- package/src/drivers/file-system/kv-limits.ts +70 -0
- package/src/drivers/file-system/log.ts +5 -0
- package/src/drivers/file-system/manager.ts +300 -0
- package/src/drivers/file-system/mod.ts +78 -0
- package/src/drivers/file-system/sqlite-runtime.ts +210 -0
- package/src/drivers/file-system/utils.ts +125 -0
- package/src/engine-process/constants.ts +2 -0
- package/src/engine-process/log.ts +5 -0
- package/src/engine-process/mod.ts +464 -0
- package/src/globals.d.ts +35 -0
- package/src/inspector/actor-inspector.ts +352 -0
- package/src/inspector/config.ts +49 -0
- package/src/inspector/handler.ts +273 -0
- package/src/inspector/log.ts +5 -0
- package/src/inspector/mod.browser.ts +8 -0
- package/src/inspector/mod.ts +4 -0
- package/src/inspector/serve-ui.ts +40 -0
- package/src/inspector/transport.ts +18 -0
- package/src/inspector/utils.ts +32 -0
- package/src/manager/driver.ts +106 -0
- package/src/manager/gateway.ts +668 -0
- package/src/manager/log.ts +5 -0
- package/src/manager/mod.ts +2 -0
- package/src/manager/protocol/mod.ts +22 -0
- package/src/manager/protocol/query.ts +85 -0
- package/src/manager/router-schema.ts +22 -0
- package/src/manager/router.ts +660 -0
- package/src/manager-api/actors.ts +83 -0
- package/src/manager-api/common.ts +4 -0
- package/src/mod.ts +24 -0
- package/src/registry/config/driver.ts +21 -0
- package/src/registry/config/index.ts +510 -0
- package/src/registry/config/legacy-runner.ts +157 -0
- package/src/registry/config/runner.ts +21 -0
- package/src/registry/config/serverless.ts +94 -0
- package/src/registry/index.ts +194 -0
- package/src/registry/log.ts +5 -0
- package/src/remote-manager-driver/actor-http-client.ts +84 -0
- package/src/remote-manager-driver/actor-websocket-client.ts +81 -0
- package/src/remote-manager-driver/api-endpoints.ts +159 -0
- package/src/remote-manager-driver/api-utils.ts +69 -0
- package/src/remote-manager-driver/log.ts +5 -0
- package/src/remote-manager-driver/metadata.ts +64 -0
- package/src/remote-manager-driver/mod.ts +414 -0
- package/src/remote-manager-driver/ws-proxy.ts +189 -0
- package/src/schemas/actor-inspector/mod.ts +1 -0
- package/src/schemas/actor-inspector/versioned.ts +233 -0
- package/src/schemas/actor-persist/mod.ts +1 -0
- package/src/schemas/actor-persist/versioned.ts +217 -0
- package/src/schemas/client-protocol/mod.ts +1 -0
- package/src/schemas/client-protocol/versioned.ts +330 -0
- package/src/schemas/client-protocol-zod/mod.ts +118 -0
- package/src/schemas/file-system-driver/mod.ts +1 -0
- package/src/schemas/file-system-driver/versioned.ts +135 -0
- package/src/schemas/persist/mod.ts +1 -0
- package/src/schemas/transport/mod.ts +1 -0
- package/src/serde.ts +138 -0
- package/src/serve-test-suite/mod.ts +148 -0
- package/src/serverless/configure.ts +82 -0
- package/src/serverless/log.ts +5 -0
- package/src/serverless/router.test.ts +299 -0
- package/src/serverless/router.ts +215 -0
- package/src/test/log.ts +5 -0
- package/src/test/mod.ts +99 -0
- package/src/utils/crypto.ts +24 -0
- package/src/utils/endpoint-parser.test.ts +202 -0
- package/src/utils/endpoint-parser.ts +124 -0
- package/src/utils/env-vars.ts +78 -0
- package/src/utils/node.ts +178 -0
- package/src/utils/router.ts +83 -0
- package/src/utils/serve.ts +212 -0
- package/src/utils.test.ts +34 -0
- package/src/utils.ts +437 -0
- package/src/workflow/constants.ts +2 -0
- package/src/workflow/context.ts +597 -0
- package/src/workflow/driver.ts +194 -0
- package/src/workflow/inspector.ts +268 -0
- package/src/workflow/mod.ts +128 -0
|
@@ -0,0 +1,1027 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ActorConfig as EngineActorConfig,
|
|
3
|
+
RunnerConfig as EngineRunnerConfig,
|
|
4
|
+
HibernatingWebSocketMetadata,
|
|
5
|
+
} from "@rivetkit/engine-runner";
|
|
6
|
+
import type { SqliteVfs } from "@rivetkit/sqlite-vfs";
|
|
7
|
+
import { idToStr, Runner } from "@rivetkit/engine-runner";
|
|
8
|
+
import * as cbor from "cbor-x";
|
|
9
|
+
import type { Context as HonoContext } from "hono";
|
|
10
|
+
import { streamSSE } from "hono/streaming";
|
|
11
|
+
import { WSContext, type WSContextInit } from "hono/ws";
|
|
12
|
+
import invariant from "invariant";
|
|
13
|
+
import { type AnyConn, CONN_STATE_MANAGER_SYMBOL } from "@/actor/conn/mod";
|
|
14
|
+
import { lookupInRegistry } from "@/actor/definition";
|
|
15
|
+
import { KEYS } from "@/actor/instance/keys";
|
|
16
|
+
import { deserializeActorKey } from "@/actor/keys";
|
|
17
|
+
import { getValueLength } from "@/actor/protocol/old";
|
|
18
|
+
import { type ActorRouter, createActorRouter } from "@/actor/router";
|
|
19
|
+
import {
|
|
20
|
+
parseWebSocketProtocols,
|
|
21
|
+
routeWebSocket,
|
|
22
|
+
truncateRawWebSocketPathPrefix,
|
|
23
|
+
type UpgradeWebSocketArgs,
|
|
24
|
+
} from "@/actor/router-websocket-endpoints";
|
|
25
|
+
import type { Client } from "@/client/client";
|
|
26
|
+
import {
|
|
27
|
+
PATH_CONNECT,
|
|
28
|
+
PATH_INSPECTOR_CONNECT,
|
|
29
|
+
PATH_WEBSOCKET_BASE,
|
|
30
|
+
PATH_WEBSOCKET_PREFIX,
|
|
31
|
+
} from "@/common/actor-router-consts";
|
|
32
|
+
import { getLogger } from "@/common/log";
|
|
33
|
+
import type {
|
|
34
|
+
RivetMessageEvent,
|
|
35
|
+
UniversalWebSocket,
|
|
36
|
+
} from "@/common/websocket-interface";
|
|
37
|
+
import {
|
|
38
|
+
type ActorDriver,
|
|
39
|
+
type AnyActorInstance,
|
|
40
|
+
getInitialActorKvState,
|
|
41
|
+
type ManagerDriver,
|
|
42
|
+
} from "@/driver-helpers/mod";
|
|
43
|
+
import { buildActorNames, type RegistryConfig } from "@/registry/config";
|
|
44
|
+
import { getEndpoint } from "@/remote-manager-driver/api-utils";
|
|
45
|
+
import {
|
|
46
|
+
type LongTimeoutHandle,
|
|
47
|
+
promiseWithResolvers,
|
|
48
|
+
setLongTimeout,
|
|
49
|
+
stringifyError,
|
|
50
|
+
VERSION,
|
|
51
|
+
} from "@/utils";
|
|
52
|
+
import { logger } from "./log";
|
|
53
|
+
|
|
54
|
+
const RUNNER_SSE_PING_INTERVAL = 1000;
|
|
55
|
+
const RUNNER_STOP_WAIT_MS = 15_000;
|
|
56
|
+
|
|
57
|
+
// Message ack deadline is 30s on the gateway, but we will ack more frequently
|
|
58
|
+
// in order to minimize the message buffer size on the gateway and to give
|
|
59
|
+
// generous breathing room for the timeout.
|
|
60
|
+
//
|
|
61
|
+
// See engine/packages/pegboard-gateway/src/shared_state.rs
|
|
62
|
+
// (HWS_MESSAGE_ACK_TIMEOUT)
|
|
63
|
+
const CONN_MESSAGE_ACK_DEADLINE = 5_000;
|
|
64
|
+
|
|
65
|
+
// Force saveState when cumulative message size reaches this threshold (0.5 MB)
|
|
66
|
+
//
|
|
67
|
+
// See engine/packages/pegboard-gateway/src/shared_state.rs
|
|
68
|
+
// (HWS_MAX_PENDING_MSGS_SIZE_PER_REQ)
|
|
69
|
+
const CONN_BUFFERED_MESSAGE_SIZE_THRESHOLD = 500_000;
|
|
70
|
+
|
|
71
|
+
interface ActorHandler {
|
|
72
|
+
actor?: AnyActorInstance;
|
|
73
|
+
actorStartPromise?: ReturnType<typeof promiseWithResolvers<void>>;
|
|
74
|
+
actorStartError?: Error;
|
|
75
|
+
alarmTimeout?: LongTimeoutHandle;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type DriverContext = {};
|
|
79
|
+
|
|
80
|
+
export class EngineActorDriver implements ActorDriver {
|
|
81
|
+
#config: RegistryConfig;
|
|
82
|
+
#managerDriver: ManagerDriver;
|
|
83
|
+
#inlineClient: Client<any>;
|
|
84
|
+
#runner: Runner;
|
|
85
|
+
#actors: Map<string, ActorHandler> = new Map();
|
|
86
|
+
#actorRouter: ActorRouter;
|
|
87
|
+
|
|
88
|
+
#runnerStarted: PromiseWithResolvers<undefined> = promiseWithResolvers((reason) => logger().warn({ msg: "unhandled runner started promise rejection", reason }));
|
|
89
|
+
#runnerStopped: PromiseWithResolvers<undefined> = promiseWithResolvers((reason) => logger().warn({ msg: "unhandled runner stopped promise rejection", reason }));
|
|
90
|
+
#isRunnerStopped: boolean = false;
|
|
91
|
+
|
|
92
|
+
// HACK: Track actor stop intent locally since the runner protocol doesn't
|
|
93
|
+
// pass the stop reason to onActorStop. This will be fixed when the runner
|
|
94
|
+
// protocol is updated to send the intent directly (see RVT-5284)
|
|
95
|
+
#actorStopIntent: Map<string, "sleep" | "destroy"> = new Map();
|
|
96
|
+
|
|
97
|
+
// Map of conn IDs to message index waiting to be persisted before sending
|
|
98
|
+
// an ack
|
|
99
|
+
//
|
|
100
|
+
// serverMessageIndex is updated and pendingAck is flagged in needed in
|
|
101
|
+
// onBeforePersistConnect, then the HWS ack message is sent in
|
|
102
|
+
// onAfterPersistConn. This allows us to track what's about to be written
|
|
103
|
+
// to storage to prevent race conditions with the serverMessageIndex being
|
|
104
|
+
// updated while writing the existing state.
|
|
105
|
+
//
|
|
106
|
+
// bufferedMessageSize tracks the total bytes received since last persist
|
|
107
|
+
// to force a saveState when threshold is reached. This is the amount of
|
|
108
|
+
// data currently buffered on the gateway.
|
|
109
|
+
#hwsMessageIndex = new Map<
|
|
110
|
+
string,
|
|
111
|
+
{
|
|
112
|
+
serverMessageIndex: number;
|
|
113
|
+
bufferedMessageSize: number;
|
|
114
|
+
pendingAckFromMessageIndex: boolean;
|
|
115
|
+
pendingAckFromBufferSize: boolean;
|
|
116
|
+
}
|
|
117
|
+
>();
|
|
118
|
+
|
|
119
|
+
constructor(
|
|
120
|
+
config: RegistryConfig,
|
|
121
|
+
managerDriver: ManagerDriver,
|
|
122
|
+
inlineClient: Client<any>,
|
|
123
|
+
) {
|
|
124
|
+
this.#config = config;
|
|
125
|
+
this.#managerDriver = managerDriver;
|
|
126
|
+
this.#inlineClient = inlineClient;
|
|
127
|
+
|
|
128
|
+
// HACK: Override inspector token (which are likely to be
|
|
129
|
+
// removed later on) with token from x-rivet-token header
|
|
130
|
+
const token = config.token;
|
|
131
|
+
// TODO:
|
|
132
|
+
// if (token && runConfig.inspector && runConfig.inspector.enabled) {
|
|
133
|
+
// runConfig.inspector.token = () => token;
|
|
134
|
+
// }
|
|
135
|
+
|
|
136
|
+
this.#actorRouter = createActorRouter(
|
|
137
|
+
config,
|
|
138
|
+
this,
|
|
139
|
+
undefined,
|
|
140
|
+
config.test.enabled,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// Create runner configuration
|
|
144
|
+
const engineRunnerConfig: EngineRunnerConfig = {
|
|
145
|
+
version: config.runner.version,
|
|
146
|
+
endpoint: getEndpoint(config),
|
|
147
|
+
token,
|
|
148
|
+
namespace: config.namespace,
|
|
149
|
+
totalSlots: config.runner.totalSlots,
|
|
150
|
+
runnerName: config.runner.runnerName,
|
|
151
|
+
runnerKey: config.runner.runnerKey ?? crypto.randomUUID(),
|
|
152
|
+
metadata: {
|
|
153
|
+
rivetkit: { version: VERSION },
|
|
154
|
+
},
|
|
155
|
+
prepopulateActorNames: buildActorNames(config),
|
|
156
|
+
onConnected: () => {
|
|
157
|
+
this.#runnerStarted.resolve(undefined);
|
|
158
|
+
},
|
|
159
|
+
onDisconnected: (_code, _reason) => {},
|
|
160
|
+
onShutdown: () => {
|
|
161
|
+
this.#runnerStopped.resolve(undefined);
|
|
162
|
+
this.#isRunnerStopped = true;
|
|
163
|
+
},
|
|
164
|
+
fetch: this.#runnerFetch.bind(this),
|
|
165
|
+
websocket: this.#runnerWebSocket.bind(this),
|
|
166
|
+
hibernatableWebSocket: {
|
|
167
|
+
canHibernate: this.#hwsCanHibernate.bind(this),
|
|
168
|
+
},
|
|
169
|
+
onActorStart: this.#runnerOnActorStart.bind(this),
|
|
170
|
+
onActorStop: this.#runnerOnActorStop.bind(this),
|
|
171
|
+
logger: getLogger("engine-runner"),
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Create and start runner
|
|
175
|
+
this.#runner = new Runner(engineRunnerConfig);
|
|
176
|
+
this.#runner.start();
|
|
177
|
+
logger().debug({
|
|
178
|
+
msg: "engine runner started",
|
|
179
|
+
endpoint: config.endpoint,
|
|
180
|
+
namespace: config.namespace,
|
|
181
|
+
runnerName: config.runner.runnerName,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
getExtraActorLogParams(): Record<string, string> {
|
|
186
|
+
return { runnerId: this.#runner.runnerId ?? "-" };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async #loadActorHandler(actorId: string): Promise<ActorHandler> {
|
|
190
|
+
// Check if actor is already loaded
|
|
191
|
+
const handler = this.#actors.get(actorId);
|
|
192
|
+
if (!handler)
|
|
193
|
+
throw new Error(`Actor handler does not exist ${actorId}`);
|
|
194
|
+
if (handler.actorStartPromise) await handler.actorStartPromise.promise;
|
|
195
|
+
if (handler.actorStartError) throw handler.actorStartError;
|
|
196
|
+
if (!handler.actor) throw new Error("Actor should be loaded");
|
|
197
|
+
return handler;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
getContext(actorId: string): DriverContext {
|
|
201
|
+
return {};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async setAlarm(actor: AnyActorInstance, timestamp: number): Promise<void> {
|
|
205
|
+
const handler = this.#actors.get(actor.id);
|
|
206
|
+
if (!handler) {
|
|
207
|
+
logger().warn({
|
|
208
|
+
msg: "no handler for actor to set alarm",
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Clear prev timeout
|
|
215
|
+
if (handler.alarmTimeout) {
|
|
216
|
+
handler.alarmTimeout.abort();
|
|
217
|
+
handler.alarmTimeout = undefined;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Set alarm
|
|
221
|
+
const delay = Math.max(0, timestamp - Date.now());
|
|
222
|
+
handler.alarmTimeout = setLongTimeout(() => {
|
|
223
|
+
actor.onAlarm();
|
|
224
|
+
handler.alarmTimeout = undefined;
|
|
225
|
+
}, delay);
|
|
226
|
+
|
|
227
|
+
// TODO: This call may not be needed on ActorInstance.start, but it does help ensure that the local state is synced with the alarm state
|
|
228
|
+
// Set alarm on Rivet
|
|
229
|
+
//
|
|
230
|
+
// This does not call an "alarm" event like Durable Objects.
|
|
231
|
+
// Instead, it just wakes the actor on the alarm (if not
|
|
232
|
+
// already awake).
|
|
233
|
+
//
|
|
234
|
+
// onAlarm is automatically called on `ActorInstance.start` when waking
|
|
235
|
+
// again.
|
|
236
|
+
this.#runner.setAlarm(actor.id, timestamp);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// No database overrides - will use KV-backed implementation from rivetkit/db
|
|
240
|
+
|
|
241
|
+
// MARK: - Batch KV operations
|
|
242
|
+
async kvBatchPut(
|
|
243
|
+
actorId: string,
|
|
244
|
+
entries: [Uint8Array, Uint8Array][],
|
|
245
|
+
): Promise<void> {
|
|
246
|
+
await this.#runner.kvPut(actorId, entries);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async kvBatchGet(
|
|
250
|
+
actorId: string,
|
|
251
|
+
keys: Uint8Array[],
|
|
252
|
+
): Promise<(Uint8Array | null)[]> {
|
|
253
|
+
return await this.#runner.kvGet(actorId, keys);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async kvBatchDelete(actorId: string, keys: Uint8Array[]): Promise<void> {
|
|
257
|
+
await this.#runner.kvDelete(actorId, keys);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async kvList(actorId: string): Promise<Uint8Array[]> {
|
|
261
|
+
const entries = await this.#runner.kvListPrefix(
|
|
262
|
+
actorId,
|
|
263
|
+
new Uint8Array(),
|
|
264
|
+
);
|
|
265
|
+
const keys = entries.map(([key]) => key);
|
|
266
|
+
logger().info({
|
|
267
|
+
msg: "kvList called",
|
|
268
|
+
actorId,
|
|
269
|
+
keysCount: keys.length,
|
|
270
|
+
keys: keys.map((k) => new TextDecoder().decode(k)),
|
|
271
|
+
});
|
|
272
|
+
return keys;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async kvListPrefix(
|
|
276
|
+
actorId: string,
|
|
277
|
+
prefix: Uint8Array,
|
|
278
|
+
): Promise<[Uint8Array, Uint8Array][]> {
|
|
279
|
+
const result = await this.#runner.kvListPrefix(actorId, prefix);
|
|
280
|
+
logger().info({
|
|
281
|
+
msg: "kvListPrefix called",
|
|
282
|
+
actorId,
|
|
283
|
+
prefixStr: new TextDecoder().decode(prefix),
|
|
284
|
+
entriesCount: result.length,
|
|
285
|
+
keys: result.map(([key]) => new TextDecoder().decode(key)),
|
|
286
|
+
});
|
|
287
|
+
return result;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Creates a SQLite VFS instance for creating KV-backed databases */
|
|
291
|
+
async createSqliteVfs(): Promise<SqliteVfs> {
|
|
292
|
+
// Dynamic import keeps @rivetkit/sqlite out of the main entrypoint bundle.
|
|
293
|
+
// Returning a fresh SqliteVfs gives each actor an isolated sqlite module
|
|
294
|
+
// instance, avoiding async re-entrancy across actors.
|
|
295
|
+
const specifier = "@rivetkit/" + "sqlite-vfs";
|
|
296
|
+
const { SqliteVfs } = await import(specifier);
|
|
297
|
+
return new SqliteVfs();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// MARK: - Actor Lifecycle
|
|
301
|
+
async loadActor(actorId: string): Promise<AnyActorInstance> {
|
|
302
|
+
const handler = await this.#loadActorHandler(actorId);
|
|
303
|
+
if (!handler.actor) throw new Error(`Actor ${actorId} failed to load`);
|
|
304
|
+
return handler.actor;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
startSleep(actorId: string) {
|
|
308
|
+
// HACK: Track intent for onActorStop (see RVT-5284)
|
|
309
|
+
this.#actorStopIntent.set(actorId, "sleep");
|
|
310
|
+
this.#runner.sleepActor(actorId);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
startDestroy(actorId: string) {
|
|
314
|
+
// HACK: Track intent for onActorStop (see RVT-5284)
|
|
315
|
+
this.#actorStopIntent.set(actorId, "destroy");
|
|
316
|
+
this.#runner.stopActor(actorId);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async shutdownRunner(immediate: boolean): Promise<void> {
|
|
320
|
+
logger().info({ msg: "stopping engine actor driver", immediate });
|
|
321
|
+
|
|
322
|
+
// TODO: We need to update the runner to have a draining state so:
|
|
323
|
+
// 1. Send ToServerDraining
|
|
324
|
+
// - This causes Pegboard to stop allocating actors to this runner
|
|
325
|
+
// 2. Pegboard sends ToClientStopActor for all actors on this runner which handles the graceful migration of each actor independently
|
|
326
|
+
// 3. Send ToServerStopping once all actors have successfully stopped
|
|
327
|
+
//
|
|
328
|
+
// What's happening right now is:
|
|
329
|
+
// 1. All actors enter stopped state
|
|
330
|
+
// 2. Actors still respond to requests because only RivetKit knows it's
|
|
331
|
+
// stopping, this causes all requests to issue errors that the actor is
|
|
332
|
+
// stopping. (This will NOT return a 503 bc the runner has no idea the
|
|
333
|
+
// actors are stopping.)
|
|
334
|
+
// 3. Once the last actor stops, then the runner finally stops + actors
|
|
335
|
+
// reschedule
|
|
336
|
+
//
|
|
337
|
+
// This means that:
|
|
338
|
+
// - All actors on this runner are bricked until the slowest onStop finishes
|
|
339
|
+
// - Guard will not gracefully handle requests bc it's not receiving a 503
|
|
340
|
+
// - Actors can still be scheduled to this runner while the other
|
|
341
|
+
// actors are stopping, meaning that those actors will NOT get onStop
|
|
342
|
+
// and will potentiall corrupt their state
|
|
343
|
+
//
|
|
344
|
+
// HACK: Stop all actors to allow state to be saved
|
|
345
|
+
// NOTE: onStop is only supposed to be called by the runner, we're
|
|
346
|
+
// abusing it here
|
|
347
|
+
logger().debug({
|
|
348
|
+
msg: "stopping all actors before shutdown",
|
|
349
|
+
actorCount: this.#actors.size,
|
|
350
|
+
});
|
|
351
|
+
const stopPromises: Promise<void>[] = [];
|
|
352
|
+
for (const [_actorId, handler] of this.#actors.entries()) {
|
|
353
|
+
if (handler.actor) {
|
|
354
|
+
stopPromises.push(
|
|
355
|
+
handler.actor.onStop("sleep").catch((err) => {
|
|
356
|
+
handler.actor?.rLog.error({
|
|
357
|
+
msg: "onStop errored",
|
|
358
|
+
error: stringifyError(err),
|
|
359
|
+
});
|
|
360
|
+
}),
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
await Promise.all(stopPromises);
|
|
365
|
+
logger().debug({ msg: "all actors stopped" });
|
|
366
|
+
|
|
367
|
+
try {
|
|
368
|
+
await this.#runner.shutdown(immediate);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
371
|
+
if (message.includes("WebSocket connection closed during shutdown")) {
|
|
372
|
+
logger().debug({
|
|
373
|
+
msg: "ignoring shutdown websocket close race",
|
|
374
|
+
error: message,
|
|
375
|
+
});
|
|
376
|
+
} else {
|
|
377
|
+
throw error;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const stopped = await Promise.race([
|
|
382
|
+
this.#runnerStopped.promise.then(() => true),
|
|
383
|
+
new Promise<false>((resolve) =>
|
|
384
|
+
setTimeout(() => resolve(false), RUNNER_STOP_WAIT_MS),
|
|
385
|
+
),
|
|
386
|
+
]);
|
|
387
|
+
if (!stopped) {
|
|
388
|
+
logger().warn({
|
|
389
|
+
msg: "timed out waiting for runner shutdown",
|
|
390
|
+
waitMs: RUNNER_STOP_WAIT_MS,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async serverlessHandleStart(c: HonoContext): Promise<Response> {
|
|
396
|
+
return streamSSE(c, async (stream) => {
|
|
397
|
+
// NOTE: onAbort does not work reliably
|
|
398
|
+
stream.onAbort(() => {});
|
|
399
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
400
|
+
logger().debug("SSE aborted, shutting down runner");
|
|
401
|
+
|
|
402
|
+
// We cannot assume that the request will always be closed gracefully by Rivet. We always proceed with a graceful shutdown in case the request was terminated for any other reason.
|
|
403
|
+
//
|
|
404
|
+
// If we did not use a graceful shutdown, the runner would
|
|
405
|
+
this.shutdownRunner(false);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
await this.#runnerStarted.promise;
|
|
409
|
+
|
|
410
|
+
// Runner id should be set if the runner started
|
|
411
|
+
const payload = this.#runner.getServerlessInitPacket();
|
|
412
|
+
invariant(payload, "runnerId not set");
|
|
413
|
+
await stream.writeSSE({ data: payload });
|
|
414
|
+
|
|
415
|
+
// Send ping every second to keep the connection alive
|
|
416
|
+
while (true) {
|
|
417
|
+
if (this.#isRunnerStopped) {
|
|
418
|
+
logger().debug({
|
|
419
|
+
msg: "runner is stopped",
|
|
420
|
+
});
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (stream.closed || stream.aborted) {
|
|
425
|
+
logger().debug({
|
|
426
|
+
msg: "runner sse stream closed",
|
|
427
|
+
closed: stream.closed,
|
|
428
|
+
aborted: stream.aborted,
|
|
429
|
+
});
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
await stream.writeSSE({ event: "ping", data: "" });
|
|
434
|
+
await stream.sleep(RUNNER_SSE_PING_INTERVAL);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Wait for the runner to stop if the SSE stream aborted early for any reason
|
|
438
|
+
await this.#runnerStopped.promise;
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async #runnerOnActorStart(
|
|
443
|
+
actorId: string,
|
|
444
|
+
generation: number,
|
|
445
|
+
actorConfig: EngineActorConfig,
|
|
446
|
+
): Promise<void> {
|
|
447
|
+
logger().debug({
|
|
448
|
+
msg: "runner actor starting",
|
|
449
|
+
actorId,
|
|
450
|
+
name: actorConfig.name,
|
|
451
|
+
key: actorConfig.key,
|
|
452
|
+
generation,
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// Deserialize input
|
|
456
|
+
let input: any;
|
|
457
|
+
if (actorConfig.input) {
|
|
458
|
+
input = cbor.decode(actorConfig.input);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Get or create handler
|
|
462
|
+
let handler = this.#actors.get(actorId);
|
|
463
|
+
if (!handler) {
|
|
464
|
+
// IMPORTANT: We must set the handler in the map synchronously before doing any
|
|
465
|
+
// async operations to avoid race conditions where multiple calls might try to
|
|
466
|
+
// create the same handler simultaneously.
|
|
467
|
+
handler = {
|
|
468
|
+
actorStartPromise: promiseWithResolvers((reason) => logger().warn({ msg: "unhandled actor start promise rejection", reason })),
|
|
469
|
+
};
|
|
470
|
+
this.#actors.set(actorId, handler);
|
|
471
|
+
}
|
|
472
|
+
handler.actorStartError = undefined;
|
|
473
|
+
|
|
474
|
+
const name = actorConfig.name as string;
|
|
475
|
+
invariant(actorConfig.key, "actor should have a key");
|
|
476
|
+
const key = deserializeActorKey(actorConfig.key);
|
|
477
|
+
|
|
478
|
+
try {
|
|
479
|
+
// Initialize storage
|
|
480
|
+
const [persistDataBuffer] = await this.#runner.kvGet(actorId, [
|
|
481
|
+
KEYS.PERSIST_DATA,
|
|
482
|
+
]);
|
|
483
|
+
if (persistDataBuffer === null) {
|
|
484
|
+
const initialKvState = getInitialActorKvState(input);
|
|
485
|
+
await this.#runner.kvPut(actorId, initialKvState);
|
|
486
|
+
logger().debug({
|
|
487
|
+
msg: "initialized persist data for new actor",
|
|
488
|
+
actorId,
|
|
489
|
+
});
|
|
490
|
+
} else {
|
|
491
|
+
logger().debug({
|
|
492
|
+
msg: "found existing persist data for actor",
|
|
493
|
+
actorId,
|
|
494
|
+
dataSize: persistDataBuffer.byteLength,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Create actor instance
|
|
499
|
+
const definition = lookupInRegistry(this.#config, actorConfig.name);
|
|
500
|
+
handler.actor = await definition.instantiate();
|
|
501
|
+
|
|
502
|
+
// Start actor
|
|
503
|
+
await handler.actor.start(
|
|
504
|
+
this,
|
|
505
|
+
this.#inlineClient,
|
|
506
|
+
actorId,
|
|
507
|
+
name,
|
|
508
|
+
key,
|
|
509
|
+
"unknown", // TODO: Add regions
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
logger().debug({ msg: "runner actor started", actorId, name, key });
|
|
513
|
+
} catch (innerError) {
|
|
514
|
+
const error =
|
|
515
|
+
innerError instanceof Error
|
|
516
|
+
? new Error(
|
|
517
|
+
`Failed to start actor ${actorId}: ${innerError.message}`,
|
|
518
|
+
{ cause: innerError },
|
|
519
|
+
)
|
|
520
|
+
: new Error(`Failed to start actor ${actorId}: ${String(innerError)}`);
|
|
521
|
+
handler.actor = undefined;
|
|
522
|
+
handler.actorStartError = error;
|
|
523
|
+
handler.actorStartPromise?.reject(error);
|
|
524
|
+
handler.actorStartPromise = undefined;
|
|
525
|
+
logger().error({
|
|
526
|
+
msg: "runner actor failed to start",
|
|
527
|
+
actorId,
|
|
528
|
+
name,
|
|
529
|
+
key,
|
|
530
|
+
err: stringifyError(error),
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
try {
|
|
534
|
+
this.#runner.stopActor(actorId);
|
|
535
|
+
} catch (stopError) {
|
|
536
|
+
logger().debug({
|
|
537
|
+
msg: "failed to stop actor after start failure",
|
|
538
|
+
actorId,
|
|
539
|
+
err: stringifyError(stopError),
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
async #runnerOnActorStop(
|
|
546
|
+
actorId: string,
|
|
547
|
+
generation: number,
|
|
548
|
+
): Promise<void> {
|
|
549
|
+
logger().debug({ msg: "runner actor stopping", actorId, generation });
|
|
550
|
+
|
|
551
|
+
// HACK: Retrieve the stop intent we tracked locally (see RVT-5284)
|
|
552
|
+
// Default to "sleep" if no intent was recorded (e.g., if the runner
|
|
553
|
+
// initiated the stop)
|
|
554
|
+
//
|
|
555
|
+
// TODO: This will not work if the actor is destroyed from the API
|
|
556
|
+
// correctly. Currently, it will use the sleep intent, but it's
|
|
557
|
+
// actually a destroy intent.
|
|
558
|
+
const reason = this.#actorStopIntent.get(actorId) ?? "sleep";
|
|
559
|
+
this.#actorStopIntent.delete(actorId);
|
|
560
|
+
|
|
561
|
+
const handler = this.#actors.get(actorId);
|
|
562
|
+
if (handler?.actorStartPromise) {
|
|
563
|
+
const startError =
|
|
564
|
+
handler.actorStartError ??
|
|
565
|
+
new Error(`Actor ${actorId} stopped before start completed`);
|
|
566
|
+
handler.actorStartError = startError;
|
|
567
|
+
handler.actorStartPromise.reject(startError);
|
|
568
|
+
handler.actorStartPromise = undefined;
|
|
569
|
+
}
|
|
570
|
+
if (handler?.actor) {
|
|
571
|
+
try {
|
|
572
|
+
await handler.actor.onStop(reason);
|
|
573
|
+
} catch (err) {
|
|
574
|
+
logger().error({
|
|
575
|
+
msg: "error in onStop, proceeding with removing actor",
|
|
576
|
+
err: stringifyError(err),
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (handler) this.#actors.delete(actorId);
|
|
581
|
+
|
|
582
|
+
logger().debug({ msg: "runner actor stopped", actorId, reason });
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// MARK: - Runner Networking
|
|
586
|
+
async #runnerFetch(
|
|
587
|
+
_runner: Runner,
|
|
588
|
+
actorId: string,
|
|
589
|
+
_gatewayIdBuf: ArrayBuffer,
|
|
590
|
+
_requestIdBuf: ArrayBuffer,
|
|
591
|
+
request: Request,
|
|
592
|
+
): Promise<Response> {
|
|
593
|
+
logger().debug({
|
|
594
|
+
msg: "runner fetch",
|
|
595
|
+
actorId,
|
|
596
|
+
url: request.url,
|
|
597
|
+
method: request.method,
|
|
598
|
+
});
|
|
599
|
+
return await this.#actorRouter.fetch(request, { actorId });
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
async #runnerWebSocket(
|
|
603
|
+
_runner: Runner,
|
|
604
|
+
actorId: string,
|
|
605
|
+
websocketRaw: any,
|
|
606
|
+
gatewayIdBuf: ArrayBuffer,
|
|
607
|
+
requestIdBuf: ArrayBuffer,
|
|
608
|
+
request: Request,
|
|
609
|
+
requestPath: string,
|
|
610
|
+
requestHeaders: Record<string, string>,
|
|
611
|
+
isHibernatable: boolean,
|
|
612
|
+
isRestoringHibernatable: boolean,
|
|
613
|
+
): Promise<void> {
|
|
614
|
+
const websocket = websocketRaw as UniversalWebSocket;
|
|
615
|
+
|
|
616
|
+
// Add a unique ID to track this WebSocket object
|
|
617
|
+
const wsUniqueId = `ws_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
618
|
+
(websocket as any).__rivet_ws_id = wsUniqueId;
|
|
619
|
+
|
|
620
|
+
logger().debug({
|
|
621
|
+
msg: "runner websocket",
|
|
622
|
+
actorId,
|
|
623
|
+
url: request.url,
|
|
624
|
+
isRestoringHibernatable,
|
|
625
|
+
websocketObjectId: websocketRaw
|
|
626
|
+
? Object.prototype.toString.call(websocketRaw)
|
|
627
|
+
: "null",
|
|
628
|
+
websocketType: websocketRaw?.constructor?.name,
|
|
629
|
+
wsUniqueId,
|
|
630
|
+
websocketProps: websocketRaw
|
|
631
|
+
? Object.keys(websocketRaw).join(", ")
|
|
632
|
+
: "null",
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
// Parse configuration from Sec-WebSocket-Protocol header (optional for path-based routing)
|
|
636
|
+
const protocols = request.headers.get("sec-websocket-protocol");
|
|
637
|
+
const { encoding, connParams } = parseWebSocketProtocols(protocols);
|
|
638
|
+
|
|
639
|
+
// Fetch WS handler
|
|
640
|
+
//
|
|
641
|
+
// We store the promise since we need to add WebSocket event listeners immediately that will wait for the promise to resolve
|
|
642
|
+
let wsHandler: UpgradeWebSocketArgs;
|
|
643
|
+
try {
|
|
644
|
+
wsHandler = await routeWebSocket(
|
|
645
|
+
request,
|
|
646
|
+
requestPath,
|
|
647
|
+
requestHeaders,
|
|
648
|
+
this.#config,
|
|
649
|
+
this,
|
|
650
|
+
actorId,
|
|
651
|
+
encoding,
|
|
652
|
+
connParams,
|
|
653
|
+
gatewayIdBuf,
|
|
654
|
+
requestIdBuf,
|
|
655
|
+
isHibernatable,
|
|
656
|
+
isRestoringHibernatable,
|
|
657
|
+
);
|
|
658
|
+
} catch (err) {
|
|
659
|
+
logger().error({ msg: "building websocket handlers errored", err });
|
|
660
|
+
websocketRaw.close(1011, "ws.route_error");
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Connect the Hono WS hook to the adapter
|
|
665
|
+
//
|
|
666
|
+
// We need to assign to `raw` in order for WSContext to expose it on
|
|
667
|
+
// `ws.raw`
|
|
668
|
+
(websocket as WSContextInit).raw = websocket;
|
|
669
|
+
const wsContext = new WSContext(websocket);
|
|
670
|
+
|
|
671
|
+
// Get connection and actor from wsHandler (may be undefined for inspector endpoint)
|
|
672
|
+
const conn = wsHandler.conn;
|
|
673
|
+
const actor = wsHandler.actor;
|
|
674
|
+
const connStateManager = conn?.[CONN_STATE_MANAGER_SYMBOL];
|
|
675
|
+
|
|
676
|
+
// Bind event listeners to Hono WebSocket handlers
|
|
677
|
+
//
|
|
678
|
+
// We update the HWS data after calling handlers in order to ensure
|
|
679
|
+
// that the handler ran successfully. By doing this, we ensure at least
|
|
680
|
+
// once delivery of events to the event handlers.
|
|
681
|
+
|
|
682
|
+
// Log when attaching event listeners
|
|
683
|
+
logger().debug({
|
|
684
|
+
msg: "attaching websocket event listeners",
|
|
685
|
+
actorId,
|
|
686
|
+
connId: conn?.id,
|
|
687
|
+
wsUniqueId: (websocket as any).__rivet_ws_id,
|
|
688
|
+
isRestoringHibernatable,
|
|
689
|
+
websocketType: websocket?.constructor?.name,
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
if (isRestoringHibernatable) {
|
|
693
|
+
wsHandler.onRestore?.(wsContext);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
websocket.addEventListener("open", (event) => {
|
|
697
|
+
wsHandler.onOpen(event, wsContext);
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
websocket.addEventListener("message", (event: RivetMessageEvent) => {
|
|
701
|
+
logger().debug({
|
|
702
|
+
msg: "websocket message event listener triggered",
|
|
703
|
+
connId: conn?.id,
|
|
704
|
+
actorId: actor?.id,
|
|
705
|
+
messageIndex: event.rivetMessageIndex,
|
|
706
|
+
hasWsHandler: !!wsHandler,
|
|
707
|
+
hasOnMessage: !!wsHandler?.onMessage,
|
|
708
|
+
actorIsStopping: actor?.isStopping,
|
|
709
|
+
websocketType: websocket?.constructor?.name,
|
|
710
|
+
wsUniqueId: (websocket as any).__rivet_ws_id,
|
|
711
|
+
eventTargetWsId: (event.target as any)?.__rivet_ws_id,
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
// Check if actor is stopping - if so, don't process new messages.
|
|
715
|
+
// These messages will be reprocessed when the actor wakes up from hibernation.
|
|
716
|
+
// TODO: This will never retransmit the socket and the socket will close
|
|
717
|
+
if (actor?.isStopping) {
|
|
718
|
+
logger().debug({
|
|
719
|
+
msg: "ignoring ws message, actor is stopping",
|
|
720
|
+
connId: conn?.id,
|
|
721
|
+
actorId: actor?.id,
|
|
722
|
+
messageIndex: event.rivetMessageIndex,
|
|
723
|
+
});
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// Process message
|
|
728
|
+
logger().debug({
|
|
729
|
+
msg: "calling wsHandler.onMessage",
|
|
730
|
+
connId: conn?.id,
|
|
731
|
+
messageIndex: event.rivetMessageIndex,
|
|
732
|
+
});
|
|
733
|
+
wsHandler.onMessage(event, wsContext);
|
|
734
|
+
|
|
735
|
+
// Persist message index for hibernatable connections
|
|
736
|
+
const hibernate = connStateManager?.hibernatableData;
|
|
737
|
+
|
|
738
|
+
if (hibernate && conn && actor) {
|
|
739
|
+
invariant(
|
|
740
|
+
typeof event.rivetMessageIndex === "number",
|
|
741
|
+
"missing event.rivetMessageIndex",
|
|
742
|
+
);
|
|
743
|
+
|
|
744
|
+
// Persist message index
|
|
745
|
+
const previousMsgIndex = hibernate.serverMessageIndex;
|
|
746
|
+
hibernate.serverMessageIndex = event.rivetMessageIndex;
|
|
747
|
+
logger().info({
|
|
748
|
+
msg: "persisting message index",
|
|
749
|
+
connId: conn.id,
|
|
750
|
+
previousMsgIndex,
|
|
751
|
+
newMsgIndex: event.rivetMessageIndex,
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
// Calculate message size and track cumulative size
|
|
755
|
+
const entry = this.#hwsMessageIndex.get(conn.id);
|
|
756
|
+
if (entry) {
|
|
757
|
+
// Track message length
|
|
758
|
+
const messageLength = getValueLength(event.data);
|
|
759
|
+
entry.bufferedMessageSize += messageLength;
|
|
760
|
+
|
|
761
|
+
if (
|
|
762
|
+
entry.bufferedMessageSize >=
|
|
763
|
+
CONN_BUFFERED_MESSAGE_SIZE_THRESHOLD
|
|
764
|
+
) {
|
|
765
|
+
// Reset buffered message size immeidatley (instead
|
|
766
|
+
// of waiting for onAfterPersistConn) since we may
|
|
767
|
+
// receive more messages before onAfterPersistConn
|
|
768
|
+
// is called, which would called saveState
|
|
769
|
+
// immediate multiple times
|
|
770
|
+
entry.bufferedMessageSize = 0;
|
|
771
|
+
entry.pendingAckFromBufferSize = true;
|
|
772
|
+
|
|
773
|
+
// Save state immediately if approaching buffer threshold
|
|
774
|
+
actor.stateManager.saveState({
|
|
775
|
+
immediate: true,
|
|
776
|
+
});
|
|
777
|
+
} else {
|
|
778
|
+
// Save message index. The maxWait is set to the ack deadline
|
|
779
|
+
// since we ack the message immediately after persisting the index.
|
|
780
|
+
// If cumulative size exceeds threshold, force immediate persist.
|
|
781
|
+
//
|
|
782
|
+
// This will call EngineActorDriver.onAfterPersistConn after
|
|
783
|
+
// persist to send the ack to the gateway.
|
|
784
|
+
actor.stateManager.saveState({
|
|
785
|
+
maxWait: CONN_MESSAGE_ACK_DEADLINE,
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
} else {
|
|
789
|
+
// Fallback if entry missing
|
|
790
|
+
actor.stateManager.saveState({
|
|
791
|
+
maxWait: CONN_MESSAGE_ACK_DEADLINE,
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
websocket.addEventListener("close", (event) => {
|
|
798
|
+
wsHandler.onClose(event, wsContext);
|
|
799
|
+
|
|
800
|
+
// NOTE: Persisted connection is removed when `conn.disconnect`
|
|
801
|
+
// is called by the WebSocket route
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
websocket.addEventListener("error", (event) => {
|
|
805
|
+
wsHandler.onError(event, wsContext);
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
// Log event listener attachment for restored connections
|
|
809
|
+
if (isRestoringHibernatable) {
|
|
810
|
+
logger().info({
|
|
811
|
+
msg: "event listeners attached to restored websocket",
|
|
812
|
+
actorId,
|
|
813
|
+
connId: conn?.id,
|
|
814
|
+
gatewayId: idToStr(gatewayIdBuf),
|
|
815
|
+
requestId: idToStr(requestIdBuf),
|
|
816
|
+
websocketType: websocket?.constructor?.name,
|
|
817
|
+
hasMessageListener: !!websocket.addEventListener,
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// MARK: - Hibernating WebSockets
|
|
823
|
+
#hwsCanHibernate(
|
|
824
|
+
actorId: string,
|
|
825
|
+
gatewayId: ArrayBuffer,
|
|
826
|
+
requestId: ArrayBuffer,
|
|
827
|
+
request: Request,
|
|
828
|
+
): boolean {
|
|
829
|
+
const url = new URL(request.url);
|
|
830
|
+
const path = url.pathname;
|
|
831
|
+
|
|
832
|
+
// Get actor instance from runner to access actor name
|
|
833
|
+
const actorInstance = this.#runner.getActor(actorId);
|
|
834
|
+
if (!actorInstance) {
|
|
835
|
+
logger().warn({
|
|
836
|
+
msg: "actor not found in #hwsCanHibernate",
|
|
837
|
+
actorId,
|
|
838
|
+
});
|
|
839
|
+
return false;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// Load actor handler to access persisted data
|
|
843
|
+
const handler = this.#actors.get(actorId);
|
|
844
|
+
if (!handler) {
|
|
845
|
+
logger().warn({
|
|
846
|
+
msg: "actor handler not found in #hwsCanHibernate",
|
|
847
|
+
actorId,
|
|
848
|
+
});
|
|
849
|
+
return false;
|
|
850
|
+
}
|
|
851
|
+
if (!handler.actor) {
|
|
852
|
+
logger().warn({
|
|
853
|
+
msg: "actor not found in #hwsCanHibernate",
|
|
854
|
+
actorId,
|
|
855
|
+
});
|
|
856
|
+
return false;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// Determine configuration for new WS
|
|
860
|
+
logger().debug({
|
|
861
|
+
msg: "no existing hibernatable websocket found",
|
|
862
|
+
gatewayId: idToStr(gatewayId),
|
|
863
|
+
requestId: idToStr(requestId),
|
|
864
|
+
});
|
|
865
|
+
if (path === PATH_CONNECT) {
|
|
866
|
+
return true;
|
|
867
|
+
} else if (
|
|
868
|
+
path === PATH_WEBSOCKET_BASE ||
|
|
869
|
+
path.startsWith(PATH_WEBSOCKET_PREFIX)
|
|
870
|
+
) {
|
|
871
|
+
// Find actor config
|
|
872
|
+
const definition = lookupInRegistry(
|
|
873
|
+
this.#config,
|
|
874
|
+
actorInstance.config.name,
|
|
875
|
+
);
|
|
876
|
+
|
|
877
|
+
// Check if can hibernate
|
|
878
|
+
const canHibernateWebSocket =
|
|
879
|
+
definition.config.options?.canHibernateWebSocket;
|
|
880
|
+
if (canHibernateWebSocket === true) {
|
|
881
|
+
return true;
|
|
882
|
+
} else if (typeof canHibernateWebSocket === "function") {
|
|
883
|
+
try {
|
|
884
|
+
// Truncate the path to match the behavior on onRawWebSocket
|
|
885
|
+
const newPath = truncateRawWebSocketPathPrefix(
|
|
886
|
+
url.pathname,
|
|
887
|
+
);
|
|
888
|
+
const truncatedRequest = new Request(
|
|
889
|
+
`http://actor${newPath}`,
|
|
890
|
+
request,
|
|
891
|
+
);
|
|
892
|
+
|
|
893
|
+
const canHibernate =
|
|
894
|
+
canHibernateWebSocket(truncatedRequest);
|
|
895
|
+
return canHibernate;
|
|
896
|
+
} catch (error) {
|
|
897
|
+
logger().error({
|
|
898
|
+
msg: "error calling canHibernateWebSocket",
|
|
899
|
+
error,
|
|
900
|
+
});
|
|
901
|
+
return false;
|
|
902
|
+
}
|
|
903
|
+
} else {
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
906
|
+
} else if (path === PATH_INSPECTOR_CONNECT) {
|
|
907
|
+
return false;
|
|
908
|
+
} else {
|
|
909
|
+
logger().warn({
|
|
910
|
+
msg: "unexpected path for getActorHibernationConfig",
|
|
911
|
+
path,
|
|
912
|
+
});
|
|
913
|
+
return false;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
async #hwsLoadAll(
|
|
918
|
+
actorId: string,
|
|
919
|
+
): Promise<HibernatingWebSocketMetadata[]> {
|
|
920
|
+
const actor = await this.loadActor(actorId);
|
|
921
|
+
return actor.conns
|
|
922
|
+
.values()
|
|
923
|
+
.map((conn) => {
|
|
924
|
+
const connStateManager = conn[CONN_STATE_MANAGER_SYMBOL];
|
|
925
|
+
const hibernatable = connStateManager.hibernatableData;
|
|
926
|
+
if (!hibernatable) return undefined;
|
|
927
|
+
return {
|
|
928
|
+
gatewayId: hibernatable.gatewayId,
|
|
929
|
+
requestId: hibernatable.requestId,
|
|
930
|
+
serverMessageIndex: hibernatable.serverMessageIndex,
|
|
931
|
+
clientMessageIndex: hibernatable.clientMessageIndex,
|
|
932
|
+
path: hibernatable.requestPath,
|
|
933
|
+
headers: hibernatable.requestHeaders,
|
|
934
|
+
} satisfies HibernatingWebSocketMetadata;
|
|
935
|
+
})
|
|
936
|
+
.filter((x) => x !== undefined)
|
|
937
|
+
.toArray();
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
async onBeforeActorStart(actor: AnyActorInstance): Promise<void> {
|
|
941
|
+
// Resolve promise if waiting
|
|
942
|
+
const handler = this.#actors.get(actor.id);
|
|
943
|
+
invariant(handler, "missing actor handler in onBeforeActorReady");
|
|
944
|
+
handler.actorStartError = undefined;
|
|
945
|
+
handler.actorStartPromise?.resolve();
|
|
946
|
+
handler.actorStartPromise = undefined;
|
|
947
|
+
|
|
948
|
+
// Restore hibernating requests
|
|
949
|
+
const metaEntries = await this.#hwsLoadAll(actor.id);
|
|
950
|
+
await this.#runner.restoreHibernatingRequests(actor.id, metaEntries);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
onCreateConn(conn: AnyConn) {
|
|
954
|
+
const hibernatable = conn[CONN_STATE_MANAGER_SYMBOL].hibernatableData;
|
|
955
|
+
if (!hibernatable) return;
|
|
956
|
+
|
|
957
|
+
this.#hwsMessageIndex.set(conn.id, {
|
|
958
|
+
serverMessageIndex: hibernatable.serverMessageIndex,
|
|
959
|
+
bufferedMessageSize: 0,
|
|
960
|
+
pendingAckFromMessageIndex: false,
|
|
961
|
+
pendingAckFromBufferSize: false,
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
logger().debug({
|
|
965
|
+
msg: "created #hwsMessageIndex entry",
|
|
966
|
+
connId: conn.id,
|
|
967
|
+
serverMessageIndex: hibernatable.serverMessageIndex,
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
onDestroyConn(conn: AnyConn) {
|
|
972
|
+
this.#hwsMessageIndex.delete(conn.id);
|
|
973
|
+
|
|
974
|
+
logger().debug({
|
|
975
|
+
msg: "removed #hwsMessageIndex entry",
|
|
976
|
+
connId: conn.id,
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
onBeforePersistConn(conn: AnyConn) {
|
|
981
|
+
const stateManager = conn[CONN_STATE_MANAGER_SYMBOL];
|
|
982
|
+
const hibernatable = stateManager.hibernatableDataOrError();
|
|
983
|
+
|
|
984
|
+
const entry = this.#hwsMessageIndex.get(conn.id);
|
|
985
|
+
if (!entry) {
|
|
986
|
+
logger().warn({
|
|
987
|
+
msg: "missing EngineActorDriver.#hwsMessageIndex entry for conn",
|
|
988
|
+
connId: conn.id,
|
|
989
|
+
});
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// There is a newer message index
|
|
994
|
+
entry.pendingAckFromMessageIndex =
|
|
995
|
+
hibernatable.serverMessageIndex > entry.serverMessageIndex;
|
|
996
|
+
entry.serverMessageIndex = hibernatable.serverMessageIndex;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
onAfterPersistConn(conn: AnyConn) {
|
|
1000
|
+
const stateManager = conn[CONN_STATE_MANAGER_SYMBOL];
|
|
1001
|
+
const hibernatable = stateManager.hibernatableDataOrError();
|
|
1002
|
+
|
|
1003
|
+
const entry = this.#hwsMessageIndex.get(conn.id);
|
|
1004
|
+
if (!entry) {
|
|
1005
|
+
logger().warn({
|
|
1006
|
+
msg: "missing EngineActorDriver.#hwsMessageIndex entry for conn",
|
|
1007
|
+
connId: conn.id,
|
|
1008
|
+
});
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
// Ack entry
|
|
1013
|
+
if (
|
|
1014
|
+
entry.pendingAckFromMessageIndex ||
|
|
1015
|
+
entry.pendingAckFromBufferSize
|
|
1016
|
+
) {
|
|
1017
|
+
this.#runner.sendHibernatableWebSocketMessageAck(
|
|
1018
|
+
hibernatable.gatewayId,
|
|
1019
|
+
hibernatable.requestId,
|
|
1020
|
+
entry.serverMessageIndex,
|
|
1021
|
+
);
|
|
1022
|
+
entry.pendingAckFromMessageIndex = false;
|
|
1023
|
+
entry.pendingAckFromBufferSize = false;
|
|
1024
|
+
entry.bufferedMessageSize = 0;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
}
|