Skip to content

Commit df908b5

Browse files
committed
feat: add stateless get/create/getWithId
1 parent 7846c6e commit df908b5

File tree

23 files changed

+869
-206
lines changed

23 files changed

+869
-206
lines changed

packages/actor-core/src/actor/action.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ export class ActionContext<S, CP, CS, V> {
5757
return this.#actorContext.log;
5858
}
5959

60+
/**
61+
* Gets actor ID.
62+
*/
63+
get actorId(): string {
64+
return this.#actorContext.actorId;
65+
}
66+
6067
/**
6168
* Gets the actor name.
6269
*/

packages/actor-core/src/actor/connection.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { CachedSerializer } from "./protocol/serde";
55
import type { ConnDriver } from "./driver";
66
import * as messageToClient from "@/actor/protocol/message/to-client";
77
import type { PersistedConn } from "./persisted";
8+
import * as wsToClient from "@/actor/protocol/message/to-client";
89

910
export function generateConnId(): string {
1011
return crypto.randomUUID();
@@ -135,7 +136,7 @@ export class Conn<S, CP, CS, V> {
135136
*/
136137
public send(eventName: string, ...args: unknown[]) {
137138
this._sendMessage(
138-
new CachedSerializer({
139+
new CachedSerializer<wsToClient.ToClient>({
139140
b: {
140141
ev: {
141142
n: eventName,

packages/actor-core/src/actor/context.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { Conn, ConnId } from "./connection";
55
import { ActorKey } from "@/common/utils";
66
import { Schedule } from "./schedule";
77

8-
98
/**
109
* ActorContext class that provides access to actor methods and state
1110
*/
@@ -36,7 +35,6 @@ export class ActorContext<S, CP, CS, V> {
3635
* @param args - The arguments to send with the event.
3736
*/
3837
broadcast<Args extends Array<unknown>>(name: string, ...args: Args): void {
39-
// @ts-ignore - Access protected method
4038
this.#actor._broadcast(name, ...args);
4139
return;
4240
}
@@ -45,47 +43,48 @@ export class ActorContext<S, CP, CS, V> {
4543
* Gets the logger instance.
4644
*/
4745
get log(): Logger {
48-
// @ts-ignore - Access protected method
4946
return this.#actor.log;
5047
}
5148

49+
/**
50+
* Gets actor ID.
51+
*/
52+
get actorId(): string {
53+
return this.#actor.id;
54+
}
55+
5256
/**
5357
* Gets the actor name.
5458
*/
5559
get name(): string {
56-
// @ts-ignore - Access protected method
5760
return this.#actor.name;
5861
}
5962

6063
/**
6164
* Gets the actor key.
6265
*/
6366
get key(): ActorKey {
64-
// @ts-ignore - Access protected method
6567
return this.#actor.key;
6668
}
6769

6870
/**
6971
* Gets the region.
7072
*/
7173
get region(): string {
72-
// @ts-ignore - Access protected method
7374
return this.#actor.region;
7475
}
7576

7677
/**
7778
* Gets the scheduler.
7879
*/
7980
get schedule(): Schedule {
80-
// @ts-ignore - Access protected method
8181
return this.#actor.schedule;
8282
}
8383

8484
/**
8585
* Gets the map of connections.
8686
*/
8787
get conns(): Map<ConnId, Conn<S, CP, CS, V>> {
88-
// @ts-ignore - Access protected method
8988
return this.#actor.conns;
9089
}
9190

@@ -95,7 +94,6 @@ export class ActorContext<S, CP, CS, V> {
9594
* @param opts - Options for saving the state.
9695
*/
9796
async saveState(opts: SaveStateOptions): Promise<void> {
98-
// @ts-ignore - Access protected method
9997
return this.#actor.saveState(opts);
10098
}
10199

@@ -105,7 +103,6 @@ export class ActorContext<S, CP, CS, V> {
105103
* @param promise - The promise to run in the background.
106104
*/
107105
runInBackground(promise: Promise<void>): void {
108-
// @ts-ignore - Access protected method
109106
this.#actor._runInBackground(promise);
110107
return;
111108
}

packages/actor-core/src/actor/instance.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { instanceLogger, logger } from "./log";
1515
import type { ActionContext } from "./action";
1616
import { DeadlineError, Lock, deadline } from "./utils";
1717
import { Schedule } from "./schedule";
18+
import * as wsToClient from "@/actor/protocol/message/to-client";
1819
import type * as wsToServer from "@/actor/protocol/message/to-server";
1920
import { CachedSerializer } from "./protocol/serde";
2021
import { ActorInspector } from "@/inspector/actor";
@@ -716,7 +717,7 @@ export class ActorInstance<S, CP, CS, V> {
716717

717718
// Send init message
718719
conn._sendMessage(
719-
new CachedSerializer({
720+
new CachedSerializer<wsToClient.ToClient>({
720721
b: {
721722
i: {
722723
ci: `${conn.id}`,
@@ -1019,7 +1020,7 @@ export class ActorInstance<S, CP, CS, V> {
10191020
const subscriptions = this.#subscriptionIndex.get(name);
10201021
if (!subscriptions) return;
10211022

1022-
const toClientSerializer = new CachedSerializer({
1023+
const toClientSerializer = new CachedSerializer<wsToClient.ToClient>({
10231024
b: {
10241025
ev: {
10251026
n: name,
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { z } from "zod";
2+
3+
export const ResponseErrorSchema = z.object({
4+
// Code
5+
c: z.string(),
6+
// Message
7+
m: z.string(),
8+
// Metadata
9+
md: z.unknown().optional(),
10+
});
11+
12+
export type ResponseError = z.infer<typeof ResponseErrorSchema>;
Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,15 @@
11
import { z } from "zod";
22

3-
export const RequestSchema = z.object({
3+
export const RpcRequestSchema = z.object({
44
// Args
55
a: z.array(z.unknown()),
66
});
77

8-
export const ResponseOkSchema = z.object({
8+
export const RpcResponseSchema = z.object({
99
// Output
1010
o: z.unknown(),
1111
});
1212

13-
export const ResponseErrSchema = z.object({
14-
// Code
15-
c: z.string(),
16-
// Message
17-
m: z.string(),
18-
// Metadata
19-
md: z.unknown().optional(),
20-
});
2113

22-
export type Request = z.infer<typeof RequestSchema>;
23-
export type ResponseOk = z.infer<typeof ResponseOkSchema>;
24-
export type ResponseErr = z.infer<typeof ResponseErrSchema>;
14+
export type RpcRequest = z.infer<typeof RpcRequestSchema>;
15+
export type RpcResponse = z.infer<typeof RpcResponseSchema>;

packages/actor-core/src/actor/protocol/message/mod.ts

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ export async function processMessage<S, CP, CS, V>(
126126
conn._sendMessage(
127127
new CachedSerializer<wsToClient.ToClient>({
128128
b: {
129-
ro: {
129+
rr: {
130130
i: id,
131131
o: output,
132132
},
@@ -179,32 +179,18 @@ export async function processMessage<S, CP, CS, V>(
179179
});
180180

181181
// Build response
182-
if (rpcId !== undefined) {
183-
conn._sendMessage(
184-
new CachedSerializer({
185-
b: {
186-
re: {
187-
i: rpcId,
188-
c: code,
189-
m: message,
190-
md: metadata,
191-
},
182+
conn._sendMessage(
183+
new CachedSerializer<wsToClient.ToClient>({
184+
b: {
185+
e: {
186+
c: code,
187+
m: message,
188+
md: metadata,
189+
ri: rpcId,
192190
},
193-
}),
194-
);
195-
} else {
196-
conn._sendMessage(
197-
new CachedSerializer({
198-
b: {
199-
er: {
200-
c: code,
201-
m: message,
202-
md: metadata,
203-
},
204-
},
205-
}),
206-
);
207-
}
191+
},
192+
}),
193+
);
208194

209195
logger().debug("error response sent", { rpcId, rpcName });
210196
}

packages/actor-core/src/actor/protocol/message/to-client.ts

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,64 +9,42 @@ export const InitSchema = z.object({
99
});
1010

1111
// Used for connection errors (both during initialization and afterwards)
12-
export const ConnectionErrorSchema = z.object({
12+
export const ErrorSchema = z.object({
1313
// Code
1414
c: z.string(),
1515
// Message
1616
m: z.string(),
1717
// Metadata
1818
md: z.unknown().optional(),
19+
// RPC ID
20+
ri: z.number().int().optional(),
1921
});
2022

21-
export const RpcResponseOkSchema = z.object({
23+
export const RpcResponseSchema = z.object({
2224
// ID
2325
i: z.number().int(),
2426
// Output
2527
o: z.unknown(),
2628
});
2729

28-
export const RpcResponseErrorSchema = z.object({
29-
// ID
30-
i: z.number().int(),
31-
// Code
32-
c: z.string(),
33-
// Message
34-
m: z.string(),
35-
// Metadata
36-
md: z.unknown().optional(),
37-
});
38-
39-
export const ToClientEventSchema = z.object({
30+
export const EventSchema = z.object({
4031
// Name
4132
n: z.string(),
4233
// Args
4334
a: z.array(z.unknown()),
4435
});
4536

46-
export const ToClientErrorSchema = z.object({
47-
// Code
48-
c: z.string(),
49-
// Message
50-
m: z.string(),
51-
// Metadata
52-
md: z.unknown().optional(),
53-
});
54-
5537
export const ToClientSchema = z.object({
5638
// Body
5739
b: z.union([
5840
z.object({ i: InitSchema }),
59-
z.object({ ce: ConnectionErrorSchema }),
60-
z.object({ ro: RpcResponseOkSchema }),
61-
z.object({ re: RpcResponseErrorSchema }),
62-
z.object({ ev: ToClientEventSchema }),
63-
z.object({ er: ToClientErrorSchema }),
41+
z.object({ e: ErrorSchema }),
42+
z.object({ rr: RpcResponseSchema }),
43+
z.object({ ev: EventSchema }),
6444
]),
6545
});
6646

6747
export type ToClient = z.infer<typeof ToClientSchema>;
68-
export type ConnectionError = z.infer<typeof ConnectionErrorSchema>;
69-
export type RpcResponseOk = z.infer<typeof RpcResponseOkSchema>;
70-
export type RpcResponseError = z.infer<typeof RpcResponseErrorSchema>;
71-
export type ToClientEvent = z.infer<typeof ToClientEventSchema>;
72-
export type ToClientError = z.infer<typeof ToClientErrorSchema>;
48+
export type Error = z.infer<typeof ErrorSchema>;
49+
export type RpcResponse = z.infer<typeof RpcResponseSchema>;
50+
export type Event = z.infer<typeof EventSchema>;

packages/actor-core/src/actor/protocol/serde.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export type Encoding = z.infer<typeof EncodingSchema>;
2020
/**
2121
* Helper class that helps serialize data without re-serializing for the same encoding.
2222
*/
23-
export class CachedSerializer<T = unknown> {
23+
export class CachedSerializer<T> {
2424
#data: T;
2525
#cache = new Map<Encoding, OutputData>();
2626

packages/actor-core/src/actor/router_endpoints.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ export async function handleRpc(
249249
const encoding = getRequestEncoding(c.req);
250250
const parameters = getRequestConnParams(c.req, appConfig, driverConfig);
251251

252+
logger().debug("handling rpc", { rpcName, encoding });
253+
252254
// Validate incoming request
253255
let rpcArgs: unknown[];
254256
if (encoding === "json") {
@@ -271,7 +273,7 @@ export async function handleRpc(
271273
);
272274

273275
// Validate using the RPC schema
274-
const result = protoHttpRpc.RequestSchema.safeParse(deserialized);
276+
const result = protoHttpRpc.RpcRequestSchema.safeParse(deserialized);
275277
if (!result.success) {
276278
throw new errors.InvalidRpcRequest("Invalid RPC request format");
277279
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { AnyActorDefinition, ActorDefinition } from "@/actor/definition";
2+
3+
/**
4+
* RPC function returned by Actor connections and handles.
5+
*
6+
* @typedef {Function} ActorRPCFunction
7+
* @template Args
8+
* @template Response
9+
* @param {...Args} args - Arguments for the RPC function.
10+
* @returns {Promise<Response>}
11+
*/
12+
export type ActorRPCFunction<
13+
Args extends Array<unknown> = unknown[],
14+
Response = unknown,
15+
> = (
16+
...args: Args extends [unknown, ...infer Rest] ? Rest : Args
17+
) => Promise<Response>;
18+
19+
/**
20+
* Maps RPC methods from actor definition to typed function signatures.
21+
*/
22+
export type ActorDefinitionRpcs<AD extends AnyActorDefinition> =
23+
AD extends ActorDefinition<any, any, any, any, infer R> ? {
24+
[K in keyof R]: R[K] extends (
25+
...args: infer Args
26+
) => infer Return
27+
? ActorRPCFunction<Args, Return>
28+
: never;
29+
} : never;

0 commit comments

Comments
 (0)