@rivetkit/engine-runner 2.0.28 → 2.0.29-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/package.json +7 -2
- package/.turbo/turbo-build.log +0 -22
- package/benches/actor-lifecycle.bench.ts +0 -190
- package/benches/utils.ts +0 -143
- package/tests/lifecycle.test.ts +0 -596
- package/tests/utils.test.ts +0 -194
- package/tsconfig.json +0 -11
- package/tsup.config.ts +0 -4
- package/turbo.json +0 -4
- package/vitest.config.ts +0 -16
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rivetkit/engine-runner",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.29-rc.1",
|
|
4
4
|
"type": "module",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist",
|
|
7
|
+
"src",
|
|
8
|
+
"package.json"
|
|
9
|
+
],
|
|
5
10
|
"exports": {
|
|
6
11
|
"import": {
|
|
7
12
|
"types": "./dist/mod.d.ts",
|
|
@@ -16,7 +21,7 @@
|
|
|
16
21
|
"uuid": "^12.0.0",
|
|
17
22
|
"pino": "^9.9.5",
|
|
18
23
|
"ws": "^8.18.3",
|
|
19
|
-
"@rivetkit/engine-runner-protocol": "2.0.
|
|
24
|
+
"@rivetkit/engine-runner-protocol": "2.0.29-rc.1"
|
|
20
25
|
},
|
|
21
26
|
"devDependencies": {
|
|
22
27
|
"@types/node": "^22.18.1",
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @rivetkit/engine-runner@2.0.28 build /home/runner/work/rivet/rivet/engine/sdks/typescript/runner
|
|
3
|
-
> tsup src/mod.ts
|
|
4
|
-
|
|
5
|
-
[34mCLI[39m Building entry: src/mod.ts
|
|
6
|
-
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
-
[34mCLI[39m tsup v8.5.0
|
|
8
|
-
[34mCLI[39m Using tsup config: /home/runner/work/rivet/rivet/engine/sdks/typescript/runner/tsup.config.ts
|
|
9
|
-
[34mCLI[39m Target: node16
|
|
10
|
-
[34mCLI[39m Cleaning output folder
|
|
11
|
-
[34mCJS[39m Build start
|
|
12
|
-
[34mESM[39m Build start
|
|
13
|
-
[32mESM[39m [1mdist/mod.js [22m[32m93.71 KB[39m
|
|
14
|
-
[32mESM[39m [1mdist/mod.js.map [22m[32m184.98 KB[39m
|
|
15
|
-
[32mESM[39m ⚡️ Build success in 605ms
|
|
16
|
-
[32mCJS[39m [1mdist/mod.cjs [22m[32m94.95 KB[39m
|
|
17
|
-
[32mCJS[39m [1mdist/mod.cjs.map [22m[32m157.62 KB[39m
|
|
18
|
-
[32mCJS[39m ⚡️ Build success in 610ms
|
|
19
|
-
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in 3637ms
|
|
21
|
-
[32mDTS[39m [1mdist/mod.d.cts [22m[32m14.77 KB[39m
|
|
22
|
-
[32mDTS[39m [1mdist/mod.d.ts [22m[32m14.77 KB[39m
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
// import { Bench } from "tinybench";
|
|
2
|
-
// import { Runner } from "@/mod";
|
|
3
|
-
// import type { ActorConfig } from "@/mod";
|
|
4
|
-
// import {
|
|
5
|
-
// createActor,
|
|
6
|
-
// destroyActor,
|
|
7
|
-
// setupBenchmarkRunner,
|
|
8
|
-
// createPromiseResolver,
|
|
9
|
-
// RIVET_ENDPOINT,
|
|
10
|
-
// } from "./utils.js";
|
|
11
|
-
// import { afterEach } from "node:test";
|
|
12
|
-
//
|
|
13
|
-
// async function runActorLifecycleBenchmark() {
|
|
14
|
-
// // Shared state for benchmarks
|
|
15
|
-
// let runner: Runner | null = null;
|
|
16
|
-
// let namespaceName: string;
|
|
17
|
-
// let runnerName: string;
|
|
18
|
-
// let createdActors: string[] = [];
|
|
19
|
-
// let wakeActorId: string | null = null;
|
|
20
|
-
// let stopped: { promise: Promise<void>; resolve: () => void };
|
|
21
|
-
// let started: { promise: Promise<void>; resolve: () => void };
|
|
22
|
-
//
|
|
23
|
-
// const bench = new Bench({
|
|
24
|
-
// time: 1000,
|
|
25
|
-
// iterations: 10,
|
|
26
|
-
// warmupTime: 0,
|
|
27
|
-
// warmupIterations: 0,
|
|
28
|
-
// throws: true,
|
|
29
|
-
// setup: async (task) => {
|
|
30
|
-
// // Setup benchmark runner
|
|
31
|
-
// console.log("Setting up benchmark runner...");
|
|
32
|
-
// stopped = createPromiseResolver<void>();
|
|
33
|
-
// started = createPromiseResolver<void>();
|
|
34
|
-
//
|
|
35
|
-
// const setup = await setupBenchmarkRunner(
|
|
36
|
-
// "lifecycle",
|
|
37
|
-
// 5054,
|
|
38
|
-
// async (
|
|
39
|
-
// _actorId: string,
|
|
40
|
-
// _generation: number,
|
|
41
|
-
// _config: ActorConfig,
|
|
42
|
-
// ) => {
|
|
43
|
-
// started.resolve();
|
|
44
|
-
// },
|
|
45
|
-
// async (_actorId: string, _generation: number) => {
|
|
46
|
-
// stopped.resolve();
|
|
47
|
-
// },
|
|
48
|
-
// );
|
|
49
|
-
// runner = setup.runner;
|
|
50
|
-
// namespaceName = setup.namespaceName;
|
|
51
|
-
// runnerName = setup.runnerName;
|
|
52
|
-
//
|
|
53
|
-
// console.log(
|
|
54
|
-
// `Benchmark setup complete. Namespace: ${namespaceName}, Runner: ${runnerName}`,
|
|
55
|
-
// );
|
|
56
|
-
// },
|
|
57
|
-
// teardown: async () => {
|
|
58
|
-
// if (runner) {
|
|
59
|
-
// await runner.shutdown(true);
|
|
60
|
-
// }
|
|
61
|
-
//
|
|
62
|
-
// // Clean up created actors from creation benchmark
|
|
63
|
-
// console.log(
|
|
64
|
-
// `Cleaning up ${createdActors.length} actors in ${namespaceName}...`,
|
|
65
|
-
// );
|
|
66
|
-
// const cleanupActor = createdActors;
|
|
67
|
-
// createdActors = [];
|
|
68
|
-
// wakeActorId = null;
|
|
69
|
-
// for (const actorId of cleanupActor) {
|
|
70
|
-
// try {
|
|
71
|
-
// await destroyActor(namespaceName, actorId);
|
|
72
|
-
// } catch (err) {
|
|
73
|
-
// console.warn(`Failed to clean up actor ${actorId}:`, err);
|
|
74
|
-
// }
|
|
75
|
-
// }
|
|
76
|
-
//
|
|
77
|
-
// console.log("Benchmark teardown complete!");
|
|
78
|
-
// },
|
|
79
|
-
// });
|
|
80
|
-
//
|
|
81
|
-
// bench.add("create actor", async () => {
|
|
82
|
-
// const actorResponse = await createActor(
|
|
83
|
-
// namespaceName,
|
|
84
|
-
// runnerName,
|
|
85
|
-
// false,
|
|
86
|
-
// );
|
|
87
|
-
// const actorId = actorResponse.actor.actor_id;
|
|
88
|
-
// createdActors.push(actorId);
|
|
89
|
-
//
|
|
90
|
-
// // Ping the actor
|
|
91
|
-
// const pingResponse = await fetch(`${RIVET_ENDPOINT}/ping`, {
|
|
92
|
-
// method: "GET",
|
|
93
|
-
// headers: {
|
|
94
|
-
// "x-rivet-target": "actor",
|
|
95
|
-
// "x-rivet-actor": actorId,
|
|
96
|
-
// },
|
|
97
|
-
// });
|
|
98
|
-
// if (!pingResponse.ok) throw "Request failed";
|
|
99
|
-
// });
|
|
100
|
-
//
|
|
101
|
-
// //bench.add(
|
|
102
|
-
// // "wake actor from sleep",
|
|
103
|
-
// // async () => {
|
|
104
|
-
// // if (!wakeActorId) throw "No wake actor ID";
|
|
105
|
-
// //
|
|
106
|
-
// // // Ping the actor
|
|
107
|
-
// // const pingResponse = await fetch(`${RIVET_ENDPOINT}/ping`, {
|
|
108
|
-
// // method: "GET",
|
|
109
|
-
// // headers: {
|
|
110
|
-
// // "x-rivet-target": "actor",
|
|
111
|
-
// // "x-rivet-actor": wakeActorId,
|
|
112
|
-
// // },
|
|
113
|
-
// // });
|
|
114
|
-
// //
|
|
115
|
-
// // if (!pingResponse.ok) {
|
|
116
|
-
// // console.error(
|
|
117
|
-
// // `Ping failed: ${pingResponse.status} ${pingResponse.statusText}`,
|
|
118
|
-
// // );
|
|
119
|
-
// // const errorText = await pingResponse.text();
|
|
120
|
-
// // console.error(`Error response: ${errorText}`);
|
|
121
|
-
// // throw `Request failed: ${pingResponse.status} ${pingResponse.statusText}`;
|
|
122
|
-
// // }
|
|
123
|
-
// // },
|
|
124
|
-
// // {
|
|
125
|
-
// // beforeEach: async () => {
|
|
126
|
-
// // // Reset promise resolvers for this iteration
|
|
127
|
-
// // started = createPromiseResolver<void>();
|
|
128
|
-
// // stopped = createPromiseResolver<void>();
|
|
129
|
-
// //
|
|
130
|
-
// // // Create the actor that will be used for wake benchmarking
|
|
131
|
-
// // console.log('Creating actor');
|
|
132
|
-
// // const wakeActorResponse = await createActor(
|
|
133
|
-
// // namespaceName,
|
|
134
|
-
// // runnerName,
|
|
135
|
-
// // false,
|
|
136
|
-
// // "wake-bench-actor",
|
|
137
|
-
// // );
|
|
138
|
-
// // wakeActorId = wakeActorResponse.actor.actor_id;
|
|
139
|
-
// // createdActors.push(wakeActorId!);
|
|
140
|
-
// //
|
|
141
|
-
// // // Wait for actor to start
|
|
142
|
-
// // await started.promise;
|
|
143
|
-
// //
|
|
144
|
-
// // // Put actor to sleep initially
|
|
145
|
-
// // runner!.sleepActor(wakeActorId!);
|
|
146
|
-
// // await stopped.promise;
|
|
147
|
-
// // },
|
|
148
|
-
// // },
|
|
149
|
-
// // // TODO(RVT-4979): Add back after sleep cycles fixed
|
|
150
|
-
// // //{
|
|
151
|
-
// // // beforeAll: async () => {
|
|
152
|
-
// // // // Create the actor that will be used for wake benchmarking
|
|
153
|
-
// // // console.log("Creating wake actor...");
|
|
154
|
-
// // // const wakeActorResponse = await createActor(
|
|
155
|
-
// // // namespaceName,
|
|
156
|
-
// // // runnerName,
|
|
157
|
-
// // // false,
|
|
158
|
-
// // // "wake-bench-actor",
|
|
159
|
-
// // // );
|
|
160
|
-
// // // wakeActorId = wakeActorResponse.actor.actor_id;
|
|
161
|
-
// // // createdActors.push(wakeActorId!);
|
|
162
|
-
// // //
|
|
163
|
-
// // // // Wait for actor to start
|
|
164
|
-
// // // await started.promise;
|
|
165
|
-
// // // },
|
|
166
|
-
// // // beforeEach: async () => {
|
|
167
|
-
// // // console.log("Putting actor to sleep...");
|
|
168
|
-
// // //
|
|
169
|
-
// // // // Put actor to sleep initially
|
|
170
|
-
// // // stopped = createPromiseResolver<void>();
|
|
171
|
-
// // // runner!.sleepActor(wakeActorId!);
|
|
172
|
-
// // // await stopped.promise;
|
|
173
|
-
// // // },
|
|
174
|
-
// // //},
|
|
175
|
-
// //);
|
|
176
|
-
//
|
|
177
|
-
// // Run the benchmark
|
|
178
|
-
// console.log("Running benchmarks...");
|
|
179
|
-
// await bench.run();
|
|
180
|
-
//
|
|
181
|
-
// // Display results
|
|
182
|
-
// console.table(bench.table());
|
|
183
|
-
//
|
|
184
|
-
// console.log("Benchmark complete!");
|
|
185
|
-
// }
|
|
186
|
-
//
|
|
187
|
-
// // Run the benchmark if this file is executed directly
|
|
188
|
-
// if (import.meta.url === `file://${process.argv[1]}`) {
|
|
189
|
-
// runActorLifecycleBenchmark();
|
|
190
|
-
// }
|
package/benches/utils.ts
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
// import { Runner } from "@/mod";
|
|
2
|
-
// import type { RunnerConfig, ActorConfig } from "@/mod";
|
|
3
|
-
//
|
|
4
|
-
// export const RIVET_ENDPOINT =
|
|
5
|
-
// process.env.RIVET_ENDPOINT ?? "http://localhost:6420";
|
|
6
|
-
//
|
|
7
|
-
// export async function createActor(
|
|
8
|
-
// namespaceName: string,
|
|
9
|
-
// runnerNameSelector: string,
|
|
10
|
-
// durable: boolean,
|
|
11
|
-
// actorName: string = "bench-actor",
|
|
12
|
-
// ): Promise<any> {
|
|
13
|
-
// const response = await fetch(
|
|
14
|
-
// `${RIVET_ENDPOINT}/actors?namespace=${namespaceName}`,
|
|
15
|
-
// {
|
|
16
|
-
// method: "POST",
|
|
17
|
-
// headers: {
|
|
18
|
-
// "Content-Type": "application/json",
|
|
19
|
-
// },
|
|
20
|
-
// body: JSON.stringify({
|
|
21
|
-
// name: actorName,
|
|
22
|
-
// input: btoa("bench-input"),
|
|
23
|
-
// runner_name_selector: runnerNameSelector,
|
|
24
|
-
// durable,
|
|
25
|
-
// }),
|
|
26
|
-
// },
|
|
27
|
-
// );
|
|
28
|
-
//
|
|
29
|
-
// if (!response.ok) {
|
|
30
|
-
// throw new Error(
|
|
31
|
-
// `Failed to create actor: ${response.status} ${response.statusText}\n${await response.text()}`,
|
|
32
|
-
// );
|
|
33
|
-
// }
|
|
34
|
-
//
|
|
35
|
-
// return response.json();
|
|
36
|
-
// }
|
|
37
|
-
//
|
|
38
|
-
// export async function destroyActor(
|
|
39
|
-
// namespaceName: string,
|
|
40
|
-
// actorId: string,
|
|
41
|
-
// ): Promise<void> {
|
|
42
|
-
// const response = await fetch(
|
|
43
|
-
// `${RIVET_ENDPOINT}/actors/${actorId}?namespace=${namespaceName}`,
|
|
44
|
-
// {
|
|
45
|
-
// method: "DELETE",
|
|
46
|
-
// },
|
|
47
|
-
// );
|
|
48
|
-
//
|
|
49
|
-
// if (!response.ok) {
|
|
50
|
-
// throw new Error(
|
|
51
|
-
// `Failed to delete actor: ${response.status} ${response.statusText}\n${await response.text()}`,
|
|
52
|
-
// );
|
|
53
|
-
// }
|
|
54
|
-
// }
|
|
55
|
-
//
|
|
56
|
-
// export async function createNamespace(
|
|
57
|
-
// name: string,
|
|
58
|
-
// displayName: string,
|
|
59
|
-
// ): Promise<any> {
|
|
60
|
-
// const response = await fetch(`${RIVET_ENDPOINT}/namespaces`, {
|
|
61
|
-
// method: "POST",
|
|
62
|
-
// headers: {
|
|
63
|
-
// "Content-Type": "application/json",
|
|
64
|
-
// },
|
|
65
|
-
// body: JSON.stringify({
|
|
66
|
-
// name,
|
|
67
|
-
// display_name: displayName,
|
|
68
|
-
// }),
|
|
69
|
-
// });
|
|
70
|
-
//
|
|
71
|
-
// if (!response.ok) {
|
|
72
|
-
// console.warn(
|
|
73
|
-
// `Failed to create namespace: ${response.status} ${response.statusText}\n${await response.text()}`,
|
|
74
|
-
// );
|
|
75
|
-
// }
|
|
76
|
-
// }
|
|
77
|
-
//
|
|
78
|
-
// export interface BenchmarkRunnerSetup {
|
|
79
|
-
// runner: Runner;
|
|
80
|
-
// namespaceName: string;
|
|
81
|
-
// runnerName: string;
|
|
82
|
-
// }
|
|
83
|
-
//
|
|
84
|
-
// export async function setupBenchmarkRunner(
|
|
85
|
-
// namespaceSuffix: string,
|
|
86
|
-
// port: number,
|
|
87
|
-
// onActorStart?: (
|
|
88
|
-
// actorId: string,
|
|
89
|
-
// generation: number,
|
|
90
|
-
// config: ActorConfig,
|
|
91
|
-
// ) => Promise<void>,
|
|
92
|
-
// onActorStop?: (actorId: string, generation: number) => Promise<void>,
|
|
93
|
-
// ): Promise<BenchmarkRunnerSetup> {
|
|
94
|
-
// const namespaceName = `bench-${crypto.randomUUID().slice(0, 8)}`;
|
|
95
|
-
// const runnerName = `bench-runner`;
|
|
96
|
-
//
|
|
97
|
-
// let runnerStartedResolver: () => void;
|
|
98
|
-
// const runnerStarted = new Promise<void>((resolve) => {
|
|
99
|
-
// runnerStartedResolver = resolve;
|
|
100
|
-
// });
|
|
101
|
-
//
|
|
102
|
-
// const config: RunnerConfig = {
|
|
103
|
-
// version: 1,
|
|
104
|
-
// endpoint: RIVET_ENDPOINT,
|
|
105
|
-
// namespace: namespaceName,
|
|
106
|
-
// addresses: { main: { host: "127.0.0.1", port } },
|
|
107
|
-
// totalSlots: 100,
|
|
108
|
-
// prepopulateActorNames: [],
|
|
109
|
-
// runnerName: runnerName,
|
|
110
|
-
// runnerKey: "default",
|
|
111
|
-
// onConnected: () => {
|
|
112
|
-
// runnerStartedResolver();
|
|
113
|
-
// },
|
|
114
|
-
// onDisconnected: () => {},
|
|
115
|
-
// fetch: async (_actorId: string, request: Request) => {
|
|
116
|
-
// return new Response("ok", { status: 200 });
|
|
117
|
-
// },
|
|
118
|
-
// onActorStart: onActorStart || (async () => {}),
|
|
119
|
-
// onActorStop: onActorStop || (async () => {}),
|
|
120
|
-
// };
|
|
121
|
-
//
|
|
122
|
-
// await createNamespace(namespaceName, `Bench ${namespaceSuffix} Namespace`);
|
|
123
|
-
// const runner = new Runner(config);
|
|
124
|
-
// runner.start();
|
|
125
|
-
// await runnerStarted;
|
|
126
|
-
//
|
|
127
|
-
// return { runner, namespaceName, runnerName };
|
|
128
|
-
// }
|
|
129
|
-
//
|
|
130
|
-
// export function createPromiseResolver<T = void>(): {
|
|
131
|
-
// promise: Promise<T>;
|
|
132
|
-
// resolve: (value: T) => void;
|
|
133
|
-
// reject: (error: any) => void;
|
|
134
|
-
// } {
|
|
135
|
-
// let resolve: (value: T) => void;
|
|
136
|
-
// let reject: (error: any) => void;
|
|
137
|
-
// const promise = new Promise<T>((res, rej) => {
|
|
138
|
-
// resolve = res;
|
|
139
|
-
// reject = rej;
|
|
140
|
-
// });
|
|
141
|
-
// return { promise, resolve: resolve!, reject: reject! };
|
|
142
|
-
// }
|
|
143
|
-
//
|
package/tests/lifecycle.test.ts
DELETED
|
@@ -1,596 +0,0 @@
|
|
|
1
|
-
// import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
// import { Runner } from "@/mod";
|
|
3
|
-
// import type { RunnerConfig, ActorConfig } from "@/mod";
|
|
4
|
-
// import WebSocket, { type CloseEvent } from "ws";
|
|
5
|
-
//
|
|
6
|
-
// const RIVET_ENDPOINT = process.env.RIVET_ENDPOINT ?? "http://localhost:6420";
|
|
7
|
-
// const RIVET_ENDPOINT_WS = RIVET_ENDPOINT.replace("http://", "ws://").replace(
|
|
8
|
-
// "https://",
|
|
9
|
-
// "wss://",
|
|
10
|
-
// );
|
|
11
|
-
//
|
|
12
|
-
// async function createActor(
|
|
13
|
-
// namespaceName: string,
|
|
14
|
-
// runnerNameSelector: string,
|
|
15
|
-
// durable: boolean,
|
|
16
|
-
// name?: string,
|
|
17
|
-
// ): Promise<any> {
|
|
18
|
-
// const response = await fetch(
|
|
19
|
-
// `${RIVET_ENDPOINT}/actors?namespace=${namespaceName}`,
|
|
20
|
-
// {
|
|
21
|
-
// method: "POST",
|
|
22
|
-
// headers: {
|
|
23
|
-
// "Content-Type": "application/json",
|
|
24
|
-
// },
|
|
25
|
-
// body: JSON.stringify({
|
|
26
|
-
// name: name ?? "thingy",
|
|
27
|
-
// input: btoa("hello"),
|
|
28
|
-
// runner_name_selector: runnerNameSelector,
|
|
29
|
-
// durable,
|
|
30
|
-
// }),
|
|
31
|
-
// },
|
|
32
|
-
// );
|
|
33
|
-
//
|
|
34
|
-
// if (!response.ok) {
|
|
35
|
-
// throw new Error(
|
|
36
|
-
// `Failed to create actor: ${response.status} ${response.statusText}\n${await response.text()}`,
|
|
37
|
-
// );
|
|
38
|
-
// }
|
|
39
|
-
//
|
|
40
|
-
// return response.json();
|
|
41
|
-
// }
|
|
42
|
-
//
|
|
43
|
-
// async function destroyActor(
|
|
44
|
-
// namespaceName: string,
|
|
45
|
-
// actorId: string,
|
|
46
|
-
// ): Promise<void> {
|
|
47
|
-
// const response = await fetch(
|
|
48
|
-
// `${RIVET_ENDPOINT}/actors/${actorId}?namespace=${namespaceName}`,
|
|
49
|
-
// {
|
|
50
|
-
// method: "DELETE",
|
|
51
|
-
// },
|
|
52
|
-
// );
|
|
53
|
-
//
|
|
54
|
-
// if (!response.ok) {
|
|
55
|
-
// throw new Error(
|
|
56
|
-
// `Failed to delete actor: ${response.status} ${response.statusText}\n${await response.text()}`,
|
|
57
|
-
// );
|
|
58
|
-
// }
|
|
59
|
-
// }
|
|
60
|
-
//
|
|
61
|
-
// async function createNamespace(
|
|
62
|
-
// name: string,
|
|
63
|
-
// displayName: string,
|
|
64
|
-
// ): Promise<any> {
|
|
65
|
-
// const response = await fetch(`${RIVET_ENDPOINT}/namespaces`, {
|
|
66
|
-
// method: "POST",
|
|
67
|
-
// headers: {
|
|
68
|
-
// "Content-Type": "application/json",
|
|
69
|
-
// },
|
|
70
|
-
// body: JSON.stringify({
|
|
71
|
-
// name,
|
|
72
|
-
// display_name: displayName,
|
|
73
|
-
// }),
|
|
74
|
-
// });
|
|
75
|
-
//
|
|
76
|
-
// if (!response.ok) {
|
|
77
|
-
// console.warn(
|
|
78
|
-
// `Failed to create namespace: ${response.status} ${response.statusText}\n${await response.text()}`,
|
|
79
|
-
// );
|
|
80
|
-
// }
|
|
81
|
-
// }
|
|
82
|
-
//
|
|
83
|
-
// async function getActorNames(namespaceName: string): Promise<any> {
|
|
84
|
-
// const response = await fetch(
|
|
85
|
-
// `${RIVET_ENDPOINT}/actors/names?namespace=${namespaceName}`,
|
|
86
|
-
// {
|
|
87
|
-
// method: "GET",
|
|
88
|
-
// headers: {
|
|
89
|
-
// "Content-Type": "application/json",
|
|
90
|
-
// },
|
|
91
|
-
// },
|
|
92
|
-
// );
|
|
93
|
-
//
|
|
94
|
-
// if (!response.ok) {
|
|
95
|
-
// throw new Error(
|
|
96
|
-
// `Failed to get actor names: ${response.status} ${response.statusText}\n${await response.text()}`,
|
|
97
|
-
// );
|
|
98
|
-
// }
|
|
99
|
-
//
|
|
100
|
-
// return await response.json();
|
|
101
|
-
// }
|
|
102
|
-
//
|
|
103
|
-
// describe("Runner E2E", () => {
|
|
104
|
-
// it("performs end-to-end actor lifecycle", async () => {
|
|
105
|
-
// const namespaceName = `test-${Math.floor(Math.random() * 10000)}`;
|
|
106
|
-
// const runnerName = "test-runner";
|
|
107
|
-
// const prepopulateActorNames: string[] = Array.from(
|
|
108
|
-
// { length: 8 },
|
|
109
|
-
// () =>
|
|
110
|
-
// `actor-${Math.random().toString(36).substring(2, 10)}-${Date.now()}`,
|
|
111
|
-
// );
|
|
112
|
-
// let runnerStarted = Promise.withResolvers();
|
|
113
|
-
// let websocketOpen = Promise.withResolvers();
|
|
114
|
-
// let websocketClosed = Promise.withResolvers();
|
|
115
|
-
// let runner: Runner | null = null;
|
|
116
|
-
// const actorWebSockets = new Map<string, WebSocket>();
|
|
117
|
-
//
|
|
118
|
-
// // Use objects to hold the current promise resolvers so callbacks always get the latest
|
|
119
|
-
// const startedRef = { current: Promise.withResolvers() };
|
|
120
|
-
// const stoppedRef = { current: Promise.withResolvers() };
|
|
121
|
-
//
|
|
122
|
-
// const config: RunnerConfig = {
|
|
123
|
-
// version: 1,
|
|
124
|
-
// endpoint: RIVET_ENDPOINT,
|
|
125
|
-
// namespace: namespaceName,
|
|
126
|
-
// addresses: { main: { host: "127.0.0.1", port: 5051 } },
|
|
127
|
-
// totalSlots: 100,
|
|
128
|
-
// runnerName: runnerName,
|
|
129
|
-
// runnerKey: "default",
|
|
130
|
-
// prepopulateActorNames,
|
|
131
|
-
// onConnected: () => {
|
|
132
|
-
// runnerStarted.resolve(undefined);
|
|
133
|
-
// },
|
|
134
|
-
// onDisconnected: () => { },
|
|
135
|
-
// fetch: async (actorId: string, request: Request) => {
|
|
136
|
-
// const url = new URL(request.url);
|
|
137
|
-
// if (url.pathname === "/ping") {
|
|
138
|
-
// // Return the actor ID in response
|
|
139
|
-
// return new Response(
|
|
140
|
-
// JSON.stringify({
|
|
141
|
-
// actorId,
|
|
142
|
-
// status: "ok",
|
|
143
|
-
// timestamp: Date.now(),
|
|
144
|
-
// }),
|
|
145
|
-
// {
|
|
146
|
-
// status: 200,
|
|
147
|
-
// headers: { "Content-Type": "application/json" },
|
|
148
|
-
// },
|
|
149
|
-
// );
|
|
150
|
-
// }
|
|
151
|
-
// return new Response("ok", { status: 200 });
|
|
152
|
-
// },
|
|
153
|
-
// onActorStart: async (
|
|
154
|
-
// _actorId: string,
|
|
155
|
-
// _generation: number,
|
|
156
|
-
// _config: ActorConfig,
|
|
157
|
-
// ) => {
|
|
158
|
-
// console.log(
|
|
159
|
-
// `Actor ${_actorId} started (generation ${_generation})`,
|
|
160
|
-
// );
|
|
161
|
-
// startedRef.current.resolve(undefined);
|
|
162
|
-
// },
|
|
163
|
-
// onActorStop: async (_actorId: string, _generation: number) => {
|
|
164
|
-
// console.log(
|
|
165
|
-
// `Actor ${_actorId} stopped (generation ${_generation})`,
|
|
166
|
-
// );
|
|
167
|
-
// stoppedRef.current.resolve(undefined);
|
|
168
|
-
// },
|
|
169
|
-
// websocket: async (
|
|
170
|
-
// actorId: string,
|
|
171
|
-
// ws: WebSocket,
|
|
172
|
-
// request: Request,
|
|
173
|
-
// ) => {
|
|
174
|
-
// console.log(`WebSocket connected for actor ${actorId}`);
|
|
175
|
-
// websocketOpen.resolve(undefined);
|
|
176
|
-
// actorWebSockets.set(actorId, ws);
|
|
177
|
-
//
|
|
178
|
-
// // Echo server - send back any messages received
|
|
179
|
-
// ws.on("message", (data) => {
|
|
180
|
-
// console.log(
|
|
181
|
-
// `WebSocket message from actor ${actorId}:`,
|
|
182
|
-
// data.toString(),
|
|
183
|
-
// );
|
|
184
|
-
// ws.send(`Echo: ${data}`);
|
|
185
|
-
// });
|
|
186
|
-
//
|
|
187
|
-
// ws.on("close", () => {
|
|
188
|
-
// console.log(`WebSocket closed for actor ${actorId}`);
|
|
189
|
-
// actorWebSockets.delete(actorId);
|
|
190
|
-
// websocketClosed.resolve(undefined);
|
|
191
|
-
// });
|
|
192
|
-
// },
|
|
193
|
-
// };
|
|
194
|
-
//
|
|
195
|
-
// // Create namespace first
|
|
196
|
-
// await createNamespace(namespaceName, "Test Namespace");
|
|
197
|
-
//
|
|
198
|
-
// runner = new Runner(config);
|
|
199
|
-
//
|
|
200
|
-
// // Check pegboard URL configuration
|
|
201
|
-
// expect(runner.pegboardUrl).toBe(
|
|
202
|
-
// `${RIVET_ENDPOINT_WS}/v1?namespace=${namespaceName}`,
|
|
203
|
-
// );
|
|
204
|
-
//
|
|
205
|
-
// // Start runner
|
|
206
|
-
// runner.start();
|
|
207
|
-
//
|
|
208
|
-
// // Wait for runner to be ready
|
|
209
|
-
// console.log("Waiting runner start...");
|
|
210
|
-
// await runnerStarted.promise;
|
|
211
|
-
//
|
|
212
|
-
// // Check actor names prepopulated
|
|
213
|
-
// console.log("Comparing actor names...");
|
|
214
|
-
// await vi.waitFor(
|
|
215
|
-
// async () => {
|
|
216
|
-
// const { names } = await getActorNames(namespaceName);
|
|
217
|
-
// expect(names.sort()).toStrictEqual(
|
|
218
|
-
// prepopulateActorNames.sort(),
|
|
219
|
-
// );
|
|
220
|
-
// },
|
|
221
|
-
// { interval: 100 },
|
|
222
|
-
// );
|
|
223
|
-
//
|
|
224
|
-
// // Create an actor
|
|
225
|
-
// console.log("Creating actor...");
|
|
226
|
-
// const actorResponse = await createActor(
|
|
227
|
-
// namespaceName,
|
|
228
|
-
// runnerName,
|
|
229
|
-
// false,
|
|
230
|
-
// );
|
|
231
|
-
// console.log("Actor created:", actorResponse.actor);
|
|
232
|
-
// const actorId = actorResponse.actor.actor_id;
|
|
233
|
-
//
|
|
234
|
-
// // Wait for actor to start
|
|
235
|
-
// console.log("Waiting new actor start...");
|
|
236
|
-
// await startedRef.current.promise;
|
|
237
|
-
//
|
|
238
|
-
// // Ping actor to get actor ID in response (via Guard port)
|
|
239
|
-
// console.log("Pinging actor...");
|
|
240
|
-
// const actorPingResponse = await fetch(`${RIVET_ENDPOINT}/ping`, {
|
|
241
|
-
// method: "GET",
|
|
242
|
-
// headers: {
|
|
243
|
-
// "x-rivet-target": "actor",
|
|
244
|
-
// "x-rivet-actor": actorId,
|
|
245
|
-
// },
|
|
246
|
-
// });
|
|
247
|
-
// expect(actorPingResponse.ok).toBe(true);
|
|
248
|
-
// const pingResult = (await actorPingResponse.json()) as any;
|
|
249
|
-
// expect(pingResult.actorId).toBe(actorId);
|
|
250
|
-
//
|
|
251
|
-
// // Test WebSocket connection
|
|
252
|
-
// console.log("Testing WebSocket connection...");
|
|
253
|
-
// const ws = new WebSocket(`${RIVET_ENDPOINT_WS}/ws`, {
|
|
254
|
-
// headers: {
|
|
255
|
-
// "x-rivet-target": "actor",
|
|
256
|
-
// "x-rivet-actor": actorId,
|
|
257
|
-
// },
|
|
258
|
-
// });
|
|
259
|
-
//
|
|
260
|
-
// const testMessage = "Hello, actor!";
|
|
261
|
-
// const messagePromise = new Promise<string>((resolve, reject) => {
|
|
262
|
-
// ws.once("open", () => {
|
|
263
|
-
// console.log("WebSocket connected");
|
|
264
|
-
// ws.send(testMessage);
|
|
265
|
-
// });
|
|
266
|
-
// ws.once("message", (data) => {
|
|
267
|
-
// resolve(data.toString());
|
|
268
|
-
// });
|
|
269
|
-
// ws.once("error", reject);
|
|
270
|
-
// });
|
|
271
|
-
//
|
|
272
|
-
// await websocketOpen.promise;
|
|
273
|
-
//
|
|
274
|
-
// // Test WebSocket messaging
|
|
275
|
-
// console.log("Testing WebSocket messaging...");
|
|
276
|
-
// const response = await messagePromise;
|
|
277
|
-
// expect(response).toBe(`Echo: ${testMessage}`);
|
|
278
|
-
//
|
|
279
|
-
// // Close WebSocket for now
|
|
280
|
-
// ws.close();
|
|
281
|
-
// console.log("Waiting websocket close...");
|
|
282
|
-
// await websocketClosed.promise;
|
|
283
|
-
//
|
|
284
|
-
// await testKv(runner, actorId);
|
|
285
|
-
//
|
|
286
|
-
// // Sleep and wake actor 3 times in a loop
|
|
287
|
-
// for (let i = 1; i <= 3; i++) {
|
|
288
|
-
// console.log(`Sleep/wake cycle ${i}/3`);
|
|
289
|
-
//
|
|
290
|
-
// // Sleep actor
|
|
291
|
-
// console.log(`Sleeping actor (cycle ${i})...`);
|
|
292
|
-
// stoppedRef.current = Promise.withResolvers();
|
|
293
|
-
// runner.sleepActor(actorId);
|
|
294
|
-
//
|
|
295
|
-
// console.log("Waiting actor sleep...");
|
|
296
|
-
// await stoppedRef.current.promise;
|
|
297
|
-
//
|
|
298
|
-
// // Make network request to wake actor (via Guard)
|
|
299
|
-
// console.log(`Waking actor (cycle ${i})...`);
|
|
300
|
-
// startedRef.current = Promise.withResolvers();
|
|
301
|
-
// const wakeResponse = await fetch(`${RIVET_ENDPOINT}/wake`, {
|
|
302
|
-
// method: "GET",
|
|
303
|
-
// headers: {
|
|
304
|
-
// "x-rivet-target": "actor",
|
|
305
|
-
// "x-rivet-actor": actorId,
|
|
306
|
-
// },
|
|
307
|
-
// });
|
|
308
|
-
// console.log(`Wake response status: ${wakeResponse.status}`);
|
|
309
|
-
// console.log(`Wake response body: ${await wakeResponse.text()}`);
|
|
310
|
-
// expect(wakeResponse.status).toBe(200);
|
|
311
|
-
//
|
|
312
|
-
// // TODO: Remove this
|
|
313
|
-
// // Wait for actor to wake
|
|
314
|
-
// console.log("Waiting actor start...");
|
|
315
|
-
// await startedRef.current.promise;
|
|
316
|
-
// console.log(`Actor started successfully for cycle ${i}`);
|
|
317
|
-
//
|
|
318
|
-
// await testKvAfterSleep(runner, actorId);
|
|
319
|
-
// }
|
|
320
|
-
//
|
|
321
|
-
// // Sleep and wake actor 3 times in a loop
|
|
322
|
-
// for (let i = 1; i <= 3; i++) {
|
|
323
|
-
// console.log(`Sleep/wake cycle ${i}/3`);
|
|
324
|
-
//
|
|
325
|
-
// // Sleep actor
|
|
326
|
-
// console.log(`Sleeping actor (cycle ${i})...`);
|
|
327
|
-
// stoppedRef.current = Promise.withResolvers();
|
|
328
|
-
// runner.sleepActor(actorId);
|
|
329
|
-
//
|
|
330
|
-
// console.log("Waiting actor sleep...");
|
|
331
|
-
// await stoppedRef.current.promise;
|
|
332
|
-
//
|
|
333
|
-
// // Open websocket to wake actor (via Guard)
|
|
334
|
-
// console.log(`Waking actor (cycle ${i})...`);
|
|
335
|
-
// startedRef.current = Promise.withResolvers();
|
|
336
|
-
// const ws = new WebSocket(`${RIVET_ENDPOINT_WS}/ws`, {
|
|
337
|
-
// headers: {
|
|
338
|
-
// "x-rivet-target": "actor",
|
|
339
|
-
// "x-rivet-actor": actorId,
|
|
340
|
-
// },
|
|
341
|
-
// });
|
|
342
|
-
//
|
|
343
|
-
// await new Promise<void>((resolve, reject) => {
|
|
344
|
-
// ws.on("open", () => {
|
|
345
|
-
// console.log("WebSocket connected for wake test");
|
|
346
|
-
// resolve();
|
|
347
|
-
// });
|
|
348
|
-
// ws.on("error", reject);
|
|
349
|
-
// });
|
|
350
|
-
//
|
|
351
|
-
// // TODO: Remove this
|
|
352
|
-
// // Wait for actor to wake
|
|
353
|
-
// console.log("Waiting actor start...");
|
|
354
|
-
// await startedRef.current.promise;
|
|
355
|
-
// console.log(`Actor started successfully for cycle ${i}`);
|
|
356
|
-
//
|
|
357
|
-
// await testKvAfterSleep(runner, actorId);
|
|
358
|
-
// }
|
|
359
|
-
//
|
|
360
|
-
// // Create a fresh WebSocket connection for destroy testing
|
|
361
|
-
// console.log("Creating WebSocket for destroy test...");
|
|
362
|
-
// const wsForDestroy = new WebSocket(`${RIVET_ENDPOINT_WS}/ws`, {
|
|
363
|
-
// headers: {
|
|
364
|
-
// "x-rivet-target": "actor",
|
|
365
|
-
// "x-rivet-actor": actorId,
|
|
366
|
-
// },
|
|
367
|
-
// });
|
|
368
|
-
//
|
|
369
|
-
// await new Promise<void>((resolve, reject) => {
|
|
370
|
-
// wsForDestroy.on("open", () => {
|
|
371
|
-
// console.log("WebSocket connected for destroy test");
|
|
372
|
-
// resolve();
|
|
373
|
-
// });
|
|
374
|
-
// wsForDestroy.on("error", reject);
|
|
375
|
-
// });
|
|
376
|
-
//
|
|
377
|
-
// // Test WebSocket closes on actor destroy
|
|
378
|
-
// const wsClosePromise = new Promise<void>((resolve) => {
|
|
379
|
-
// wsForDestroy.on("close", () => {
|
|
380
|
-
// console.log("WebSocket closed after actor destroy");
|
|
381
|
-
// resolve();
|
|
382
|
-
// });
|
|
383
|
-
// });
|
|
384
|
-
//
|
|
385
|
-
// // Destroy actor
|
|
386
|
-
// console.log("Destroying actor...");
|
|
387
|
-
// stoppedRef.current = Promise.withResolvers(); // Create new promise for actor destroy
|
|
388
|
-
//
|
|
389
|
-
// // Start destroy and wait for WebSocket close simultaneously
|
|
390
|
-
// const destroyPromise = destroyActor(namespaceName, actorId);
|
|
391
|
-
//
|
|
392
|
-
// // Wait for WebSocket to close
|
|
393
|
-
// console.log("Waiting WS close...");
|
|
394
|
-
// await wsClosePromise;
|
|
395
|
-
//
|
|
396
|
-
// // Ensure destroy API call completed
|
|
397
|
-
// await destroyPromise;
|
|
398
|
-
// console.log("Destroy API call completed");
|
|
399
|
-
//
|
|
400
|
-
// // Wait for actor to stop with timeout
|
|
401
|
-
// console.log("Waiting actor stopped...");
|
|
402
|
-
// await stoppedRef.current.promise;
|
|
403
|
-
// console.log("Actor stop callback completed");
|
|
404
|
-
//
|
|
405
|
-
// // Validate actor is destroyed
|
|
406
|
-
// console.log("Validating actor is destroyed...");
|
|
407
|
-
// const destroyedPingResponse = await fetch(`${RIVET_ENDPOINT}/ping`, {
|
|
408
|
-
// headers: {
|
|
409
|
-
// "x-rivet-target": "actor",
|
|
410
|
-
// "x-rivet-actor": actorId,
|
|
411
|
-
// },
|
|
412
|
-
// });
|
|
413
|
-
// expect(destroyedPingResponse.status).toBe(404);
|
|
414
|
-
//
|
|
415
|
-
// // Test WebSocket connection to destroyed actor fails
|
|
416
|
-
// console.log("Testing WebSocket to destroyed actor...");
|
|
417
|
-
// const wsToDestroyed = new WebSocket(`${RIVET_ENDPOINT_WS}/ws`, {
|
|
418
|
-
// headers: {
|
|
419
|
-
// "x-rivet-target": "actor",
|
|
420
|
-
// "x-rivet-actor": actorId,
|
|
421
|
-
// },
|
|
422
|
-
// });
|
|
423
|
-
//
|
|
424
|
-
// console.log(
|
|
425
|
-
// "Waiting WS close...",
|
|
426
|
-
// );
|
|
427
|
-
// const closeCode = await new Promise<number>((resolve, reject) => {
|
|
428
|
-
// wsToDestroyed.on("error", (err) => {
|
|
429
|
-
// console.log("WebSocket should not have errored");
|
|
430
|
-
// reject(err);
|
|
431
|
-
// });
|
|
432
|
-
// wsToDestroyed.on("close", (code) => {
|
|
433
|
-
// console.log("WebSocket closed");
|
|
434
|
-
// resolve(code);
|
|
435
|
-
// });
|
|
436
|
-
// });
|
|
437
|
-
// expect(closeCode).toBe(1011);
|
|
438
|
-
//
|
|
439
|
-
// console.log("E2E test completed successfully!");
|
|
440
|
-
//
|
|
441
|
-
// // Clean up - stop the runner
|
|
442
|
-
// if (runner) {
|
|
443
|
-
// await runner.shutdown(false);
|
|
444
|
-
// }
|
|
445
|
-
// }, 30_000);
|
|
446
|
-
// });
|
|
447
|
-
//
|
|
448
|
-
// async function testKv(runner: Runner, actorId: string) {
|
|
449
|
-
// // Test KV operations
|
|
450
|
-
// console.log("Testing KV operations...");
|
|
451
|
-
//
|
|
452
|
-
// // Test kvPut and kvGet
|
|
453
|
-
// const testEntries: [Uint8Array, Uint8Array][] = [
|
|
454
|
-
// [createTestKey(["user", "123"]), createTestValue("alice")],
|
|
455
|
-
// [createTestKey(["user", "456"]), createTestValue("bob")],
|
|
456
|
-
// [createTestKey(["config", "theme"]), createTestValue("dark")],
|
|
457
|
-
// [createTestKey(["config", "lang"]), createTestValue("en")],
|
|
458
|
-
// ];
|
|
459
|
-
//
|
|
460
|
-
// console.log("Testing kvPut...");
|
|
461
|
-
// await runner.kvPut(actorId, testEntries);
|
|
462
|
-
//
|
|
463
|
-
// console.log("Testing kvGet...");
|
|
464
|
-
// const getKeys = testEntries.map(([key, _]) => key);
|
|
465
|
-
// const getResult = await runner.kvGet(actorId, getKeys);
|
|
466
|
-
//
|
|
467
|
-
// expect(getResult.length).toBe(4);
|
|
468
|
-
// expect(decodeValue(getResult[0]!)).toBe("alice");
|
|
469
|
-
// expect(decodeValue(getResult[1]!)).toBe("bob");
|
|
470
|
-
// expect(decodeValue(getResult[2]!)).toBe("dark");
|
|
471
|
-
// expect(decodeValue(getResult[3]!)).toBe("en");
|
|
472
|
-
//
|
|
473
|
-
// // Test getting non-existent key
|
|
474
|
-
// const nonExistentResult = await runner.kvGet(actorId, [
|
|
475
|
-
// createTestKey(["nonexistent"]),
|
|
476
|
-
// ]);
|
|
477
|
-
// expect(nonExistentResult[0]).toBe(null);
|
|
478
|
-
//
|
|
479
|
-
// // Test kvListAll
|
|
480
|
-
// console.log("Testing kvListAll...");
|
|
481
|
-
// const allEntries = await runner.kvListAll(actorId);
|
|
482
|
-
// expect(allEntries.length).toBe(4);
|
|
483
|
-
//
|
|
484
|
-
// // Verify all entries are present (order may vary)
|
|
485
|
-
// const allValues = allEntries.map(([_, value]) => decodeValue(value));
|
|
486
|
-
// expect(allValues.sort()).toEqual(["alice", "bob", "dark", "en"]);
|
|
487
|
-
//
|
|
488
|
-
// // Test kvListAll with limit
|
|
489
|
-
// const limitedEntries = await runner.kvListAll(actorId, { limit: 2 });
|
|
490
|
-
// expect(limitedEntries.length).toBe(2);
|
|
491
|
-
//
|
|
492
|
-
// // Test kvListPrefix
|
|
493
|
-
// console.log("Testing kvListPrefix...");
|
|
494
|
-
// const userEntries = await runner.kvListPrefix(
|
|
495
|
-
// actorId,
|
|
496
|
-
// createTestKey(["user"]),
|
|
497
|
-
// );
|
|
498
|
-
// // Note: Prefix queries may not be working as expected on the server side
|
|
499
|
-
// // For now, we'll test that the method executes without error
|
|
500
|
-
// expect(userEntries.length).toBeGreaterThanOrEqual(0);
|
|
501
|
-
//
|
|
502
|
-
// const configEntries = await runner.kvListPrefix(
|
|
503
|
-
// actorId,
|
|
504
|
-
// createTestKey(["config"]),
|
|
505
|
-
// );
|
|
506
|
-
// expect(configEntries.length).toBeGreaterThanOrEqual(0);
|
|
507
|
-
//
|
|
508
|
-
// // Test kvListRange
|
|
509
|
-
// console.log("Testing kvListRange...");
|
|
510
|
-
// const rangeEntries = await runner.kvListRange(
|
|
511
|
-
// actorId,
|
|
512
|
-
// createTestKey(["config"]),
|
|
513
|
-
// createTestKey(["user"]),
|
|
514
|
-
// false, // inclusive
|
|
515
|
-
// );
|
|
516
|
-
// // Range queries may have varying behavior depending on key ordering
|
|
517
|
-
// expect(rangeEntries.length).toBeGreaterThanOrEqual(0);
|
|
518
|
-
//
|
|
519
|
-
// // Test kvDelete
|
|
520
|
-
// console.log("Testing kvDelete...");
|
|
521
|
-
// const keysToDelete = [createTestKey(["user", "456"])]; // Delete bob
|
|
522
|
-
// await runner.kvDelete(actorId, keysToDelete);
|
|
523
|
-
//
|
|
524
|
-
// // Verify deletion worked
|
|
525
|
-
// const afterDeleteResult = await runner.kvGet(actorId, [
|
|
526
|
-
// createTestKey(["user", "456"]),
|
|
527
|
-
// ]);
|
|
528
|
-
// expect(afterDeleteResult[0]).toBe(null);
|
|
529
|
-
//
|
|
530
|
-
// // Verify other data still exists
|
|
531
|
-
// const remainingUserResult = await runner.kvGet(actorId, [
|
|
532
|
-
// createTestKey(["user", "123"]),
|
|
533
|
-
// ]);
|
|
534
|
-
// expect(decodeValue(remainingUserResult[0]!)).toBe("alice");
|
|
535
|
-
//
|
|
536
|
-
// // Test kvDrop operation before destroy
|
|
537
|
-
// console.log("Testing kvDrop...");
|
|
538
|
-
// await runner.kvDrop(actorId);
|
|
539
|
-
//
|
|
540
|
-
// // Verify all data is cleared
|
|
541
|
-
// const afterDropData = await runner.kvGet(actorId, [
|
|
542
|
-
// createTestKey(["user", "123"]),
|
|
543
|
-
// createTestKey(["config", "theme"]),
|
|
544
|
-
// createTestKey(["config", "lang"]),
|
|
545
|
-
// ]);
|
|
546
|
-
// expect(afterDropData[0]).toBe(null);
|
|
547
|
-
// expect(afterDropData[1]).toBe(null);
|
|
548
|
-
// expect(afterDropData[2]).toBe(null);
|
|
549
|
-
//
|
|
550
|
-
// // Verify list operations return empty after drop
|
|
551
|
-
// const afterDropList = await runner.kvListAll(actorId);
|
|
552
|
-
// expect(afterDropList.length).toBe(0);
|
|
553
|
-
//
|
|
554
|
-
// // Write data to test it exists during a sleep
|
|
555
|
-
// console.log("Writing data to live during sleep...");
|
|
556
|
-
// await runner.kvPut(actorId, [
|
|
557
|
-
// [createTestKey(["user", "789"]), createTestValue("max")],
|
|
558
|
-
// ]);
|
|
559
|
-
// }
|
|
560
|
-
//
|
|
561
|
-
// async function testKvAfterSleep(runner: Runner, actorId: string) {
|
|
562
|
-
// // Verify data still exists after waking again
|
|
563
|
-
// const remainingUserResult = await runner.kvGet(actorId, [
|
|
564
|
-
// createTestKey(["user", "789"]),
|
|
565
|
-
// ]);
|
|
566
|
-
// expect(decodeValue(remainingUserResult[0]!)).toBe("max");
|
|
567
|
-
// }
|
|
568
|
-
//
|
|
569
|
-
// function createTestKey(segments: string[]): Uint8Array {
|
|
570
|
-
// return flattenUint8Arrays(segments.map((s) => new TextEncoder().encode(s)));
|
|
571
|
-
// }
|
|
572
|
-
//
|
|
573
|
-
// function createTestValue(value: string): Uint8Array {
|
|
574
|
-
// return new TextEncoder().encode(value);
|
|
575
|
-
// }
|
|
576
|
-
//
|
|
577
|
-
// function decodeValue(value: Uint8Array): string {
|
|
578
|
-
// return new TextDecoder().decode(value);
|
|
579
|
-
// }
|
|
580
|
-
//
|
|
581
|
-
// function flattenUint8Arrays(arrays: Uint8Array[]): Uint8Array {
|
|
582
|
-
// // Calculate total length
|
|
583
|
-
// const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
|
|
584
|
-
//
|
|
585
|
-
// // Create result array
|
|
586
|
-
// const result = new Uint8Array(totalLength);
|
|
587
|
-
//
|
|
588
|
-
// // Copy each array
|
|
589
|
-
// let offset = 0;
|
|
590
|
-
// for (const arr of arrays) {
|
|
591
|
-
// result.set(arr, offset);
|
|
592
|
-
// offset += arr.length;
|
|
593
|
-
// }
|
|
594
|
-
//
|
|
595
|
-
// return result;
|
|
596
|
-
// }
|
package/tests/utils.test.ts
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
wrappingGteU16,
|
|
4
|
-
wrappingGtU16,
|
|
5
|
-
wrappingLteU16,
|
|
6
|
-
wrappingLtU16,
|
|
7
|
-
} from "../src/utils";
|
|
8
|
-
|
|
9
|
-
describe("wrappingGtU16", () => {
|
|
10
|
-
it("should return true when a > b in normal case", () => {
|
|
11
|
-
expect(wrappingGtU16(100, 50)).toBe(true);
|
|
12
|
-
expect(wrappingGtU16(1000, 999)).toBe(true);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("should return false when a < b in normal case", () => {
|
|
16
|
-
expect(wrappingGtU16(50, 100)).toBe(false);
|
|
17
|
-
expect(wrappingGtU16(999, 1000)).toBe(false);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("should return false when a == b", () => {
|
|
21
|
-
expect(wrappingGtU16(100, 100)).toBe(false);
|
|
22
|
-
expect(wrappingGtU16(0, 0)).toBe(false);
|
|
23
|
-
expect(wrappingGtU16(65535, 65535)).toBe(false);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("should handle wrapping around u16 max", () => {
|
|
27
|
-
// When values wrap around, 1 is "greater than" 65535
|
|
28
|
-
expect(wrappingGtU16(1, 65535)).toBe(true);
|
|
29
|
-
expect(wrappingGtU16(100, 65500)).toBe(true);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("should handle edge cases near u16 boundaries", () => {
|
|
33
|
-
// 65535 is not greater than 0 (wrapped)
|
|
34
|
-
expect(wrappingGtU16(65535, 0)).toBe(false);
|
|
35
|
-
// But 0 is greater than 65535 if we consider wrapping
|
|
36
|
-
expect(wrappingGtU16(0, 65535)).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("should handle values at exactly half the range", () => {
|
|
40
|
-
// U16_MAX / 2 = 32767.5, so values with distance <= 32767 return true
|
|
41
|
-
const lessThanHalf = 32766;
|
|
42
|
-
expect(wrappingGtU16(lessThanHalf, 0)).toBe(true);
|
|
43
|
-
expect(wrappingGtU16(0, lessThanHalf)).toBe(false);
|
|
44
|
-
|
|
45
|
-
// At distance 32767, still less than 32767.5, so comparison returns true
|
|
46
|
-
const atHalfDistance = 32767;
|
|
47
|
-
expect(wrappingGtU16(atHalfDistance, 0)).toBe(true);
|
|
48
|
-
expect(wrappingGtU16(0, atHalfDistance)).toBe(false);
|
|
49
|
-
|
|
50
|
-
// At distance 32768, greater than 32767.5, so comparison returns false
|
|
51
|
-
const overHalfDistance = 32768;
|
|
52
|
-
expect(wrappingGtU16(overHalfDistance, 0)).toBe(false);
|
|
53
|
-
expect(wrappingGtU16(0, overHalfDistance)).toBe(false);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe("wrappingLtU16", () => {
|
|
58
|
-
it("should return true when a < b in normal case", () => {
|
|
59
|
-
expect(wrappingLtU16(50, 100)).toBe(true);
|
|
60
|
-
expect(wrappingLtU16(999, 1000)).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("should return false when a > b in normal case", () => {
|
|
64
|
-
expect(wrappingLtU16(100, 50)).toBe(false);
|
|
65
|
-
expect(wrappingLtU16(1000, 999)).toBe(false);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("should return false when a == b", () => {
|
|
69
|
-
expect(wrappingLtU16(100, 100)).toBe(false);
|
|
70
|
-
expect(wrappingLtU16(0, 0)).toBe(false);
|
|
71
|
-
expect(wrappingLtU16(65535, 65535)).toBe(false);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("should handle wrapping around u16 max", () => {
|
|
75
|
-
// When values wrap around, 65535 is "less than" 1
|
|
76
|
-
expect(wrappingLtU16(65535, 1)).toBe(true);
|
|
77
|
-
expect(wrappingLtU16(65500, 100)).toBe(true);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("should handle edge cases near u16 boundaries", () => {
|
|
81
|
-
// 0 is not less than 65535 (wrapped)
|
|
82
|
-
expect(wrappingLtU16(0, 65535)).toBe(false);
|
|
83
|
-
// But 65535 is less than 0 if we consider wrapping
|
|
84
|
-
expect(wrappingLtU16(65535, 0)).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("should handle values at exactly half the range", () => {
|
|
88
|
-
// U16_MAX / 2 = 32767.5, so values with distance <= 32767 return true
|
|
89
|
-
const lessThanHalf = 32766;
|
|
90
|
-
expect(wrappingLtU16(0, lessThanHalf)).toBe(true);
|
|
91
|
-
expect(wrappingLtU16(lessThanHalf, 0)).toBe(false);
|
|
92
|
-
|
|
93
|
-
// At distance 32767, still less than 32767.5, so comparison returns true
|
|
94
|
-
const atHalfDistance = 32767;
|
|
95
|
-
expect(wrappingLtU16(0, atHalfDistance)).toBe(true);
|
|
96
|
-
expect(wrappingLtU16(atHalfDistance, 0)).toBe(false);
|
|
97
|
-
|
|
98
|
-
// At distance 32768, greater than 32767.5, so comparison returns false
|
|
99
|
-
const overHalfDistance = 32768;
|
|
100
|
-
expect(wrappingLtU16(0, overHalfDistance)).toBe(false);
|
|
101
|
-
expect(wrappingLtU16(overHalfDistance, 0)).toBe(false);
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
describe("wrappingGtU16 and wrappingLtU16 consistency", () => {
|
|
106
|
-
it("should be inverse of each other for different values", () => {
|
|
107
|
-
const testCases: [number, number][] = [
|
|
108
|
-
[100, 200],
|
|
109
|
-
[200, 100],
|
|
110
|
-
[0, 65535],
|
|
111
|
-
[65535, 0],
|
|
112
|
-
[1, 65534],
|
|
113
|
-
[32767, 32768],
|
|
114
|
-
];
|
|
115
|
-
|
|
116
|
-
for (const [a, b] of testCases) {
|
|
117
|
-
const gt = wrappingGtU16(a, b);
|
|
118
|
-
const lt = wrappingLtU16(a, b);
|
|
119
|
-
const eq = a === b;
|
|
120
|
-
|
|
121
|
-
// For any pair, exactly one of gt, lt, or eq should be true
|
|
122
|
-
expect(Number(gt) + Number(lt) + Number(eq)).toBe(1);
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it("should satisfy transitivity for sequential values", () => {
|
|
127
|
-
// If we have sequential indices, a < b < c should hold
|
|
128
|
-
const a = 100;
|
|
129
|
-
const b = 101;
|
|
130
|
-
const c = 102;
|
|
131
|
-
|
|
132
|
-
expect(wrappingLtU16(a, b)).toBe(true);
|
|
133
|
-
expect(wrappingLtU16(b, c)).toBe(true);
|
|
134
|
-
expect(wrappingLtU16(a, c)).toBe(true);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it("should handle sequence across wrap boundary", () => {
|
|
138
|
-
// Test a sequence that wraps: 65534, 65535, 0, 1
|
|
139
|
-
const values = [65534, 65535, 0, 1];
|
|
140
|
-
|
|
141
|
-
for (let i = 0; i < values.length - 1; i++) {
|
|
142
|
-
expect(wrappingLtU16(values[i], values[i + 1])).toBe(true);
|
|
143
|
-
expect(wrappingGtU16(values[i + 1], values[i])).toBe(true);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
describe("wrappingGteU16", () => {
|
|
149
|
-
it("should return true when a > b", () => {
|
|
150
|
-
expect(wrappingGteU16(100, 50)).toBe(true);
|
|
151
|
-
expect(wrappingGteU16(1000, 999)).toBe(true);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it("should return true when a == b", () => {
|
|
155
|
-
expect(wrappingGteU16(100, 100)).toBe(true);
|
|
156
|
-
expect(wrappingGteU16(0, 0)).toBe(true);
|
|
157
|
-
expect(wrappingGteU16(65535, 65535)).toBe(true);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it("should return false when a < b", () => {
|
|
161
|
-
expect(wrappingGteU16(50, 100)).toBe(false);
|
|
162
|
-
expect(wrappingGteU16(999, 1000)).toBe(false);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it("should handle wrapping around u16 max", () => {
|
|
166
|
-
expect(wrappingGteU16(1, 65535)).toBe(true);
|
|
167
|
-
expect(wrappingGteU16(100, 65500)).toBe(true);
|
|
168
|
-
expect(wrappingGteU16(0, 65535)).toBe(true);
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
describe("wrappingLteU16", () => {
|
|
173
|
-
it("should return true when a < b", () => {
|
|
174
|
-
expect(wrappingLteU16(50, 100)).toBe(true);
|
|
175
|
-
expect(wrappingLteU16(999, 1000)).toBe(true);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it("should return true when a == b", () => {
|
|
179
|
-
expect(wrappingLteU16(100, 100)).toBe(true);
|
|
180
|
-
expect(wrappingLteU16(0, 0)).toBe(true);
|
|
181
|
-
expect(wrappingLteU16(65535, 65535)).toBe(true);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it("should return false when a > b", () => {
|
|
185
|
-
expect(wrappingLteU16(100, 50)).toBe(false);
|
|
186
|
-
expect(wrappingLteU16(1000, 999)).toBe(false);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it("should handle wrapping around u16 max", () => {
|
|
190
|
-
expect(wrappingLteU16(65535, 1)).toBe(true);
|
|
191
|
-
expect(wrappingLteU16(65500, 100)).toBe(true);
|
|
192
|
-
expect(wrappingLteU16(65535, 0)).toBe(true);
|
|
193
|
-
});
|
|
194
|
-
});
|
package/tsconfig.json
DELETED
package/tsup.config.ts
DELETED
package/turbo.json
DELETED
package/vitest.config.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
import { defineConfig } from "vitest/config";
|
|
3
|
-
import defaultConfig from "../../../../vitest.base.ts";
|
|
4
|
-
|
|
5
|
-
export default defineConfig({
|
|
6
|
-
...defaultConfig,
|
|
7
|
-
resolve: {
|
|
8
|
-
alias: {
|
|
9
|
-
"@": resolve(__dirname, "./src"),
|
|
10
|
-
},
|
|
11
|
-
},
|
|
12
|
-
test: {
|
|
13
|
-
...defaultConfig.test,
|
|
14
|
-
include: ["tests/**/*.test.ts"],
|
|
15
|
-
},
|
|
16
|
-
});
|