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,215 @@
|
|
|
1
|
+
import invariant from "invariant";
|
|
2
|
+
import {
|
|
3
|
+
EndpointMismatch,
|
|
4
|
+
InvalidRequest,
|
|
5
|
+
NamespaceMismatch,
|
|
6
|
+
} from "@/actor/errors";
|
|
7
|
+
import { convertRegistryConfigToClientConfig } from "@/client/config";
|
|
8
|
+
import { handleHealthRequest, handleMetadataRequest } from "@/common/router";
|
|
9
|
+
import { ServerlessStartHeadersSchema } from "@/manager/router-schema";
|
|
10
|
+
import { createClientWithDriver } from "@/mod";
|
|
11
|
+
import type { DriverConfig, RegistryConfig } from "@/registry/config";
|
|
12
|
+
import { RemoteManagerDriver } from "@/remote-manager-driver/mod";
|
|
13
|
+
import { createRouter } from "@/utils/router";
|
|
14
|
+
import { logger } from "./log";
|
|
15
|
+
|
|
16
|
+
export function buildServerlessRouter(
|
|
17
|
+
driverConfig: DriverConfig,
|
|
18
|
+
config: RegistryConfig,
|
|
19
|
+
) {
|
|
20
|
+
return createRouter(config.serverless.basePath, (router) => {
|
|
21
|
+
// GET /
|
|
22
|
+
router.get("/", (c) => {
|
|
23
|
+
return c.text(
|
|
24
|
+
"This is a RivetKit server.\n\nLearn more at https://rivetkit.org",
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Serverless start endpoint
|
|
29
|
+
router.get("/start", async (c) => {
|
|
30
|
+
// Parse headers
|
|
31
|
+
const parseResult = ServerlessStartHeadersSchema.safeParse({
|
|
32
|
+
endpoint: c.req.header("x-rivet-endpoint"),
|
|
33
|
+
token: c.req.header("x-rivet-token") ?? undefined,
|
|
34
|
+
totalSlots: c.req.header("x-rivet-total-slots"),
|
|
35
|
+
runnerName: c.req.header("x-rivet-runner-name"),
|
|
36
|
+
namespace: c.req.header("x-rivet-namespace-name"),
|
|
37
|
+
});
|
|
38
|
+
if (!parseResult.success) {
|
|
39
|
+
throw new InvalidRequest(
|
|
40
|
+
parseResult.error.issues[0]?.message ??
|
|
41
|
+
"invalid serverless start headers",
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
const { endpoint, token, totalSlots, runnerName, namespace } =
|
|
45
|
+
parseResult.data;
|
|
46
|
+
|
|
47
|
+
logger().debug({
|
|
48
|
+
msg: "received serverless runner start request",
|
|
49
|
+
endpoint,
|
|
50
|
+
totalSlots,
|
|
51
|
+
runnerName,
|
|
52
|
+
namespace,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Validate endpoint and namespace match config to catch
|
|
56
|
+
// misconfiguration or malicious requests.
|
|
57
|
+
//
|
|
58
|
+
// Only verify if namespace matches if endpoint configured since
|
|
59
|
+
// configuring an endpoint indicates you want to assert the
|
|
60
|
+
// incoming serverless requests.
|
|
61
|
+
if (config.endpoint) {
|
|
62
|
+
if (!endpointsMatch(endpoint, config.endpoint)) {
|
|
63
|
+
throw new EndpointMismatch(config.endpoint, endpoint);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (namespace !== config.namespace) {
|
|
67
|
+
throw new NamespaceMismatch(config.namespace, namespace);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Convert config to runner config
|
|
72
|
+
const newConfig: RegistryConfig = {
|
|
73
|
+
...config,
|
|
74
|
+
endpoint: endpoint,
|
|
75
|
+
namespace: namespace,
|
|
76
|
+
token: token,
|
|
77
|
+
runner: {
|
|
78
|
+
...config.runner,
|
|
79
|
+
totalSlots: totalSlots,
|
|
80
|
+
runnerName: runnerName,
|
|
81
|
+
// Not supported on serverless
|
|
82
|
+
runnerKey: undefined,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Create manager driver on demand based on the properties provided
|
|
87
|
+
// by headers
|
|
88
|
+
//
|
|
89
|
+
// NOTE: This relies on the `newConfig.runner.runnerName` to
|
|
90
|
+
// configure which runner to create actors on.
|
|
91
|
+
const managerDriver = new RemoteManagerDriver(
|
|
92
|
+
convertRegistryConfigToClientConfig(newConfig),
|
|
93
|
+
);
|
|
94
|
+
const client = createClientWithDriver(managerDriver);
|
|
95
|
+
|
|
96
|
+
// Create new actor driver with updated config
|
|
97
|
+
const actorDriver = driverConfig.actor(
|
|
98
|
+
newConfig,
|
|
99
|
+
managerDriver,
|
|
100
|
+
client,
|
|
101
|
+
);
|
|
102
|
+
invariant(
|
|
103
|
+
actorDriver.serverlessHandleStart,
|
|
104
|
+
"missing serverlessHandleStart on ActorDriver",
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
return await actorDriver.serverlessHandleStart(c);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
router.get("/health", (c) => handleHealthRequest(c));
|
|
111
|
+
|
|
112
|
+
router.get("/metadata", (c) =>
|
|
113
|
+
handleMetadataRequest(
|
|
114
|
+
c,
|
|
115
|
+
config,
|
|
116
|
+
{ serverless: {} },
|
|
117
|
+
config.publicEndpoint,
|
|
118
|
+
config.publicNamespace,
|
|
119
|
+
config.publicToken,
|
|
120
|
+
),
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Normalizes a URL for comparison by extracting protocol, host, port, and pathname.
|
|
127
|
+
* Normalizes loopback addresses (127.0.0.1, 0.0.0.0, ::1) to localhost for consistent comparison.
|
|
128
|
+
* Normalizes regional endpoints (api-*.domain) to base endpoints (api.domain).
|
|
129
|
+
* Returns null if the URL is invalid.
|
|
130
|
+
*/
|
|
131
|
+
export function normalizeEndpointUrl(url: string): string | null {
|
|
132
|
+
try {
|
|
133
|
+
const parsed = new URL(url);
|
|
134
|
+
// Normalize pathname by removing trailing slash (except for root)
|
|
135
|
+
const pathname =
|
|
136
|
+
parsed.pathname === "/" ? "/" : parsed.pathname.replace(/\/+$/, "");
|
|
137
|
+
|
|
138
|
+
// Normalize loopback addresses to localhost
|
|
139
|
+
let hostname = isLoopbackAddress(parsed.hostname)
|
|
140
|
+
? "localhost"
|
|
141
|
+
: parsed.hostname;
|
|
142
|
+
|
|
143
|
+
// Normalize regional endpoints (api-region.domain) to base endpoints (api.domain)
|
|
144
|
+
// HACK: This is specific to Rivet Cloud and will not work for self-hosted
|
|
145
|
+
// engines with different regional endpoint naming conventions.
|
|
146
|
+
hostname = normalizeRegionalHostname(hostname);
|
|
147
|
+
|
|
148
|
+
// Reconstruct host with normalized hostname and port
|
|
149
|
+
const host = parsed.port ? `${hostname}:${parsed.port}` : hostname;
|
|
150
|
+
|
|
151
|
+
// Reconstruct normalized URL with protocol, host, and pathname
|
|
152
|
+
return `${parsed.protocol}//${host}${pathname}`;
|
|
153
|
+
} catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Normalizes regional hostnames (api-region.domain) to base hostnames (api.domain).
|
|
160
|
+
* Only applies to rivet.dev domains.
|
|
161
|
+
*
|
|
162
|
+
* Examples:
|
|
163
|
+
* - api-us-west-1.rivet.dev -> api.rivet.dev
|
|
164
|
+
* - api-lax.staging.rivet.dev -> api.staging.rivet.dev
|
|
165
|
+
* - api.rivet.dev -> api.rivet.dev (unchanged)
|
|
166
|
+
* - api-us-west-1.example.com -> api-us-west-1.example.com (unchanged, not rivet.dev)
|
|
167
|
+
* - foo-bar.rivet.dev -> foo-bar.rivet.dev (unchanged, not api- prefix)
|
|
168
|
+
*/
|
|
169
|
+
function normalizeRegionalHostname(hostname: string): string {
|
|
170
|
+
// Only apply to rivet.dev domains
|
|
171
|
+
if (!hostname.endsWith(".rivet.dev")) {
|
|
172
|
+
return hostname;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!hostname.startsWith("api-")) {
|
|
176
|
+
return hostname;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Find the first dot after "api-"
|
|
180
|
+
const withoutPrefix = hostname.slice(4); // Remove "api-"
|
|
181
|
+
const firstDotIndex = withoutPrefix.indexOf(".");
|
|
182
|
+
if (firstDotIndex === -1) {
|
|
183
|
+
return hostname;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Extract the domain part and prepend "api."
|
|
187
|
+
const domain = withoutPrefix.slice(firstDotIndex + 1);
|
|
188
|
+
return `api.${domain}`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Compares two endpoint URLs after normalization.
|
|
193
|
+
* Returns true if they match (same protocol, host, port, and path).
|
|
194
|
+
*/
|
|
195
|
+
export function endpointsMatch(a: string, b: string): boolean {
|
|
196
|
+
const normalizedA = normalizeEndpointUrl(a);
|
|
197
|
+
const normalizedB = normalizeEndpointUrl(b);
|
|
198
|
+
if (normalizedA === null || normalizedB === null) {
|
|
199
|
+
// If either URL is invalid, fall back to string comparison
|
|
200
|
+
return a === b;
|
|
201
|
+
}
|
|
202
|
+
return normalizedA === normalizedB;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Checks if a hostname is a loopback address that should be normalized to localhost.
|
|
207
|
+
*/
|
|
208
|
+
function isLoopbackAddress(hostname: string): boolean {
|
|
209
|
+
return (
|
|
210
|
+
hostname === "127.0.0.1" ||
|
|
211
|
+
hostname === "0.0.0.0" ||
|
|
212
|
+
hostname === "::1" ||
|
|
213
|
+
hostname === "[::1]"
|
|
214
|
+
);
|
|
215
|
+
}
|
package/src/test/log.ts
ADDED
package/src/test/mod.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { serve as honoServe } from "@hono/node-server";
|
|
2
|
+
import { createNodeWebSocket } from "@hono/node-ws";
|
|
3
|
+
import invariant from "invariant";
|
|
4
|
+
import { type TestContext, vi } from "vitest";
|
|
5
|
+
import { ClientConfigSchema } from "@/client/config";
|
|
6
|
+
import { type Client, createClient } from "@/client/mod";
|
|
7
|
+
import { createFileSystemOrMemoryDriver } from "@/drivers/file-system/mod";
|
|
8
|
+
import { createClientWithDriver, type Registry } from "@/mod";
|
|
9
|
+
import { RegistryConfig, RegistryConfigSchema } from "@/registry/config";
|
|
10
|
+
import { buildManagerRouter } from "@/manager/router";
|
|
11
|
+
import { logger } from "./log";
|
|
12
|
+
|
|
13
|
+
export interface SetupTestResult<A extends Registry<any>> {
|
|
14
|
+
client: Client<A>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Must use `TestContext` since global hooks do not work when running concurrently
|
|
18
|
+
export async function setupTest<A extends Registry<any>>(
|
|
19
|
+
c: TestContext,
|
|
20
|
+
registry: A,
|
|
21
|
+
): Promise<SetupTestResult<A>> {
|
|
22
|
+
// Force enable test mode
|
|
23
|
+
registry.config.test = { ...registry.config.test, enabled: true };
|
|
24
|
+
|
|
25
|
+
// Create driver
|
|
26
|
+
const driver = createFileSystemOrMemoryDriver(
|
|
27
|
+
true,
|
|
28
|
+
{ path: `/tmp/rivetkit-test-${crypto.randomUUID()}` },
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Build driver config
|
|
32
|
+
// biome-ignore lint/style/useConst: Assigned later
|
|
33
|
+
let upgradeWebSocket: any;
|
|
34
|
+
registry.config.driver = driver;
|
|
35
|
+
registry.config.inspector = {
|
|
36
|
+
enabled: true,
|
|
37
|
+
token: () => "token",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Create router
|
|
41
|
+
const parsedConfig = registry.parseConfig();
|
|
42
|
+
const managerDriver = driver.manager?.(parsedConfig);
|
|
43
|
+
invariant(managerDriver, "missing manager driver");
|
|
44
|
+
const getUpgradeWebSocket = () => upgradeWebSocket;
|
|
45
|
+
managerDriver.setGetUpgradeWebSocket(getUpgradeWebSocket);
|
|
46
|
+
// const internalClient = createClientWithDriver(
|
|
47
|
+
// managerDriver,
|
|
48
|
+
// ClientConfigSchema.parse({}),
|
|
49
|
+
// );
|
|
50
|
+
const { router } = buildManagerRouter(
|
|
51
|
+
parsedConfig,
|
|
52
|
+
managerDriver,
|
|
53
|
+
getUpgradeWebSocket,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Inject WebSocket
|
|
57
|
+
const nodeWebSocket = createNodeWebSocket({ app: router });
|
|
58
|
+
upgradeWebSocket = nodeWebSocket.upgradeWebSocket;
|
|
59
|
+
|
|
60
|
+
// TODO: I think this whole function is fucked, we should probably switch to calling registry.serve() directly
|
|
61
|
+
// Start server
|
|
62
|
+
const server = honoServe({
|
|
63
|
+
fetch: router.fetch,
|
|
64
|
+
hostname: "127.0.0.1",
|
|
65
|
+
port: 0,
|
|
66
|
+
});
|
|
67
|
+
if (!server.listening) {
|
|
68
|
+
await new Promise<void>((resolve) => {
|
|
69
|
+
server.once("listening", () => resolve());
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
invariant(
|
|
73
|
+
nodeWebSocket.injectWebSocket !== undefined,
|
|
74
|
+
"should have injectWebSocket",
|
|
75
|
+
);
|
|
76
|
+
nodeWebSocket.injectWebSocket(server);
|
|
77
|
+
const address = server.address();
|
|
78
|
+
invariant(address && typeof address !== "string", "missing server address");
|
|
79
|
+
const port = address.port;
|
|
80
|
+
const endpoint = `http://127.0.0.1:${port}`;
|
|
81
|
+
|
|
82
|
+
logger().info({ msg: "test server listening", port });
|
|
83
|
+
|
|
84
|
+
// Cleanup on test finish
|
|
85
|
+
c.onTestFinished(async () => {
|
|
86
|
+
await new Promise((resolve) => server.close(() => resolve(undefined)));
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Create client
|
|
90
|
+
const client = createClient<A>({
|
|
91
|
+
endpoint,
|
|
92
|
+
namespace: "default",
|
|
93
|
+
runnerName: "default",
|
|
94
|
+
disableMetadataLookup: true,
|
|
95
|
+
});
|
|
96
|
+
c.onTestFinished(async () => await client.dispose());
|
|
97
|
+
|
|
98
|
+
return { client };
|
|
99
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timing-safe comparison of two Uint8Arrays or strings.
|
|
3
|
+
* This prevents timing attacks by always comparing all bytes.
|
|
4
|
+
*/
|
|
5
|
+
export function timingSafeEqual(
|
|
6
|
+
a: Uint8Array | string,
|
|
7
|
+
b: Uint8Array | string,
|
|
8
|
+
): boolean {
|
|
9
|
+
const encoder = new TextEncoder();
|
|
10
|
+
const bufferA = typeof a === "string" ? encoder.encode(a) : a;
|
|
11
|
+
const bufferB = typeof b === "string" ? encoder.encode(b) : b;
|
|
12
|
+
|
|
13
|
+
// Pad to max length to avoid leaking length information
|
|
14
|
+
const maxLength = Math.max(bufferA.byteLength, bufferB.byteLength);
|
|
15
|
+
let result = bufferA.byteLength ^ bufferB.byteLength;
|
|
16
|
+
|
|
17
|
+
for (let i = 0; i < maxLength; i++) {
|
|
18
|
+
const byteA = i < bufferA.byteLength ? bufferA[i] : 0;
|
|
19
|
+
const byteB = i < bufferB.byteLength ? bufferB[i] : 0;
|
|
20
|
+
result |= byteA ^ byteB;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return result === 0;
|
|
24
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import type { z } from "zod/v4";
|
|
3
|
+
import { tryParseEndpoint } from "./endpoint-parser";
|
|
4
|
+
|
|
5
|
+
// Helper to create a mock Zod refinement context for testing
|
|
6
|
+
function createMockCtx(): { ctx: z.RefinementCtx; issues: z.ZodIssue[] } {
|
|
7
|
+
const issues: z.ZodIssue[] = [];
|
|
8
|
+
const ctx = {
|
|
9
|
+
addIssue: (issue: z.IssueData) => {
|
|
10
|
+
issues.push(issue as z.ZodIssue);
|
|
11
|
+
},
|
|
12
|
+
path: [],
|
|
13
|
+
value: undefined,
|
|
14
|
+
issues: [],
|
|
15
|
+
} as unknown as z.RefinementCtx;
|
|
16
|
+
return { ctx, issues };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe("tryParseEndpoint", () => {
|
|
20
|
+
describe("basic parsing", () => {
|
|
21
|
+
test("parses endpoint with full auth", () => {
|
|
22
|
+
const { ctx, issues } = createMockCtx();
|
|
23
|
+
const result = tryParseEndpoint(ctx, {
|
|
24
|
+
endpoint: "https://foo:bar@api.rivet.dev",
|
|
25
|
+
});
|
|
26
|
+
expect(issues).toHaveLength(0);
|
|
27
|
+
expect(result).toEqual({
|
|
28
|
+
endpoint: "https://api.rivet.dev/",
|
|
29
|
+
namespace: "foo",
|
|
30
|
+
token: "bar",
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("parses endpoint with namespace only", () => {
|
|
35
|
+
const { ctx, issues } = createMockCtx();
|
|
36
|
+
const result = tryParseEndpoint(ctx, {
|
|
37
|
+
endpoint: "https://foo@api.rivet.dev",
|
|
38
|
+
});
|
|
39
|
+
expect(issues).toHaveLength(0);
|
|
40
|
+
expect(result).toEqual({
|
|
41
|
+
endpoint: "https://api.rivet.dev/",
|
|
42
|
+
namespace: "foo",
|
|
43
|
+
token: undefined,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("parses endpoint without auth", () => {
|
|
48
|
+
const { ctx, issues } = createMockCtx();
|
|
49
|
+
const result = tryParseEndpoint(ctx, {
|
|
50
|
+
endpoint: "https://api.rivet.dev",
|
|
51
|
+
});
|
|
52
|
+
expect(issues).toHaveLength(0);
|
|
53
|
+
expect(result).toEqual({
|
|
54
|
+
endpoint: "https://api.rivet.dev/",
|
|
55
|
+
namespace: undefined,
|
|
56
|
+
token: undefined,
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("preserves path", () => {
|
|
61
|
+
const { ctx, issues } = createMockCtx();
|
|
62
|
+
const result = tryParseEndpoint(ctx, {
|
|
63
|
+
endpoint: "https://foo:bar@api.rivet.dev/v1/actors",
|
|
64
|
+
});
|
|
65
|
+
expect(issues).toHaveLength(0);
|
|
66
|
+
expect(result).toEqual({
|
|
67
|
+
endpoint: "https://api.rivet.dev/v1/actors",
|
|
68
|
+
namespace: "foo",
|
|
69
|
+
token: "bar",
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("validation errors", () => {
|
|
75
|
+
test("adds issue for invalid URL", () => {
|
|
76
|
+
const { ctx, issues } = createMockCtx();
|
|
77
|
+
const result = tryParseEndpoint(ctx, { endpoint: "not-a-url" });
|
|
78
|
+
expect(result).toBeUndefined();
|
|
79
|
+
expect(issues).toHaveLength(1);
|
|
80
|
+
expect(issues[0]?.message).toContain("invalid URL");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("adds issue for query string", () => {
|
|
84
|
+
const { ctx, issues } = createMockCtx();
|
|
85
|
+
const result = tryParseEndpoint(ctx, {
|
|
86
|
+
endpoint: "https://foo:bar@api.rivet.dev?region=us",
|
|
87
|
+
});
|
|
88
|
+
expect(result).toBeUndefined();
|
|
89
|
+
expect(issues).toHaveLength(1);
|
|
90
|
+
expect(issues[0]?.message).toBe("endpoint cannot contain a query string");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("adds issue for fragment", () => {
|
|
94
|
+
const { ctx, issues } = createMockCtx();
|
|
95
|
+
const result = tryParseEndpoint(ctx, {
|
|
96
|
+
endpoint: "https://foo:bar@api.rivet.dev#section",
|
|
97
|
+
});
|
|
98
|
+
expect(result).toBeUndefined();
|
|
99
|
+
expect(issues).toHaveLength(1);
|
|
100
|
+
expect(issues[0]?.message).toBe("endpoint cannot contain a fragment");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("adds issue for token without namespace", () => {
|
|
104
|
+
const { ctx, issues } = createMockCtx();
|
|
105
|
+
const result = tryParseEndpoint(ctx, {
|
|
106
|
+
endpoint: "https://:token@api.rivet.dev",
|
|
107
|
+
});
|
|
108
|
+
expect(result).toBeUndefined();
|
|
109
|
+
expect(issues).toHaveLength(1);
|
|
110
|
+
expect(issues[0]?.message).toBe(
|
|
111
|
+
"endpoint cannot have a token without a namespace",
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe("duplicate credential checking", () => {
|
|
117
|
+
test("adds issue when namespace in URL and config", () => {
|
|
118
|
+
const { ctx, issues } = createMockCtx();
|
|
119
|
+
const result = tryParseEndpoint(ctx, {
|
|
120
|
+
endpoint: "https://url-ns@api.rivet.dev",
|
|
121
|
+
path: ["endpoint"],
|
|
122
|
+
namespace: "config-ns",
|
|
123
|
+
});
|
|
124
|
+
// Still returns result, but adds issue
|
|
125
|
+
expect(result).toEqual({
|
|
126
|
+
endpoint: "https://api.rivet.dev/",
|
|
127
|
+
namespace: "url-ns",
|
|
128
|
+
token: undefined,
|
|
129
|
+
});
|
|
130
|
+
expect(issues).toHaveLength(1);
|
|
131
|
+
expect(issues[0]?.message).toContain(
|
|
132
|
+
"cannot specify namespace both in endpoint URL and as a separate config option",
|
|
133
|
+
);
|
|
134
|
+
expect(issues[0]?.path).toEqual(["namespace"]);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("adds issue when token in URL and config", () => {
|
|
138
|
+
const { ctx, issues } = createMockCtx();
|
|
139
|
+
const result = tryParseEndpoint(ctx, {
|
|
140
|
+
endpoint: "https://ns:url-token@api.rivet.dev",
|
|
141
|
+
path: ["endpoint"],
|
|
142
|
+
token: "config-token",
|
|
143
|
+
});
|
|
144
|
+
// Still returns result, but adds issue
|
|
145
|
+
expect(result).toEqual({
|
|
146
|
+
endpoint: "https://api.rivet.dev/",
|
|
147
|
+
namespace: "ns",
|
|
148
|
+
token: "url-token",
|
|
149
|
+
});
|
|
150
|
+
expect(issues).toHaveLength(1);
|
|
151
|
+
expect(issues[0]?.message).toContain(
|
|
152
|
+
"cannot specify token both in endpoint URL and as a separate config option",
|
|
153
|
+
);
|
|
154
|
+
expect(issues[0]?.path).toEqual(["token"]);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("adds issues for both namespace and token duplicates", () => {
|
|
158
|
+
const { ctx, issues } = createMockCtx();
|
|
159
|
+
const result = tryParseEndpoint(ctx, {
|
|
160
|
+
endpoint: "https://url-ns:url-token@api.rivet.dev",
|
|
161
|
+
path: ["endpoint"],
|
|
162
|
+
namespace: "config-ns",
|
|
163
|
+
token: "config-token",
|
|
164
|
+
});
|
|
165
|
+
expect(result).toBeDefined();
|
|
166
|
+
expect(issues).toHaveLength(2);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("no issue when namespace only in URL", () => {
|
|
170
|
+
const { ctx, issues } = createMockCtx();
|
|
171
|
+
const result = tryParseEndpoint(ctx, {
|
|
172
|
+
endpoint: "https://url-ns@api.rivet.dev",
|
|
173
|
+
path: ["endpoint"],
|
|
174
|
+
token: "config-token",
|
|
175
|
+
});
|
|
176
|
+
expect(result).toBeDefined();
|
|
177
|
+
expect(issues).toHaveLength(0);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("no issue when namespace only in config", () => {
|
|
181
|
+
const { ctx, issues } = createMockCtx();
|
|
182
|
+
const result = tryParseEndpoint(ctx, {
|
|
183
|
+
endpoint: "https://api.rivet.dev",
|
|
184
|
+
path: ["endpoint"],
|
|
185
|
+
namespace: "config-ns",
|
|
186
|
+
});
|
|
187
|
+
expect(result).toBeDefined();
|
|
188
|
+
expect(issues).toHaveLength(0);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe("custom path", () => {
|
|
193
|
+
test("uses custom path in error issues", () => {
|
|
194
|
+
const { ctx, issues } = createMockCtx();
|
|
195
|
+
tryParseEndpoint(ctx, {
|
|
196
|
+
endpoint: "not-a-url",
|
|
197
|
+
path: ["serverless", "publicEndpoint"],
|
|
198
|
+
});
|
|
199
|
+
expect(issues[0]?.path).toEqual(["serverless", "publicEndpoint"]);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
|
|
3
|
+
export interface ParsedEndpoint {
|
|
4
|
+
endpoint: string;
|
|
5
|
+
namespace: string | undefined;
|
|
6
|
+
token: string | undefined;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface TryParseEndpointOptions {
|
|
10
|
+
/** The endpoint URL string to parse */
|
|
11
|
+
endpoint: string;
|
|
12
|
+
/** Path prefix for error messages (default: ["endpoint"]) */
|
|
13
|
+
path?: (string | number)[];
|
|
14
|
+
/** Namespace from config, to check for duplicate specification */
|
|
15
|
+
namespace?: string;
|
|
16
|
+
/** Token from config, to check for duplicate specification */
|
|
17
|
+
token?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Parses an endpoint URL that may contain auth syntax for namespace and token.
|
|
22
|
+
*
|
|
23
|
+
* Uses ctx.addIssue for clean error reporting in Zod transforms.
|
|
24
|
+
*
|
|
25
|
+
* Supports formats like:
|
|
26
|
+
* - `https://namespace:token@api.rivet.dev`
|
|
27
|
+
* - `https://namespace@api.rivet.dev` (namespace only, no token)
|
|
28
|
+
* - `https://api.rivet.dev` (no auth)
|
|
29
|
+
* - `https://namespace:token@api.rivet.dev/path` (with path)
|
|
30
|
+
*
|
|
31
|
+
* Query strings and fragments are not allowed as they may conflict with
|
|
32
|
+
* runtime parameters.
|
|
33
|
+
*
|
|
34
|
+
* @param ctx - Zod refinement context for error reporting
|
|
35
|
+
* @param options - Parsing options including endpoint, path, and config values
|
|
36
|
+
* @returns ParsedEndpoint on success, undefined on error (after adding issues to ctx)
|
|
37
|
+
*/
|
|
38
|
+
export function tryParseEndpoint(
|
|
39
|
+
ctx: z.RefinementCtx,
|
|
40
|
+
options: TryParseEndpointOptions,
|
|
41
|
+
): ParsedEndpoint | undefined {
|
|
42
|
+
const { endpoint, path = ["endpoint"], namespace: configNamespace, token: configToken } = options;
|
|
43
|
+
// Parse the URL
|
|
44
|
+
let url: URL;
|
|
45
|
+
try {
|
|
46
|
+
url = new URL(endpoint);
|
|
47
|
+
} catch {
|
|
48
|
+
ctx.addIssue({
|
|
49
|
+
code: "custom",
|
|
50
|
+
message: `invalid URL: ${endpoint}`,
|
|
51
|
+
path,
|
|
52
|
+
});
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Reject query strings
|
|
57
|
+
if (url.search) {
|
|
58
|
+
ctx.addIssue({
|
|
59
|
+
code: "custom",
|
|
60
|
+
message: "endpoint cannot contain a query string",
|
|
61
|
+
path,
|
|
62
|
+
});
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Reject fragments
|
|
67
|
+
if (url.hash) {
|
|
68
|
+
ctx.addIssue({
|
|
69
|
+
code: "custom",
|
|
70
|
+
message: "endpoint cannot contain a fragment",
|
|
71
|
+
path,
|
|
72
|
+
});
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Extract namespace and token from username and password
|
|
77
|
+
// URL stores these as percent-encoded, so we need to decode them
|
|
78
|
+
const namespace = url.username
|
|
79
|
+
? decodeURIComponent(url.username)
|
|
80
|
+
: undefined;
|
|
81
|
+
const token = url.password ? decodeURIComponent(url.password) : undefined;
|
|
82
|
+
|
|
83
|
+
// Reject token without namespace (e.g., https://:token@api.rivet.dev)
|
|
84
|
+
if (token && !namespace) {
|
|
85
|
+
ctx.addIssue({
|
|
86
|
+
code: "custom",
|
|
87
|
+
message: "endpoint cannot have a token without a namespace",
|
|
88
|
+
path,
|
|
89
|
+
});
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Check for duplicate credentials (specified both in URL and config)
|
|
94
|
+
if (namespace && configNamespace) {
|
|
95
|
+
ctx.addIssue({
|
|
96
|
+
code: "custom",
|
|
97
|
+
message:
|
|
98
|
+
"cannot specify namespace both in endpoint URL and as a separate config option",
|
|
99
|
+
path: ["namespace"],
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (token && configToken) {
|
|
103
|
+
ctx.addIssue({
|
|
104
|
+
code: "custom",
|
|
105
|
+
message:
|
|
106
|
+
"cannot specify token both in endpoint URL and as a separate config option",
|
|
107
|
+
path: ["token"],
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Strip auth from the URL by clearing username and password
|
|
112
|
+
url.username = "";
|
|
113
|
+
url.password = "";
|
|
114
|
+
|
|
115
|
+
// Get the cleaned endpoint without auth
|
|
116
|
+
const cleanedEndpoint = url.toString();
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
endpoint: cleanedEndpoint,
|
|
120
|
+
namespace,
|
|
121
|
+
token,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|