rivetkit 2.3.30 → 3.0.0-preview.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/dist/browser/client.d.ts +58 -9
  2. package/dist/browser/client.js +193 -20
  3. package/dist/browser/client.js.map +1 -1
  4. package/dist/browser/inspector/client.js +1 -2
  5. package/dist/browser/inspector/client.js.map +1 -1
  6. package/dist/tsup/{chunk-P2PHHJMH.cjs → chunk-25MUHNWE.cjs} +326 -166
  7. package/dist/tsup/chunk-25MUHNWE.cjs.map +1 -0
  8. package/dist/tsup/{chunk-5SN6GAYX.cjs → chunk-3SPYZMME.cjs} +3 -3
  9. package/dist/tsup/{chunk-5SN6GAYX.cjs.map → chunk-3SPYZMME.cjs.map} +1 -1
  10. package/dist/tsup/{chunk-4SNS3PYZ.js → chunk-3URL73AV.js} +2 -2
  11. package/dist/tsup/{chunk-EKAQ7HG2.js → chunk-442XBCAF.js} +3 -3
  12. package/dist/tsup/{chunk-CORYIKDC.js → chunk-5F7FWAJI.js} +10 -11
  13. package/dist/tsup/chunk-5F7FWAJI.js.map +1 -0
  14. package/dist/tsup/{chunk-I472KTIC.js → chunk-6IGGYL2C.js} +185 -25
  15. package/dist/tsup/chunk-6IGGYL2C.js.map +1 -0
  16. package/dist/tsup/{chunk-JI6GZ2C2.js → chunk-A2CF6PFH.js} +18 -1
  17. package/dist/tsup/chunk-A2CF6PFH.js.map +1 -0
  18. package/dist/tsup/{chunk-OUB7SGSA.js → chunk-ANNVZW6B.js} +2 -2
  19. package/dist/tsup/{chunk-ZIIQQHC3.cjs → chunk-BCG7PTBS.cjs} +4 -4
  20. package/dist/tsup/{chunk-ZIIQQHC3.cjs.map → chunk-BCG7PTBS.cjs.map} +1 -1
  21. package/dist/tsup/{chunk-OJFVABRH.cjs → chunk-CJ3MX5MI.cjs} +7 -7
  22. package/dist/tsup/{chunk-OJFVABRH.cjs.map → chunk-CJ3MX5MI.cjs.map} +1 -1
  23. package/dist/tsup/{chunk-GEUBEODU.cjs → chunk-EPPV7WB2.cjs} +3 -3
  24. package/dist/tsup/{chunk-GEUBEODU.cjs.map → chunk-EPPV7WB2.cjs.map} +1 -1
  25. package/dist/tsup/{chunk-MXV26ZBB.js → chunk-OLZ4KAFL.js} +4 -4
  26. package/dist/tsup/{chunk-5UUOECX2.cjs → chunk-QDRZMCX5.cjs} +10 -10
  27. package/dist/tsup/{chunk-5UUOECX2.cjs.map → chunk-QDRZMCX5.cjs.map} +1 -1
  28. package/dist/tsup/{chunk-MYWZCDML.cjs → chunk-RTWH3AX3.cjs} +19 -2
  29. package/dist/tsup/chunk-RTWH3AX3.cjs.map +1 -0
  30. package/dist/tsup/{chunk-H3SGQEHZ.js → chunk-WGKL7HMH.js} +2 -2
  31. package/dist/tsup/{chunk-6LOYTFPZ.js → chunk-WGQHG26S.js} +2 -2
  32. package/dist/tsup/{chunk-2BIRGEIU.cjs → chunk-XSOHGU4G.cjs} +10 -11
  33. package/dist/tsup/chunk-XSOHGU4G.cjs.map +1 -0
  34. package/dist/tsup/{chunk-ABPZG7QV.cjs → chunk-YZVVID47.cjs} +3 -3
  35. package/dist/tsup/{chunk-ABPZG7QV.cjs.map → chunk-YZVVID47.cjs.map} +1 -1
  36. package/dist/tsup/client/mod.cjs +6 -6
  37. package/dist/tsup/client/mod.d.cts +15 -3
  38. package/dist/tsup/client/mod.d.ts +15 -3
  39. package/dist/tsup/client/mod.js +5 -5
  40. package/dist/tsup/common/log.cjs +2 -2
  41. package/dist/tsup/common/log.js +1 -1
  42. package/dist/tsup/common/websocket.cjs +3 -3
  43. package/dist/tsup/common/websocket.js +2 -2
  44. package/dist/tsup/{config-osdJqQwX.d.cts → config-8NgsxIcT.d.cts} +43 -12
  45. package/dist/tsup/{config-ey2I2zz5.d.ts → config-B2wlj9wx.d.cts} +7 -1
  46. package/dist/tsup/{config-ey2I2zz5.d.cts → config-B2wlj9wx.d.ts} +7 -1
  47. package/dist/tsup/{config-Cv6pjCt-.d.ts → config-DJ3KW-fe.d.ts} +43 -12
  48. package/dist/tsup/{context-CoVbKBuk.d.ts → context-BL8kUGjq.d.ts} +2 -2
  49. package/dist/tsup/{context-DpjrM7n7.d.cts → context-_RoE_YHL.d.cts} +2 -2
  50. package/dist/tsup/db/drizzle.cjs +2 -2
  51. package/dist/tsup/db/drizzle.d.cts +1 -1
  52. package/dist/tsup/db/drizzle.d.ts +1 -1
  53. package/dist/tsup/db/drizzle.js +1 -1
  54. package/dist/tsup/db/mod.cjs +3 -3
  55. package/dist/tsup/db/mod.d.cts +2 -2
  56. package/dist/tsup/db/mod.d.ts +2 -2
  57. package/dist/tsup/db/mod.js +2 -2
  58. package/dist/tsup/dynamic/mod.d.cts +2 -2
  59. package/dist/tsup/dynamic/mod.d.ts +2 -2
  60. package/dist/tsup/inspector/mod.cjs +5 -5
  61. package/dist/tsup/inspector/mod.js +4 -4
  62. package/dist/tsup/inspector/workflow.cjs +4 -4
  63. package/dist/tsup/inspector/workflow.js +3 -3
  64. package/dist/tsup/inspector-tab/mod.d.cts +3 -3
  65. package/dist/tsup/inspector-tab/mod.d.ts +3 -3
  66. package/dist/tsup/mod.cjs +254 -166
  67. package/dist/tsup/mod.cjs.map +1 -1
  68. package/dist/tsup/mod.d.cts +4 -4
  69. package/dist/tsup/mod.d.ts +4 -4
  70. package/dist/tsup/mod.js +168 -80
  71. package/dist/tsup/mod.js.map +1 -1
  72. package/dist/tsup/test/mod.cjs +7 -7
  73. package/dist/tsup/test/mod.d.cts +2 -2
  74. package/dist/tsup/test/mod.d.ts +2 -2
  75. package/dist/tsup/test/mod.js +5 -5
  76. package/dist/tsup/unstable/migrations.d.cts +1 -1
  77. package/dist/tsup/unstable/migrations.d.ts +1 -1
  78. package/dist/tsup/utils.cjs +2 -2
  79. package/dist/tsup/utils.js +1 -1
  80. package/dist/tsup/workflow/mod.cjs +17 -17
  81. package/dist/tsup/workflow/mod.d.cts +4 -4
  82. package/dist/tsup/workflow/mod.d.ts +4 -4
  83. package/dist/tsup/workflow/mod.js +5 -5
  84. package/package.json +9 -10
  85. package/src/actor/keys.ts +86 -0
  86. package/src/client/actor-conn.ts +92 -6
  87. package/src/client/actor-handle.ts +21 -3
  88. package/src/client/client.ts +6 -0
  89. package/src/client/config.ts +12 -1
  90. package/src/client/lifecycle-errors.ts +16 -1
  91. package/src/client/retry-budget.ts +78 -0
  92. package/src/client/utils.ts +13 -2
  93. package/src/common/database/config.ts +7 -1
  94. package/src/common/database/native-database.ts +17 -6
  95. package/src/common/router.ts +1 -1
  96. package/src/engine-api/actors.ts +3 -3
  97. package/src/engine-client/actor-websocket-client.ts +4 -2
  98. package/src/engine-client/api-endpoints.ts +44 -14
  99. package/src/engine-client/mod.ts +2 -2
  100. package/src/registry/config/envoy.ts +1 -1
  101. package/src/registry/config/index.ts +28 -31
  102. package/src/registry/index.ts +51 -19
  103. package/src/registry/napi-runtime.ts +3 -2
  104. package/src/registry/native.ts +126 -34
  105. package/src/registry/runtime.ts +27 -10
  106. package/src/registry/wasm-runtime.ts +6 -3
  107. package/src/utils/env-vars.ts +15 -10
  108. package/dist/tsup/chunk-2BIRGEIU.cjs.map +0 -1
  109. package/dist/tsup/chunk-CORYIKDC.js.map +0 -1
  110. package/dist/tsup/chunk-I472KTIC.js.map +0 -1
  111. package/dist/tsup/chunk-JI6GZ2C2.js.map +0 -1
  112. package/dist/tsup/chunk-MYWZCDML.cjs.map +0 -1
  113. package/dist/tsup/chunk-P2PHHJMH.cjs.map +0 -1
  114. package/src/actor/config.test.ts +0 -78
  115. package/src/actor/keys.test.ts +0 -275
  116. package/src/actor/utils.test.ts +0 -48
  117. package/src/client/test.ts +0 -44
  118. package/src/client/token-provider.test.ts +0 -86
  119. package/src/common/database/mod.test.ts +0 -364
  120. package/src/common/database/native-database.test.ts +0 -220
  121. package/src/common/inline-websocket-adapter.test.ts +0 -51
  122. package/src/db/drizzle.test.ts +0 -164
  123. package/src/registry/config/index.test.ts +0 -25
  124. package/src/registry/run-handler-coordinator.test.ts +0 -252
  125. package/src/registry/runtime.test.ts +0 -124
  126. package/src/unstable/migrations.test.ts +0 -170
  127. package/src/utils/endpoint-parser.test.ts +0 -206
  128. package/src/utils.test.ts +0 -34
  129. package/src/workflow/driver.test.ts +0 -62
  130. /package/dist/tsup/{chunk-4SNS3PYZ.js.map → chunk-3URL73AV.js.map} +0 -0
  131. /package/dist/tsup/{chunk-EKAQ7HG2.js.map → chunk-442XBCAF.js.map} +0 -0
  132. /package/dist/tsup/{chunk-OUB7SGSA.js.map → chunk-ANNVZW6B.js.map} +0 -0
  133. /package/dist/tsup/{chunk-MXV26ZBB.js.map → chunk-OLZ4KAFL.js.map} +0 -0
  134. /package/dist/tsup/{chunk-H3SGQEHZ.js.map → chunk-WGKL7HMH.js.map} +0 -0
  135. /package/dist/tsup/{chunk-6LOYTFPZ.js.map → chunk-WGQHG26S.js.map} +0 -0
@@ -51,7 +51,13 @@ interface SqliteTransactionOptions {
51
51
  * to change without notice.
52
52
  */
53
53
  interface SqliteProfilingOptions {
54
- enabled?: boolean;
54
+ /** Exact SQL statements to give fingerprint labels. Profiling defaults to actor
55
+ * aggregates without fingerprint labels. Profile logs remain enabled.
56
+ * RIVETKIT_SQLITE_PROFILING overrides selection with a JSON object containing
57
+ * queries and/or fingerprints arrays. An empty object clears fingerprint selection. */
58
+ queries?: string[];
59
+ /** Exact query fingerprints to label separately, as reported in profile logs. */
60
+ fingerprints?: string[];
55
61
  maxTrackedStatementFingerprints?: number;
56
62
  maxTrackedTransactionFingerprints?: number;
57
63
  maxPrometheusSeries?: number;
@@ -753,6 +759,8 @@ interface RuntimeServeConfig {
753
759
  engineBinaryPath?: string;
754
760
  startServices?: boolean;
755
761
  servicesBinaryPath?: string;
762
+ /** Whether config resolution selected a managed local development engine. */
763
+ engineSpawn?: boolean;
756
764
  engineHost?: string;
757
765
  enginePort?: number;
758
766
  handleInspectorHttpInRuntime?: boolean;
@@ -841,7 +849,12 @@ interface CoreRuntime {
841
849
  createCancellationToken(): CancellationTokenHandle;
842
850
  cancellationTokenAborted(token: CancellationTokenHandle): boolean;
843
851
  cancelCancellationToken(token: CancellationTokenHandle): void;
844
- onCancellationTokenCancelled(token: CancellationTokenHandle, callback: (...args: unknown[]) => unknown): void;
852
+ /**
853
+ * Calls `callback` once when `token` is cancelled. Returns a release
854
+ * function that drops the registration without calling `callback`; call it
855
+ * once cancellation no longer matters so the native listener is freed.
856
+ */
857
+ onCancellationTokenCancelled(token: CancellationTokenHandle, callback: (...args: unknown[]) => unknown): () => void;
845
858
  actorState(ctx: ActorContextHandle): RuntimeBytes;
846
859
  actorBeginOnStateChange(ctx: ActorContextHandle): void;
847
860
  actorEndOnStateChange(ctx: ActorContextHandle): void;
@@ -1231,6 +1244,27 @@ declare class ActorConnRaw {
1231
1244
  */
1232
1245
  type ActorConn<AD extends AnyActorDefinition> = Omit<ActorConnRaw, "send" | "on" | "once"> & ActorDefinitionQueueSend<AD> & ActorDefinitionEventSubscriptions<AD> & ActorDefinitionActions<AD>;
1233
1246
 
1247
+ /** How many retries a client may spend, as a share of the requests it sends. */
1248
+ interface RetryBudgetOptions {
1249
+ /** Retries allowed per 100 requests (default 10). */
1250
+ percent?: number;
1251
+ /** Retries the client can bank for a burst of refusals; the budget starts full (default 100). */
1252
+ maxTokens?: number;
1253
+ }
1254
+ /**
1255
+ * Bounds the client's retries of refused requests. Each request adds a fraction of a token and
1256
+ * each retry spends one, so retries stay a fixed share of traffic while the service sheds load
1257
+ * instead of multiplying it.
1258
+ */
1259
+ declare class RetryBudget {
1260
+ #private;
1261
+ constructor(options?: RetryBudgetOptions);
1262
+ /** Credits one request sent. */
1263
+ deposit(): void;
1264
+ /** Spends one token. Returns `false` when the budget is spent. */
1265
+ tryWithdraw(): boolean;
1266
+ }
1267
+
1234
1268
  /**
1235
1269
  * Provides underlying functions for stateless {@link ActorHandle} for action calls.
1236
1270
  * Similar to ActorConnRaw but doesn't maintain a connection.
@@ -1246,7 +1280,7 @@ declare class ActorHandleRaw {
1246
1280
  *
1247
1281
  * @protected
1248
1282
  */
1249
- constructor(client: any, driver: EngineControlClient, params: unknown, getParams: (() => Promise<unknown>) | undefined, encoding: Encoding, actorResolutionState: ActorResolutionState, gatewayOptions?: ActorGatewayOptions, signal?: AbortSignal, currentActorInvocation?: CurrentActorInvocation, startCallSpan?: StartCallSpan);
1283
+ constructor(client: any, driver: EngineControlClient, params: unknown, getParams: (() => Promise<unknown>) | undefined, encoding: Encoding, actorResolutionState: ActorResolutionState, gatewayOptions?: ActorGatewayOptions, signal?: AbortSignal, currentActorInvocation?: CurrentActorInvocation, startCallSpan?: StartCallSpan, retryBudget?: RetryBudget);
1250
1284
  send(name: string, body: unknown, options: QueueSendWaitOptions): Promise<QueueSendResult>;
1251
1285
  send(name: string, body: unknown, options?: QueueSendNoWaitOptions): Promise<void>;
1252
1286
  /**
@@ -1330,6 +1364,10 @@ declare const ClientConfigSchema: z$1.ZodPipe<z$1.ZodObject<{
1330
1364
  skipReadyWait: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodBoolean>>;
1331
1365
  }, z$1.core.$strip>>>;
1332
1366
  getUpgradeWebSocket: z$1.ZodOptional<z$1.ZodCustom<GetUpgradeWebSocket, GetUpgradeWebSocket>>;
1367
+ retryBudget: z$1.ZodOptional<z$1.ZodObject<{
1368
+ percent: z$1.ZodOptional<z$1.ZodNumber>;
1369
+ maxTokens: z$1.ZodOptional<z$1.ZodNumber>;
1370
+ }, z$1.core.$strip>>;
1333
1371
  fetch: z$1.ZodOptional<z$1.ZodCustom<typeof fetch, typeof fetch>>;
1334
1372
  disableMetadataLookup: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodBoolean>>;
1335
1373
  maxInputSize: z$1.ZodDefault<z$1.ZodNumber>;
@@ -1349,6 +1387,10 @@ declare const ClientConfigSchema: z$1.ZodPipe<z$1.ZodObject<{
1349
1387
  devtools: boolean;
1350
1388
  getToken?: GetToken | undefined;
1351
1389
  getUpgradeWebSocket?: GetUpgradeWebSocket | undefined;
1390
+ retryBudget?: {
1391
+ percent?: number | undefined;
1392
+ maxTokens?: number | undefined;
1393
+ } | undefined;
1352
1394
  fetch?: typeof fetch | undefined;
1353
1395
  }, {
1354
1396
  endpoint: string;
@@ -1365,6 +1407,10 @@ declare const ClientConfigSchema: z$1.ZodPipe<z$1.ZodObject<{
1365
1407
  devtools: boolean;
1366
1408
  getToken?: GetToken | undefined;
1367
1409
  getUpgradeWebSocket?: GetUpgradeWebSocket | undefined;
1410
+ retryBudget?: {
1411
+ percent?: number | undefined;
1412
+ maxTokens?: number | undefined;
1413
+ } | undefined;
1368
1414
  fetch?: typeof fetch | undefined;
1369
1415
  }>>;
1370
1416
  type ClientConfigInput = z$1.input<typeof ClientConfigSchema>;
@@ -1513,6 +1559,8 @@ declare const CREATE_ACTOR_CONN_PROXY: unique symbol;
1513
1559
  interface ClientRawOptions {
1514
1560
  encoding?: Encoding;
1515
1561
  gateway?: ActorGatewayOptions;
1562
+ /** Bounds retries of refused requests across every handle of this client. */
1563
+ retryBudget?: RetryBudgetOptions;
1516
1564
  }
1517
1565
  /** Runtime-supplied wiring for clients created inside an actor. Set only by the native runtime. */
1518
1566
  interface ActorClientRuntimeOptions {
@@ -2308,11 +2356,11 @@ declare const RegistryConfigSchema: z$2.ZodPipe<z$2.ZodObject<{
2308
2356
  publicEndpoint: z$2.ZodPipe<z$2.ZodOptional<z$2.ZodString>, z$2.ZodTransform<string | undefined, string | undefined>>;
2309
2357
  publicToken: z$2.ZodPipe<z$2.ZodOptional<z$2.ZodString>, z$2.ZodTransform<string | undefined, string | undefined>>;
2310
2358
  }, z$2.core.$strip>>>;
2311
- envoy: z$2.ZodDefault<z$2.ZodOptional<z$2.ZodObject<{
2359
+ node: z$2.ZodDefault<z$2.ZodOptional<z$2.ZodObject<{
2312
2360
  poolName: z$2.ZodDefault<z$2.ZodString>;
2313
2361
  version: z$2.ZodDefault<z$2.ZodNumber>;
2314
2362
  totalSlots: z$2.ZodDefault<z$2.ZodNumber>;
2315
- envoyKey: z$2.ZodOptional<z$2.ZodString>;
2363
+ nodeKey: z$2.ZodOptional<z$2.ZodString>;
2316
2364
  }, z$2.core.$strip>>>;
2317
2365
  shutdown: z$2.ZodDefault<z$2.ZodOptional<z$2.ZodObject<{
2318
2366
  gracePeriodMs: z$2.ZodOptional<z$2.ZodNumber>;
@@ -2323,6 +2371,7 @@ declare const RegistryConfigSchema: z$2.ZodPipe<z$2.ZodObject<{
2323
2371
  backend: "local" | "remote";
2324
2372
  } | undefined;
2325
2373
  endpoint: string | undefined;
2374
+ engineSpawn: boolean;
2326
2375
  startServices: boolean;
2327
2376
  namespace: string;
2328
2377
  token: string | undefined;
@@ -2362,11 +2411,11 @@ declare const RegistryConfigSchema: z$2.ZodPipe<z$2.ZodObject<{
2362
2411
  engineHost: string;
2363
2412
  enginePort: number;
2364
2413
  engineVersion: string;
2365
- envoy: {
2414
+ node: {
2366
2415
  poolName: string;
2367
2416
  version: number;
2368
2417
  totalSlots: number;
2369
- envoyKey?: string | undefined;
2418
+ nodeKey?: string | undefined;
2370
2419
  };
2371
2420
  shutdown: {
2372
2421
  disableSignalHandlers: boolean;
@@ -2423,11 +2472,11 @@ declare const RegistryConfigSchema: z$2.ZodPipe<z$2.ZodObject<{
2423
2472
  publicEndpoint: string | undefined;
2424
2473
  publicToken: string | undefined;
2425
2474
  };
2426
- envoy: {
2475
+ node: {
2427
2476
  poolName: string;
2428
2477
  version: number;
2429
2478
  totalSlots: number;
2430
- envoyKey?: string | undefined;
2479
+ nodeKey?: string | undefined;
2431
2480
  };
2432
2481
  shutdown: {
2433
2482
  disableSignalHandlers: boolean;
@@ -230,7 +230,7 @@ function noopNext() {
230
230
  // package.json
231
231
  var package_default = {
232
232
  name: "rivetkit",
233
- version: "2.3.30",
233
+ version: "3.0.0-preview.2",
234
234
  description: "Lightweight libraries for building stateful actors on edge platforms",
235
235
  license: "Apache-2.0",
236
236
  keywords: [
@@ -440,7 +440,6 @@ var package_default = {
440
440
  dependencies: {
441
441
  "@hono/zod-openapi": "^1.1.5",
442
442
  "@opentelemetry/api": "^1.1.0",
443
- "@rivet-dev/services": "^0.2.3",
444
443
  "@rivetkit/bare-ts": "^0.6.2",
445
444
  "@rivetkit/engine-api-full": "workspace:*",
446
445
  "@rivetkit/engine-cli": "workspace:*",
@@ -1152,6 +1151,22 @@ function deserializeActorKey(keyString) {
1152
1151
  }
1153
1152
  return parts;
1154
1153
  }
1154
+ var CANONICAL_SEPARATOR = ",";
1155
+ var CANONICAL_EMPTY_PART = "\\0";
1156
+ function encodeActorKey(key) {
1157
+ return key.map((part) => {
1158
+ if (part === "") {
1159
+ return CANONICAL_EMPTY_PART;
1160
+ }
1161
+ return part.replace(/\\/g, "\\\\").replace(/,/g, "\\,");
1162
+ }).join(CANONICAL_SEPARATOR);
1163
+ }
1164
+ function actorKeyToGatewayQuery(key) {
1165
+ if (key.length === 0) {
1166
+ return void 0;
1167
+ }
1168
+ return encodeActorKey(key);
1169
+ }
1155
1170
 
1156
1171
  // src/client/token-provider.ts
1157
1172
  function isInvalidToken(group, code) {
@@ -1464,6 +1479,14 @@ var ClientConfigSchemaBase = z3.object({
1464
1479
  // This is required in the client config in order to support
1465
1480
  // `proxyWebSocket`
1466
1481
  getUpgradeWebSocket: z3.custom().optional(),
1482
+ /**
1483
+ * Bounds retries of refused requests: `percent` retries per 100 requests (default 10), with
1484
+ * up to `maxTokens` banked for bursts (default 100). A refusal past the budget is thrown.
1485
+ */
1486
+ retryBudget: z3.object({
1487
+ percent: z3.number().min(0).max(100).optional(),
1488
+ maxTokens: z3.number().int().min(0).optional()
1489
+ }).optional(),
1467
1490
  /** Replaces global fetch for actor HTTP gateway requests. */
1468
1491
  fetch: z3.custom().optional(),
1469
1492
  /** Whether to automatically perform health checks when the client is created. */
@@ -1780,8 +1803,9 @@ function buildActorQueryGatewayUrl(endpoint, namespace, query, token, path = "",
1780
1803
  return combineUrlPath(endpoint, gatewayPath);
1781
1804
  }
1782
1805
  function pushKeyQueryParams(params, key) {
1783
- if (key.length > 0) {
1784
- params.append("rvt-key", key.join(","));
1806
+ const rvtKey = actorKeyToGatewayQuery(key);
1807
+ if (rvtKey !== void 0) {
1808
+ params.append("rvt-key", rvtKey);
1785
1809
  }
1786
1810
  }
1787
1811
  function pushInputQueryParam(params, input, maxInputSize) {
@@ -3870,17 +3894,22 @@ function internalClientError(message, opts) {
3870
3894
  }
3871
3895
 
3872
3896
  // src/client/utils.ts
3897
+ var RETRY_AFTER_SUFFIX = /^retry_after_ms=(\d+)$/;
3873
3898
  function parseWebSocketCloseReason(reason) {
3874
- const [mainPart, rayId] = reason.split("#");
3899
+ const [mainPart, suffix] = reason.split("#");
3875
3900
  const [group, code] = mainPart.split(".");
3876
3901
  if (!group || !code) {
3877
3902
  logger2().warn({ msg: "failed to parse close reason", reason });
3878
3903
  return void 0;
3879
3904
  }
3905
+ const retryAfter = suffix?.match(RETRY_AFTER_SUFFIX);
3906
+ if (retryAfter) {
3907
+ return { group, code, retryAfterMs: Number(retryAfter[1]) };
3908
+ }
3880
3909
  return {
3881
3910
  group,
3882
3911
  code,
3883
- rayId
3912
+ rayId: suffix
3884
3913
  };
3885
3914
  }
3886
3915
  function messageLength(message) {
@@ -4459,7 +4488,7 @@ var RemoteEngineControlClient = class {
4459
4488
  datacenter: region,
4460
4489
  name,
4461
4490
  key: serializeActorKey(key),
4462
- runner_name_selector: poolName ?? this.#config.poolName,
4491
+ pool: poolName ?? this.#config.poolName,
4463
4492
  input: actorInput ? uint8ArrayToBase64(
4464
4493
  encodeCborCompat(actorInput)
4465
4494
  ) : void 0,
@@ -4512,7 +4541,7 @@ var RemoteEngineControlClient = class {
4512
4541
  (config) => createActor(config, {
4513
4542
  datacenter: region,
4514
4543
  name,
4515
- runner_name_selector: poolName ?? this.#config.poolName,
4544
+ pool: poolName ?? this.#config.poolName,
4516
4545
  key: serializeActorKey(key),
4517
4546
  input: input ? uint8ArrayToBase64(
4518
4547
  encodeCborCompat(input)
@@ -4816,6 +4845,56 @@ async function checkForSchedulingError(group, code, actorId, query, driver, rayI
4816
4845
  return null;
4817
4846
  }
4818
4847
 
4848
+ // src/client/retry-budget.ts
4849
+ var DEFAULT_PERCENT = 10;
4850
+ var DEFAULT_MAX_TOKENS = 100;
4851
+ var RetryBudget = class {
4852
+ #tokens;
4853
+ #maxTokens;
4854
+ #deposit;
4855
+ constructor(options = {}) {
4856
+ const percent = options.percent ?? DEFAULT_PERCENT;
4857
+ const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
4858
+ if (!(percent >= 0 && percent <= 100)) {
4859
+ throw new Error("retryBudget.percent must be in 0..=100");
4860
+ }
4861
+ if (!(maxTokens >= 0)) {
4862
+ throw new Error("retryBudget.maxTokens must be at least 0");
4863
+ }
4864
+ this.#maxTokens = maxTokens;
4865
+ this.#tokens = maxTokens;
4866
+ this.#deposit = percent / 100;
4867
+ }
4868
+ /** Credits one request sent. */
4869
+ deposit() {
4870
+ this.#tokens = Math.min(this.#maxTokens, this.#tokens + this.#deposit);
4871
+ }
4872
+ /** Spends one token. Returns `false` when the budget is spent. */
4873
+ tryWithdraw() {
4874
+ if (this.#tokens + 1e-9 < 1) {
4875
+ return false;
4876
+ }
4877
+ this.#tokens = Math.max(0, this.#tokens - 1);
4878
+ return true;
4879
+ }
4880
+ };
4881
+ function retryAfterMs(error) {
4882
+ if (!(error instanceof RivetError)) {
4883
+ return void 0;
4884
+ }
4885
+ const metadata = error.metadata;
4886
+ if (metadata === null || typeof metadata !== "object") {
4887
+ return void 0;
4888
+ }
4889
+ const hint = metadata.retry_after_ms;
4890
+ return typeof hint === "number" && hint >= 0 ? hint : void 0;
4891
+ }
4892
+ function retryDelayMs(attempt, opts) {
4893
+ const backoff = Math.min(opts.initialDelayMs * 2 ** attempt, opts.maxDelayMs);
4894
+ const jittered = backoff / 2 + Math.random() * (backoff / 2);
4895
+ return Math.max(jittered, opts.hintMs ?? 0);
4896
+ }
4897
+
4819
4898
  // src/client/lifecycle-errors.ts
4820
4899
  function* walkErrorChain(error, maxDepth = 8) {
4821
4900
  let current = error;
@@ -4993,6 +5072,7 @@ async function retryOnLifecycleBoundary(run, opts) {
4993
5072
  const maxAttempts = opts?.maxAttempts ?? 5;
4994
5073
  const initialDelayMs = opts?.initialDelayMs ?? 25;
4995
5074
  const maxDelayMs = opts?.maxDelayMs ?? 200;
5075
+ opts?.budget?.deposit();
4996
5076
  let lastError;
4997
5077
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
4998
5078
  throwIfAborted(opts?.signal);
@@ -5006,7 +5086,14 @@ async function retryOnLifecycleBoundary(run, opts) {
5006
5086
  if (attempt === maxAttempts - 1) {
5007
5087
  break;
5008
5088
  }
5009
- const delayMs = Math.min(initialDelayMs * 2 ** attempt, maxDelayMs);
5089
+ if (opts?.budget && !opts.budget.tryWithdraw()) {
5090
+ break;
5091
+ }
5092
+ const delayMs = retryDelayMs(attempt, {
5093
+ initialDelayMs,
5094
+ maxDelayMs,
5095
+ hintMs: retryAfterMs(error)
5096
+ });
5010
5097
  await waitWithSignal(delayMs, opts?.signal);
5011
5098
  }
5012
5099
  }
@@ -5157,6 +5244,10 @@ function createQueueSender(senderOptions) {
5157
5244
  }
5158
5245
 
5159
5246
  // src/client/actor-conn.ts
5247
+ var CONNECT_TIMEOUT_MS = 2e4;
5248
+ function isConnectionOpenTimeout(error) {
5249
+ return error instanceof RivetError && error.group === "client" && error.code === "connection_open_timeout";
5250
+ }
5160
5251
  var CONNECT_SYMBOL = /* @__PURE__ */ Symbol("connect");
5161
5252
  var ActorConnRaw = class {
5162
5253
  #disposed = false;
@@ -5184,6 +5275,8 @@ var ActorConnRaw = class {
5184
5275
  #keepNodeAliveInterval;
5185
5276
  /** Promise used to indicate the socket has connected successfully. This will be rejected if the connection fails. */
5186
5277
  #onOpenPromise;
5278
+ /** Sockets given up on by the connect timeout. Their late events are ignored. */
5279
+ #abandonedSockets = /* @__PURE__ */ new WeakSet();
5187
5280
  #websocket;
5188
5281
  #authRetryAvailable = true;
5189
5282
  #client;
@@ -5371,12 +5464,16 @@ var ActorConnRaw = class {
5371
5464
  forever: true,
5372
5465
  minTimeout: 250,
5373
5466
  maxTimeout: 3e4,
5374
- onFailedAttempt: (error) => {
5467
+ onFailedAttempt: async (error) => {
5375
5468
  logger2().warn({
5376
5469
  msg: "failed to reconnect",
5377
5470
  attempt: error.attemptNumber,
5378
5471
  error: stringifyError(error)
5379
5472
  });
5473
+ const hintMs = retryAfterMs(error);
5474
+ if (hintMs !== void 0) {
5475
+ await new Promise((resolve) => setTimeout(resolve, hintMs));
5476
+ }
5380
5477
  },
5381
5478
  // Cancel retry if aborted
5382
5479
  signal: this.#abortController.signal
@@ -5402,7 +5499,7 @@ var ActorConnRaw = class {
5402
5499
  })
5403
5500
  );
5404
5501
  await this.#connectWebSocket();
5405
- await this.#onOpenPromise.promise;
5502
+ await this.#waitForOpen(this.#onOpenPromise.promise);
5406
5503
  } catch (error) {
5407
5504
  if (this.#shouldRetryConnectionOpenError(error)) {
5408
5505
  throw error;
@@ -5424,10 +5521,63 @@ var ActorConnRaw = class {
5424
5521
  this.#onOpenPromise = void 0;
5425
5522
  }
5426
5523
  }
5524
+ /**
5525
+ * Waits for the actor's Init on the current socket, up to {@link CONNECT_TIMEOUT_MS}. A socket
5526
+ * can stay connecting with no open, error or close event, as when the engine holds the
5527
+ * upgrade or a refused connect reports only an error. On timeout the socket is abandoned
5528
+ * and closed, and the retryable `client.connection_open_timeout` starts the next attempt.
5529
+ */
5530
+ async #waitForOpen(open) {
5531
+ let timer;
5532
+ const timedOut = new Promise((_, reject) => {
5533
+ timer = setTimeout(() => {
5534
+ reject(
5535
+ new RivetError(
5536
+ "client",
5537
+ "connection_open_timeout",
5538
+ `Connection did not open within ${CONNECT_TIMEOUT_MS} ms`
5539
+ )
5540
+ );
5541
+ }, CONNECT_TIMEOUT_MS);
5542
+ });
5543
+ try {
5544
+ await Promise.race([open, timedOut]);
5545
+ } catch (error) {
5546
+ if (isConnectionOpenTimeout(error)) {
5547
+ this.#abandonCurrentSocket();
5548
+ }
5549
+ throw error;
5550
+ } finally {
5551
+ clearTimeout(timer);
5552
+ }
5553
+ }
5554
+ #abandonCurrentSocket() {
5555
+ const ws = this.#websocket;
5556
+ if (!ws) return;
5557
+ this.#websocket = void 0;
5558
+ this.#abandonedSockets.add(ws);
5559
+ logger2().warn({
5560
+ msg: "connection did not open in time, retrying",
5561
+ connId: this.#connId,
5562
+ readyState: ws.readyState,
5563
+ timeoutMs: CONNECT_TIMEOUT_MS
5564
+ });
5565
+ try {
5566
+ ws.close(1e3, "Connect timeout");
5567
+ } catch (err) {
5568
+ logger2().debug({
5569
+ msg: "failed to close abandoned websocket",
5570
+ error: stringifyError(err)
5571
+ });
5572
+ }
5573
+ }
5427
5574
  #shouldRetryConnectionOpenError(error) {
5428
5575
  if (error instanceof ActorConnDisposed) {
5429
5576
  return false;
5430
5577
  }
5578
+ if (isConnectionOpenTimeout(error)) {
5579
+ return true;
5580
+ }
5431
5581
  if (error instanceof RivetError && isInvalidToken(error.group, error.code) && this.#driver.refreshAuthToken && this.#authRetryAvailable) {
5432
5582
  this.#authRetryAvailable = false;
5433
5583
  return true;
@@ -5487,12 +5637,14 @@ var ActorConnRaw = class {
5487
5637
  });
5488
5638
  this.#websocket = ws;
5489
5639
  ws.addEventListener("open", () => {
5640
+ if (this.#abandonedSockets.has(ws)) return;
5490
5641
  logger2().debug({
5491
5642
  msg: "client websocket open",
5492
5643
  connId: this.#connId
5493
5644
  });
5494
5645
  });
5495
5646
  ws.addEventListener("message", async (ev) => {
5647
+ if (this.#abandonedSockets.has(ws)) return;
5496
5648
  try {
5497
5649
  await this.#handleOnMessage(ev.data);
5498
5650
  } catch (err) {
@@ -5503,6 +5655,7 @@ var ActorConnRaw = class {
5503
5655
  }
5504
5656
  });
5505
5657
  ws.addEventListener("close", async (ev) => {
5658
+ if (this.#abandonedSockets.has(ws)) return;
5506
5659
  try {
5507
5660
  await this.#handleOnClose(ev);
5508
5661
  } catch (err) {
@@ -5513,6 +5666,7 @@ var ActorConnRaw = class {
5513
5666
  }
5514
5667
  });
5515
5668
  ws.addEventListener("error", () => {
5669
+ if (this.#abandonedSockets.has(ws)) return;
5516
5670
  try {
5517
5671
  this.#handleOnError();
5518
5672
  } catch (err) {
@@ -5719,7 +5873,8 @@ var ActorConnRaw = class {
5719
5873
  const reason = closeEvent.reason || "";
5720
5874
  const parsed = parseWebSocketCloseReason(reason);
5721
5875
  if (parsed) {
5722
- const { group, code, rayId } = parsed;
5876
+ const { group, code, rayId, retryAfterMs: retryAfterMs2 } = parsed;
5877
+ const closeOptions = retryAfterMs2 === void 0 ? { rayId } : { rayId, metadata: { retry_after_ms: retryAfterMs2 } };
5723
5878
  if (isInvalidToken(group, code) && closeEvent.code === 1008 && failedSocket) {
5724
5879
  try {
5725
5880
  if (!await this.#driver.refreshAuthToken?.(
@@ -5741,7 +5896,7 @@ var ActorConnRaw = class {
5741
5896
  group,
5742
5897
  code,
5743
5898
  `Connection closed: ${reason}`,
5744
- { rayId }
5899
+ closeOptions
5745
5900
  )
5746
5901
  );
5747
5902
  return;
@@ -5762,7 +5917,7 @@ var ActorConnRaw = class {
5762
5917
  group,
5763
5918
  code,
5764
5919
  `Connection closed: ${reason}`,
5765
- { rayId }
5920
+ closeOptions
5766
5921
  );
5767
5922
  }
5768
5923
  } else {
@@ -5770,7 +5925,7 @@ var ActorConnRaw = class {
5770
5925
  group,
5771
5926
  code,
5772
5927
  `Connection closed: ${reason}`,
5773
- { rayId }
5928
+ closeOptions
5774
5929
  );
5775
5930
  }
5776
5931
  this.#invalidateActorIfStale(group, code);
@@ -6454,6 +6609,7 @@ var ActorHandleRaw = class {
6454
6609
  #queueSendMutex = new AsyncMutex();
6455
6610
  #currentActorInvocation;
6456
6611
  #startCallSpan;
6612
+ #retryBudget;
6457
6613
  /**
6458
6614
  * Do not call this directly.
6459
6615
  *
@@ -6461,7 +6617,7 @@ var ActorHandleRaw = class {
6461
6617
  *
6462
6618
  * @protected
6463
6619
  */
6464
- constructor(client, driver, params, getParams, encoding, actorResolutionState, gatewayOptions = {}, signal, currentActorInvocation, startCallSpan) {
6620
+ constructor(client, driver, params, getParams, encoding, actorResolutionState, gatewayOptions = {}, signal, currentActorInvocation, startCallSpan, retryBudget = new RetryBudget()) {
6465
6621
  this.#client = client;
6466
6622
  this.#driver = driver;
6467
6623
  this.#encoding = encoding;
@@ -6472,6 +6628,7 @@ var ActorHandleRaw = class {
6472
6628
  this.#signal = signal;
6473
6629
  this.#currentActorInvocation = currentActorInvocation;
6474
6630
  this.#startCallSpan = startCallSpan;
6631
+ this.#retryBudget = retryBudget;
6475
6632
  }
6476
6633
  async #resolveConnectionParams() {
6477
6634
  if (this.#getParams) {
@@ -6488,6 +6645,7 @@ var ActorHandleRaw = class {
6488
6645
  let useQueryTarget = isDynamicActorQuery(
6489
6646
  this.#actorResolutionState
6490
6647
  );
6648
+ this.#retryBudget.deposit();
6491
6649
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
6492
6650
  let actorId;
6493
6651
  try {
@@ -6521,8 +6679,17 @@ var ActorHandleRaw = class {
6521
6679
  metadata,
6522
6680
  attempt,
6523
6681
  maxAttempts
6524
- )) {
6525
- await this.#waitForRetryWindow();
6682
+ ) && this.#retryBudget.tryWithdraw()) {
6683
+ await new Promise(
6684
+ (resolve) => setTimeout(
6685
+ resolve,
6686
+ retryDelayMs(attempt, {
6687
+ initialDelayMs: 25,
6688
+ maxDelayMs: 1e3,
6689
+ hintMs: retryAfterMs(err)
6690
+ })
6691
+ )
6692
+ );
6526
6693
  continue;
6527
6694
  }
6528
6695
  if (await this.#shouldRetrySchedulingError(
@@ -6597,7 +6764,10 @@ var ActorHandleRaw = class {
6597
6764
  if (opts.name === "destroy") {
6598
6765
  return await run();
6599
6766
  }
6600
- return await retryOnLifecycleBoundary(run, { signal });
6767
+ return await retryOnLifecycleBoundary(run, {
6768
+ signal,
6769
+ budget: this.#retryBudget
6770
+ });
6601
6771
  };
6602
6772
  if (!call) {
6603
6773
  return await send();
@@ -7137,6 +7307,7 @@ var ClientRaw = class {
7137
7307
  #gatewayOptions;
7138
7308
  #currentActorInvocation;
7139
7309
  #startCallSpan;
7310
+ #retryBudget;
7140
7311
  /**
7141
7312
  * Creates an instance of Client.
7142
7313
  */
@@ -7149,6 +7320,7 @@ var ClientRaw = class {
7149
7320
  this.#gatewayOptions = options.gateway ?? {};
7150
7321
  this.#currentActorInvocation = options.currentActorInvocation;
7151
7322
  this.#startCallSpan = options.startCallSpan;
7323
+ this.#retryBudget = new RetryBudget(options.retryBudget);
7152
7324
  }
7153
7325
  /**
7154
7326
  * Gets a stateless handle to a actor by its ID.
@@ -7306,7 +7478,8 @@ var ClientRaw = class {
7306
7478
  this.#gatewayOptions,
7307
7479
  signal,
7308
7480
  this.#currentActorInvocation,
7309
- this.#startCallSpan
7481
+ this.#startCallSpan,
7482
+ this.#retryBudget
7310
7483
  );
7311
7484
  }
7312
7485
  [CREATE_ACTOR_CONN_PROXY](conn) {