rivetkit 2.0.38 → 2.0.39
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/dist/tsup/{chunk-6WLJW57U.cjs → chunk-7E3RWMR6.cjs} +161 -115
- package/dist/tsup/chunk-7E3RWMR6.cjs.map +1 -0
- package/dist/tsup/{chunk-FA6FGAEC.js → chunk-BQ36VTSB.js} +74 -28
- package/dist/tsup/chunk-BQ36VTSB.js.map +1 -0
- package/dist/tsup/{chunk-GFKZZG2A.cjs → chunk-C64FV764.cjs} +3 -3
- package/dist/tsup/{chunk-GFKZZG2A.cjs.map → chunk-C64FV764.cjs.map} +1 -1
- package/dist/tsup/{chunk-4U45T5KW.js → chunk-CDK6DRO2.js} +2 -2
- package/dist/tsup/{chunk-4U45T5KW.js.map → chunk-CDK6DRO2.js.map} +1 -1
- package/dist/tsup/{chunk-IRTVRBJA.cjs → chunk-DY4H3ASE.cjs} +50 -46
- package/dist/tsup/chunk-DY4H3ASE.cjs.map +1 -0
- package/dist/tsup/{chunk-IWXMFQDT.cjs → chunk-KMYFL3OL.cjs} +264 -77
- package/dist/tsup/chunk-KMYFL3OL.cjs.map +1 -0
- package/dist/tsup/{chunk-FZQHTGQX.cjs → chunk-MZPYVTVG.cjs} +9 -9
- package/dist/tsup/{chunk-FZQHTGQX.cjs.map → chunk-MZPYVTVG.cjs.map} +1 -1
- package/dist/tsup/{chunk-K2RNF2ZR.js → chunk-OJZRCEIA.js} +5 -5
- package/dist/tsup/{chunk-WIZ4JGP6.js → chunk-PHCD25XO.js} +2 -2
- package/dist/tsup/{chunk-MIOU6BF3.js → chunk-PVKUXMOA.js} +209 -22
- package/dist/tsup/chunk-PVKUXMOA.js.map +1 -0
- package/dist/tsup/{chunk-UUEZVDRL.js → chunk-T7IPDBWH.js} +8 -4
- package/dist/tsup/{chunk-UUEZVDRL.js.map → chunk-T7IPDBWH.js.map} +1 -1
- package/dist/tsup/{chunk-LULP6HM2.cjs → chunk-UAX5E3EU.cjs} +311 -288
- package/dist/tsup/chunk-UAX5E3EU.cjs.map +1 -0
- package/dist/tsup/{chunk-O433HWWG.cjs → chunk-X72X7I7T.cjs} +2 -2
- package/dist/tsup/{chunk-O433HWWG.cjs.map → chunk-X72X7I7T.cjs.map} +1 -1
- package/dist/tsup/{chunk-EEL32AJM.js → chunk-XU74APB4.js} +72 -49
- package/dist/tsup/chunk-XU74APB4.js.map +1 -0
- package/dist/tsup/client/mod.cjs +5 -5
- package/dist/tsup/client/mod.d.cts +3 -3
- package/dist/tsup/client/mod.d.ts +3 -3
- package/dist/tsup/client/mod.js +4 -4
- package/dist/tsup/common/log.cjs +2 -2
- package/dist/tsup/common/log.js +1 -1
- package/dist/tsup/common/websocket.cjs +3 -3
- package/dist/tsup/common/websocket.js +2 -2
- package/dist/tsup/{config-CwJCQyP1.d.cts → config-BuBlMs6C.d.cts} +72 -5
- package/dist/tsup/{config-CbIHPGKl.d.ts → config-CBwo4ooA.d.ts} +72 -5
- package/dist/tsup/{driver-Lw_oORox.d.cts → driver-CPXmh8f8.d.cts} +1 -1
- package/dist/tsup/{driver-CMN823Lc.d.ts → driver-DxWa6HUO.d.ts} +1 -1
- package/dist/tsup/driver-helpers/mod.cjs +3 -3
- package/dist/tsup/driver-helpers/mod.d.cts +2 -2
- package/dist/tsup/driver-helpers/mod.d.ts +2 -2
- package/dist/tsup/driver-helpers/mod.js +2 -2
- package/dist/tsup/driver-test-suite/mod.cjs +81 -35
- package/dist/tsup/driver-test-suite/mod.cjs.map +1 -1
- package/dist/tsup/driver-test-suite/mod.d.cts +2 -2
- package/dist/tsup/driver-test-suite/mod.d.ts +2 -2
- package/dist/tsup/driver-test-suite/mod.js +407 -361
- package/dist/tsup/driver-test-suite/mod.js.map +1 -1
- package/dist/tsup/{kv-CTM8sCvx.d.cts → keys-Chhy4ylv.d.cts} +1 -0
- package/dist/tsup/{kv-CTM8sCvx.d.ts → keys-Chhy4ylv.d.ts} +1 -0
- package/dist/tsup/mod.cjs +9 -7
- package/dist/tsup/mod.cjs.map +1 -1
- package/dist/tsup/mod.d.cts +5 -5
- package/dist/tsup/mod.d.ts +5 -5
- package/dist/tsup/mod.js +8 -6
- package/dist/tsup/test/mod.cjs +7 -7
- package/dist/tsup/test/mod.d.cts +1 -1
- package/dist/tsup/test/mod.d.ts +1 -1
- package/dist/tsup/test/mod.js +6 -6
- package/dist/tsup/utils.cjs +2 -2
- package/dist/tsup/utils.js +1 -1
- package/package.json +2 -2
- package/src/actor/config.ts +183 -34
- package/src/actor/contexts/base/actor.ts +12 -0
- package/src/actor/instance/connection-manager.ts +1 -1
- package/src/actor/instance/keys.ts +29 -0
- package/src/actor/instance/kv.ts +240 -14
- package/src/actor/instance/mod.ts +5 -4
- package/src/actor/instance/state-manager.ts +1 -1
- package/src/actor/mod.ts +2 -1
- package/src/actor/router-websocket-endpoints.ts +2 -1
- package/src/client/actor-handle.ts +13 -3
- package/src/client/mod.ts +1 -1
- package/src/driver-helpers/utils.ts +1 -1
- package/src/driver-test-suite/mod.ts +3 -0
- package/src/driver-test-suite/test-inline-client-driver.ts +3 -0
- package/src/driver-test-suite/tests/actor-kv.ts +44 -0
- package/src/driver-test-suite/utils.ts +4 -0
- package/src/drivers/engine/actor-driver.ts +3 -3
- package/src/drivers/file-system/manager.ts +5 -0
- package/src/manager/driver.ts +7 -0
- package/src/remote-manager-driver/actor-http-client.ts +5 -3
- package/src/remote-manager-driver/actor-websocket-client.ts +18 -7
- package/src/remote-manager-driver/mod.ts +10 -0
- package/dist/tsup/chunk-6WLJW57U.cjs.map +0 -1
- package/dist/tsup/chunk-EEL32AJM.js.map +0 -1
- package/dist/tsup/chunk-FA6FGAEC.js.map +0 -1
- package/dist/tsup/chunk-IRTVRBJA.cjs.map +0 -1
- package/dist/tsup/chunk-IWXMFQDT.cjs.map +0 -1
- package/dist/tsup/chunk-LULP6HM2.cjs.map +0 -1
- package/dist/tsup/chunk-MIOU6BF3.js.map +0 -1
- /package/dist/tsup/{chunk-K2RNF2ZR.js.map → chunk-OJZRCEIA.js.map} +0 -0
- /package/dist/tsup/{chunk-WIZ4JGP6.js.map → chunk-PHCD25XO.js.map} +0 -0
package/src/actor/instance/kv.ts
CHANGED
|
@@ -1,15 +1,241 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import type { ActorDriver } from "../driver";
|
|
2
|
+
import { makePrefixedKey, removePrefixFromKey } from "./keys";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* User-facing KV storage interface exposed on ActorContext.
|
|
6
|
+
*/
|
|
7
|
+
type KvValueType = "text" | "arrayBuffer" | "binary";
|
|
8
|
+
type KvKeyType = "text" | "binary";
|
|
9
|
+
type KvKey = Uint8Array | string;
|
|
10
|
+
|
|
11
|
+
type KvValueTypeMap = {
|
|
12
|
+
text: string;
|
|
13
|
+
arrayBuffer: ArrayBuffer;
|
|
14
|
+
binary: Uint8Array;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type KvKeyTypeMap = {
|
|
18
|
+
text: string;
|
|
19
|
+
binary: Uint8Array;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type KvValueOptions<T extends KvValueType = "text"> = {
|
|
23
|
+
type?: T;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type KvListOptions<
|
|
27
|
+
T extends KvValueType = "text",
|
|
28
|
+
K extends KvKeyType = "text",
|
|
29
|
+
> = KvValueOptions<T> & {
|
|
30
|
+
keyType?: K;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const textEncoder = new TextEncoder();
|
|
34
|
+
const textDecoder = new TextDecoder();
|
|
35
|
+
|
|
36
|
+
function encodeKey<K extends KvKeyType = KvKeyType>(
|
|
37
|
+
key: KvKeyTypeMap[K],
|
|
38
|
+
keyType?: K,
|
|
39
|
+
): Uint8Array {
|
|
40
|
+
if (key instanceof Uint8Array) {
|
|
41
|
+
return key;
|
|
42
|
+
}
|
|
43
|
+
const resolvedKeyType = keyType ?? "text";
|
|
44
|
+
if (resolvedKeyType === "binary") {
|
|
45
|
+
throw new TypeError("Expected a Uint8Array when keyType is binary");
|
|
46
|
+
}
|
|
47
|
+
return textEncoder.encode(key);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function decodeKey<K extends KvKeyType = "text">(
|
|
51
|
+
key: Uint8Array,
|
|
52
|
+
keyType?: K,
|
|
53
|
+
): KvKeyTypeMap[K] {
|
|
54
|
+
const resolvedKeyType = keyType ?? "text";
|
|
55
|
+
switch (resolvedKeyType) {
|
|
56
|
+
case "text":
|
|
57
|
+
return textDecoder.decode(key) as KvKeyTypeMap[K];
|
|
58
|
+
case "binary":
|
|
59
|
+
return key as KvKeyTypeMap[K];
|
|
60
|
+
default:
|
|
61
|
+
throw new TypeError("Invalid kv key type");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function resolveValueType(
|
|
66
|
+
value: string | Uint8Array | ArrayBuffer,
|
|
67
|
+
): KvValueType {
|
|
68
|
+
if (typeof value === "string") {
|
|
69
|
+
return "text";
|
|
70
|
+
}
|
|
71
|
+
if (value instanceof Uint8Array) {
|
|
72
|
+
return "binary";
|
|
73
|
+
}
|
|
74
|
+
if (value instanceof ArrayBuffer) {
|
|
75
|
+
return "arrayBuffer";
|
|
76
|
+
}
|
|
77
|
+
throw new TypeError("Invalid kv value");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function encodeValue<T extends KvValueType = KvValueType>(
|
|
81
|
+
value: KvValueTypeMap[T],
|
|
82
|
+
options?: KvValueOptions<T>,
|
|
83
|
+
): Uint8Array {
|
|
84
|
+
const type =
|
|
85
|
+
options?.type ??
|
|
86
|
+
resolveValueType(value as string | Uint8Array | ArrayBuffer);
|
|
87
|
+
switch (type) {
|
|
88
|
+
case "text":
|
|
89
|
+
if (typeof value !== "string") {
|
|
90
|
+
throw new TypeError("Expected a string when type is text");
|
|
91
|
+
}
|
|
92
|
+
return textEncoder.encode(value);
|
|
93
|
+
case "arrayBuffer":
|
|
94
|
+
if (!(value instanceof ArrayBuffer)) {
|
|
95
|
+
throw new TypeError("Expected an ArrayBuffer when type is arrayBuffer");
|
|
96
|
+
}
|
|
97
|
+
return new Uint8Array(value);
|
|
98
|
+
case "binary":
|
|
99
|
+
if (!(value instanceof Uint8Array)) {
|
|
100
|
+
throw new TypeError("Expected a Uint8Array when type is binary");
|
|
101
|
+
}
|
|
102
|
+
return value;
|
|
103
|
+
default:
|
|
104
|
+
throw new TypeError("Invalid kv value type");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function decodeValue<T extends KvValueType = "text">(
|
|
109
|
+
value: Uint8Array,
|
|
110
|
+
options?: KvValueOptions<T>,
|
|
111
|
+
): KvValueTypeMap[T] {
|
|
112
|
+
const type = options?.type ?? "text";
|
|
113
|
+
switch (type) {
|
|
114
|
+
case "text":
|
|
115
|
+
return textDecoder.decode(value) as KvValueTypeMap[T];
|
|
116
|
+
case "arrayBuffer": {
|
|
117
|
+
const copy = new Uint8Array(value.byteLength);
|
|
118
|
+
copy.set(value);
|
|
119
|
+
return copy.buffer as KvValueTypeMap[T];
|
|
120
|
+
}
|
|
121
|
+
case "binary":
|
|
122
|
+
return value as KvValueTypeMap[T];
|
|
123
|
+
default:
|
|
124
|
+
throw new TypeError("Invalid kv value type");
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export class ActorKv {
|
|
129
|
+
#driver: ActorDriver;
|
|
130
|
+
#actorId: string;
|
|
131
|
+
|
|
132
|
+
constructor(driver: ActorDriver, actorId: string) {
|
|
133
|
+
this.#driver = driver;
|
|
134
|
+
this.#actorId = actorId;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get a single value by key.
|
|
139
|
+
*/
|
|
140
|
+
async get<T extends KvValueType = "text">(
|
|
141
|
+
key: KvKey,
|
|
142
|
+
options?: KvValueOptions<T>,
|
|
143
|
+
): Promise<KvValueTypeMap[T] | null> {
|
|
144
|
+
const results = await this.#driver.kvBatchGet(this.#actorId, [
|
|
145
|
+
makePrefixedKey(encodeKey(key)),
|
|
146
|
+
]);
|
|
147
|
+
const result = results[0];
|
|
148
|
+
if (!result) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
return decodeValue(result, options);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get multiple values by keys.
|
|
156
|
+
*/
|
|
157
|
+
async getBatch<T extends KvValueType = "text">(
|
|
158
|
+
keys: KvKey[],
|
|
159
|
+
options?: KvValueOptions<T>,
|
|
160
|
+
): Promise<(KvValueTypeMap[T] | null)[]> {
|
|
161
|
+
const prefixedKeys = keys.map((key) =>
|
|
162
|
+
makePrefixedKey(encodeKey(key)),
|
|
163
|
+
);
|
|
164
|
+
const results = await this.#driver.kvBatchGet(
|
|
165
|
+
this.#actorId,
|
|
166
|
+
prefixedKeys,
|
|
167
|
+
);
|
|
168
|
+
return results.map((result) =>
|
|
169
|
+
result ? decodeValue(result, options) : null,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Put a single key-value pair.
|
|
175
|
+
*/
|
|
176
|
+
async put<T extends KvValueType = KvValueType>(
|
|
177
|
+
key: KvKey,
|
|
178
|
+
value: KvValueTypeMap[T],
|
|
179
|
+
options?: KvValueOptions<T>,
|
|
180
|
+
): Promise<void> {
|
|
181
|
+
await this.#driver.kvBatchPut(this.#actorId, [
|
|
182
|
+
[makePrefixedKey(encodeKey(key)), encodeValue(value, options)],
|
|
183
|
+
]);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Put multiple key-value pairs.
|
|
188
|
+
*/
|
|
189
|
+
async putBatch<T extends KvValueType = KvValueType>(
|
|
190
|
+
entries: [KvKey, KvValueTypeMap[T]][],
|
|
191
|
+
options?: KvValueOptions<T>,
|
|
192
|
+
): Promise<void> {
|
|
193
|
+
const prefixedEntries: [Uint8Array, Uint8Array][] = entries.map(
|
|
194
|
+
([key, value]) => [
|
|
195
|
+
makePrefixedKey(encodeKey(key)),
|
|
196
|
+
encodeValue(value, options),
|
|
197
|
+
],
|
|
198
|
+
);
|
|
199
|
+
await this.#driver.kvBatchPut(this.#actorId, prefixedEntries);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Delete a single key.
|
|
204
|
+
*/
|
|
205
|
+
async delete(key: KvKey): Promise<void> {
|
|
206
|
+
await this.#driver.kvBatchDelete(this.#actorId, [
|
|
207
|
+
makePrefixedKey(encodeKey(key)),
|
|
208
|
+
]);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Delete multiple keys.
|
|
213
|
+
*/
|
|
214
|
+
async deleteBatch(keys: KvKey[]): Promise<void> {
|
|
215
|
+
const prefixedKeys = keys.map((key) =>
|
|
216
|
+
makePrefixedKey(encodeKey(key)),
|
|
217
|
+
);
|
|
218
|
+
await this.#driver.kvBatchDelete(this.#actorId, prefixedKeys);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* List all keys with a given prefix.
|
|
223
|
+
* Returns key-value pairs where keys have the user prefix removed.
|
|
224
|
+
*/
|
|
225
|
+
async list<T extends KvValueType = "text", K extends KvKeyType = "text">(
|
|
226
|
+
prefix: KvKeyTypeMap[K],
|
|
227
|
+
options?: KvListOptions<T, K>,
|
|
228
|
+
): Promise<[KvKeyTypeMap[K], KvValueTypeMap[T]][]> {
|
|
229
|
+
const prefixedPrefix = makePrefixedKey(
|
|
230
|
+
encodeKey(prefix, options?.keyType),
|
|
231
|
+
);
|
|
232
|
+
const results = await this.#driver.kvListPrefix(
|
|
233
|
+
this.#actorId,
|
|
234
|
+
prefixedPrefix,
|
|
235
|
+
);
|
|
236
|
+
return results.map(([key, value]) => [
|
|
237
|
+
decodeKey<K>(removePrefixFromKey(key), options?.keyType),
|
|
238
|
+
decodeValue<T>(value, options),
|
|
239
|
+
]);
|
|
240
|
+
}
|
|
15
241
|
}
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
} from "../utils";
|
|
45
45
|
import { ConnectionManager } from "./connection-manager";
|
|
46
46
|
import { EventManager } from "./event-manager";
|
|
47
|
-
import { KEYS } from "./
|
|
47
|
+
import { KEYS } from "./keys";
|
|
48
48
|
import {
|
|
49
49
|
convertActorFromBarePersisted,
|
|
50
50
|
type PersistedActor,
|
|
@@ -203,7 +203,7 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
get actions(): string[] {
|
|
206
|
-
return Object.keys(this.#config.actions);
|
|
206
|
+
return Object.keys(this.#config.actions ?? {});
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
get config(): ActorConfig<S, CP, CS, V, I, DB> {
|
|
@@ -516,12 +516,13 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
516
516
|
): Promise<unknown> {
|
|
517
517
|
this.assertReady();
|
|
518
518
|
|
|
519
|
-
|
|
519
|
+
const actions = this.#config.actions ?? {};
|
|
520
|
+
if (!(actionName in actions)) {
|
|
520
521
|
this.#rLog.warn({ msg: "action does not exist", actionName });
|
|
521
522
|
throw new errors.ActionNotFound(actionName);
|
|
522
523
|
}
|
|
523
524
|
|
|
524
|
-
const actionFunction =
|
|
525
|
+
const actionFunction = actions[actionName];
|
|
525
526
|
if (typeof actionFunction !== "function") {
|
|
526
527
|
this.#rLog.warn({
|
|
527
528
|
msg: "action is not a function",
|
|
@@ -12,7 +12,7 @@ import { convertConnToBarePersistedConn } from "../conn/persisted";
|
|
|
12
12
|
import type { ActorDriver } from "../driver";
|
|
13
13
|
import * as errors from "../errors";
|
|
14
14
|
import { isConnStatePath, isStatePath } from "../utils";
|
|
15
|
-
import { KEYS, makeConnKey } from "./
|
|
15
|
+
import { KEYS, makeConnKey } from "./keys";
|
|
16
16
|
import type { ActorInstance } from "./mod";
|
|
17
17
|
import { convertActorToBarePersisted, type PersistedActor } from "./persisted";
|
|
18
18
|
|
package/src/actor/mod.ts
CHANGED
|
@@ -76,7 +76,8 @@ export type { AnyConn, Conn } from "./conn/mod";
|
|
|
76
76
|
export type { ActorDefinition, AnyActorDefinition } from "./definition";
|
|
77
77
|
export { lookupInRegistry } from "./definition";
|
|
78
78
|
export { UserError, type UserErrorOptions } from "./errors";
|
|
79
|
-
export { KEYS as KV_KEYS } from "./instance/
|
|
79
|
+
export { KEYS as KV_KEYS } from "./instance/keys";
|
|
80
|
+
export { ActorKv } from "./instance/kv";
|
|
80
81
|
export type { AnyActorInstance } from "./instance/mod";
|
|
81
82
|
export {
|
|
82
83
|
type ActorRouter,
|
|
@@ -384,7 +384,8 @@ export function parseWebSocketProtocols(
|
|
|
384
384
|
}
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
-
|
|
387
|
+
// Default to "json" encoding for raw WebSocket connections without subprotocols
|
|
388
|
+
const encoding = EncodingSchema.parse(encodingRaw ?? "json");
|
|
388
389
|
const connParams = connParamsRaw ? JSON.parse(connParamsRaw) : undefined;
|
|
389
390
|
|
|
390
391
|
return { encoding, connParams };
|
|
@@ -239,9 +239,7 @@ export class ActorHandleRaw {
|
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
/**
|
|
242
|
-
* Resolves the actor to get its unique actor ID
|
|
243
|
-
*
|
|
244
|
-
* @returns {Promise<string>} - A promise that resolves to the actor's ID
|
|
242
|
+
* Resolves the actor to get its unique actor ID.
|
|
245
243
|
*/
|
|
246
244
|
async resolve({ signal }: { signal?: AbortSignal } = {}): Promise<string> {
|
|
247
245
|
if (
|
|
@@ -277,6 +275,18 @@ export class ActorHandleRaw {
|
|
|
277
275
|
assertUnreachable(this.#actorQuery);
|
|
278
276
|
}
|
|
279
277
|
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Returns the raw URL for routing traffic to the actor.
|
|
281
|
+
*/
|
|
282
|
+
async getGatewayUrl(): Promise<string> {
|
|
283
|
+
const { actorId } = await queryActor(
|
|
284
|
+
undefined,
|
|
285
|
+
this.#actorQuery,
|
|
286
|
+
this.#driver,
|
|
287
|
+
);
|
|
288
|
+
return await this.#driver.buildGatewayUrl(actorId);
|
|
289
|
+
}
|
|
280
290
|
}
|
|
281
291
|
|
|
282
292
|
/**
|
package/src/client/mod.ts
CHANGED
|
@@ -22,7 +22,7 @@ export {
|
|
|
22
22
|
ManagerError,
|
|
23
23
|
} from "@/client/errors";
|
|
24
24
|
export type { CreateRequest } from "@/manager/protocol/query";
|
|
25
|
-
export { KEYS as KV_KEYS } from "../actor/instance/
|
|
25
|
+
export { KEYS as KV_KEYS } from "../actor/instance/keys";
|
|
26
26
|
export type { ActorActionFunction } from "./actor-common";
|
|
27
27
|
export type {
|
|
28
28
|
ActorConn,
|
|
@@ -22,6 +22,7 @@ import { runActorErrorHandlingTests } from "./tests/actor-error-handling";
|
|
|
22
22
|
import { runActorHandleTests } from "./tests/actor-handle";
|
|
23
23
|
import { runActorInlineClientTests } from "./tests/actor-inline-client";
|
|
24
24
|
import { runActorInspectorTests } from "./tests/actor-inspector";
|
|
25
|
+
import { runActorKvTests } from "./tests/actor-kv";
|
|
25
26
|
import { runActorMetadataTests } from "./tests/actor-metadata";
|
|
26
27
|
import { runActorOnStateChangeTests } from "./tests/actor-onstatechange";
|
|
27
28
|
import { runActorVarsTests } from "./tests/actor-vars";
|
|
@@ -122,6 +123,8 @@ export function runDriverTests(
|
|
|
122
123
|
|
|
123
124
|
runActorInlineClientTests(driverTestConfig);
|
|
124
125
|
|
|
126
|
+
runActorKvTests(driverTestConfig);
|
|
127
|
+
|
|
125
128
|
runRawHttpTests(driverTestConfig);
|
|
126
129
|
|
|
127
130
|
runRawHttpRequestPropertiesTests(driverTestConfig);
|
|
@@ -237,6 +237,9 @@ export function createTestInlineClientDriver(
|
|
|
237
237
|
);
|
|
238
238
|
return upgradeWebSocket(() => wsHandler)(c, noopNext());
|
|
239
239
|
},
|
|
240
|
+
async buildGatewayUrl(actorId: string): Promise<string> {
|
|
241
|
+
return `${endpoint}/gateway/${actorId}`;
|
|
242
|
+
},
|
|
240
243
|
displayInformation(): ManagerDisplayInformation {
|
|
241
244
|
return { properties: {} };
|
|
242
245
|
},
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { DriverTestConfig } from "../mod";
|
|
2
|
+
import { setupDriverTest } from "../utils";
|
|
3
|
+
import { describe, expect, test, type TestContext } from "vitest";
|
|
4
|
+
|
|
5
|
+
export function runActorKvTests(driverTestConfig: DriverTestConfig) {
|
|
6
|
+
describe("Actor KV Tests", () => {
|
|
7
|
+
test("supports text encoding and decoding", async (c: TestContext) => {
|
|
8
|
+
const { client } = await setupDriverTest(c, driverTestConfig);
|
|
9
|
+
const kvHandle = client.kvActor.getOrCreate(["kv-text"]);
|
|
10
|
+
|
|
11
|
+
await kvHandle.putText("greeting", "hello");
|
|
12
|
+
const value = await kvHandle.getText("greeting");
|
|
13
|
+
expect(value).toBe("hello");
|
|
14
|
+
|
|
15
|
+
await kvHandle.putText("prefix-a", "alpha");
|
|
16
|
+
await kvHandle.putText("prefix-b", "beta");
|
|
17
|
+
|
|
18
|
+
const results = await kvHandle.listText("prefix-");
|
|
19
|
+
const sorted = results.sort((a, b) => a.key.localeCompare(b.key));
|
|
20
|
+
expect(sorted).toEqual([
|
|
21
|
+
{ key: "prefix-a", value: "alpha" },
|
|
22
|
+
{ key: "prefix-b", value: "beta" },
|
|
23
|
+
]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test(
|
|
27
|
+
"supports arrayBuffer encoding and decoding",
|
|
28
|
+
async (c: TestContext) => {
|
|
29
|
+
const { client } = await setupDriverTest(c, driverTestConfig);
|
|
30
|
+
const kvHandle = client.kvActor.getOrCreate(["kv-array-buffer"]);
|
|
31
|
+
|
|
32
|
+
const values = await kvHandle.roundtripArrayBuffer("bytes", [
|
|
33
|
+
4,
|
|
34
|
+
8,
|
|
35
|
+
15,
|
|
36
|
+
16,
|
|
37
|
+
23,
|
|
38
|
+
42,
|
|
39
|
+
]);
|
|
40
|
+
expect(values).toEqual([4, 8, 15, 16, 23, 42]);
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
@@ -39,6 +39,10 @@ export async function setupDriverTest(
|
|
|
39
39
|
namespace,
|
|
40
40
|
runnerName,
|
|
41
41
|
encoding: driverTestConfig.encoding,
|
|
42
|
+
// Disable metadata lookup to prevent redirect to the wrong port.
|
|
43
|
+
// Each test starts a new server on a dynamic port, but the
|
|
44
|
+
// registry's publicEndpoint defaults to port 6420.
|
|
45
|
+
disableMetadataLookup: true,
|
|
42
46
|
});
|
|
43
47
|
} else if (driverTestConfig.clientType === "inline") {
|
|
44
48
|
// Use inline client from driver
|
|
@@ -11,7 +11,7 @@ import { WSContext, type WSContextInit } from "hono/ws";
|
|
|
11
11
|
import invariant from "invariant";
|
|
12
12
|
import { type AnyConn, CONN_STATE_MANAGER_SYMBOL } from "@/actor/conn/mod";
|
|
13
13
|
import { lookupInRegistry } from "@/actor/definition";
|
|
14
|
-
import { KEYS } from "@/actor/instance/
|
|
14
|
+
import { KEYS } from "@/actor/instance/keys";
|
|
15
15
|
import { deserializeActorKey } from "@/actor/keys";
|
|
16
16
|
import { getValueLength } from "@/actor/protocol/old";
|
|
17
17
|
import { type ActorRouter, createActorRouter } from "@/actor/router";
|
|
@@ -153,7 +153,7 @@ export class EngineActorDriver implements ActorDriver {
|
|
|
153
153
|
onConnected: () => {
|
|
154
154
|
this.#runnerStarted.resolve(undefined);
|
|
155
155
|
},
|
|
156
|
-
onDisconnected: (_code, _reason) => {
|
|
156
|
+
onDisconnected: (_code, _reason) => {},
|
|
157
157
|
onShutdown: () => {
|
|
158
158
|
this.#runnerStopped.resolve(undefined);
|
|
159
159
|
this.#isRunnerStopped = true;
|
|
@@ -358,7 +358,7 @@ export class EngineActorDriver implements ActorDriver {
|
|
|
358
358
|
async serverlessHandleStart(c: HonoContext): Promise<Response> {
|
|
359
359
|
return streamSSE(c, async (stream) => {
|
|
360
360
|
// NOTE: onAbort does not work reliably
|
|
361
|
-
stream.onAbort(() => {
|
|
361
|
+
stream.onAbort(() => {});
|
|
362
362
|
c.req.raw.signal.addEventListener("abort", () => {
|
|
363
363
|
logger().debug("SSE aborted, shutting down runner");
|
|
364
364
|
|
|
@@ -147,6 +147,11 @@ export class FileSystemManagerDriver implements ManagerDriver {
|
|
|
147
147
|
return upgradeWebSocket(() => wsHandler)(c, noopNext());
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
async buildGatewayUrl(actorId: string): Promise<string> {
|
|
151
|
+
const port = this.#config.managerPort ?? 6420;
|
|
152
|
+
return `http://127.0.0.1:${port}/gateway/${encodeURIComponent(actorId)}`;
|
|
153
|
+
}
|
|
154
|
+
|
|
150
155
|
async getForId({
|
|
151
156
|
actorId,
|
|
152
157
|
}: GetForIdInput): Promise<ActorOutput | undefined> {
|
package/src/manager/driver.ts
CHANGED
|
@@ -32,6 +32,13 @@ export interface ManagerDriver {
|
|
|
32
32
|
params: unknown,
|
|
33
33
|
): Promise<Response>;
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Build a public gateway URL for a specific actor.
|
|
37
|
+
*
|
|
38
|
+
* This lives on the driver because the base endpoint varies by runtime.
|
|
39
|
+
*/
|
|
40
|
+
buildGatewayUrl(actorId: string): Promise<string>;
|
|
41
|
+
|
|
35
42
|
displayInformation(): ManagerDisplayInformation;
|
|
36
43
|
|
|
37
44
|
extraStartupLog?: () => Record<string, unknown>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ClientConfig } from "@/client/config";
|
|
2
2
|
import { HEADER_RIVET_TOKEN } from "@/common/actor-router-consts";
|
|
3
|
-
import {
|
|
3
|
+
import { buildActorGatewayUrl } from "./actor-websocket-client";
|
|
4
4
|
import { getEndpoint } from "./api-utils";
|
|
5
5
|
|
|
6
6
|
export async function sendHttpRequestToActor(
|
|
@@ -11,9 +11,11 @@ export async function sendHttpRequestToActor(
|
|
|
11
11
|
// Route through guard port
|
|
12
12
|
const url = new URL(actorRequest.url);
|
|
13
13
|
const endpoint = getEndpoint(runConfig);
|
|
14
|
-
const guardUrl =
|
|
14
|
+
const guardUrl = buildActorGatewayUrl(
|
|
15
15
|
endpoint,
|
|
16
|
-
|
|
16
|
+
actorId,
|
|
17
|
+
runConfig.token,
|
|
18
|
+
`${url.pathname}${url.search}`,
|
|
17
19
|
);
|
|
18
20
|
|
|
19
21
|
// Handle body properly based on method and presence
|
|
@@ -13,6 +13,18 @@ import { combineUrlPath } from "@/utils";
|
|
|
13
13
|
import { getEndpoint } from "./api-utils";
|
|
14
14
|
import { logger } from "./log";
|
|
15
15
|
|
|
16
|
+
export function buildActorGatewayUrl(
|
|
17
|
+
endpoint: string,
|
|
18
|
+
actorId: string,
|
|
19
|
+
token: string | undefined,
|
|
20
|
+
path = "",
|
|
21
|
+
): string {
|
|
22
|
+
const tokenSegment =
|
|
23
|
+
token !== undefined ? `@${encodeURIComponent(token)}` : "";
|
|
24
|
+
const gatewayPath = `/gateway/${encodeURIComponent(actorId)}${tokenSegment}${path}`;
|
|
25
|
+
return combineUrlPath(endpoint, gatewayPath);
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
export async function openWebSocketToActor(
|
|
17
29
|
runConfig: ClientConfig,
|
|
18
30
|
path: string,
|
|
@@ -24,13 +36,12 @@ export async function openWebSocketToActor(
|
|
|
24
36
|
|
|
25
37
|
// WebSocket connections go through guard
|
|
26
38
|
const endpoint = getEndpoint(runConfig);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const guardUrl = combineUrlPath(endpoint, gatewayPath);
|
|
39
|
+
const guardUrl = buildActorGatewayUrl(
|
|
40
|
+
endpoint,
|
|
41
|
+
actorId,
|
|
42
|
+
runConfig.token,
|
|
43
|
+
path,
|
|
44
|
+
);
|
|
34
45
|
|
|
35
46
|
logger().debug({
|
|
36
47
|
msg: "opening websocket to actor via guard",
|
|
@@ -21,6 +21,7 @@ import { combineUrlPath, type GetUpgradeWebSocket } from "@/utils";
|
|
|
21
21
|
import { getNextPhase } from "@/utils/env-vars";
|
|
22
22
|
import { sendHttpRequestToActor } from "./actor-http-client";
|
|
23
23
|
import {
|
|
24
|
+
buildActorGatewayUrl,
|
|
24
25
|
buildWebSocketProtocols,
|
|
25
26
|
openWebSocketToActor,
|
|
26
27
|
} from "./actor-websocket-client";
|
|
@@ -309,6 +310,15 @@ export class RemoteManagerDriver implements ManagerDriver {
|
|
|
309
310
|
);
|
|
310
311
|
}
|
|
311
312
|
|
|
313
|
+
async buildGatewayUrl(actorId: string): Promise<string> {
|
|
314
|
+
if (this.#metadataPromise) {
|
|
315
|
+
await this.#metadataPromise;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const endpoint = getEndpoint(this.#config);
|
|
319
|
+
return buildActorGatewayUrl(endpoint, actorId, this.#config.token);
|
|
320
|
+
}
|
|
321
|
+
|
|
312
322
|
async proxyRequest(
|
|
313
323
|
_c: HonoContext,
|
|
314
324
|
actorRequest: Request,
|