durable-actors 0.2.4 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -139
- package/dist/actor/actor.d.ts +25 -0
- package/dist/actor/actor.d.ts.map +1 -1
- package/dist/actor/actor.js +17 -0
- package/dist/actor/actor.js.map +1 -1
- package/dist/actor/decorators.d.ts +8 -0
- package/dist/actor/decorators.d.ts.map +1 -1
- package/dist/actor/decorators.js +8 -0
- package/dist/actor/decorators.js.map +1 -1
- package/dist/actor/socket.d.ts +26 -0
- package/dist/actor/socket.d.ts.map +1 -1
- package/dist/actor/socket.js.map +1 -1
- package/dist/actor/socketValidation.d.ts +3 -0
- package/dist/actor/socketValidation.d.ts.map +1 -1
- package/dist/actor/socketValidation.js.map +1 -1
- package/dist/backend.d.ts +5 -3
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +3 -0
- package/dist/backend.js.map +1 -1
- package/dist/cli/actor-source-watcher.js +1 -1
- package/dist/cli/actor-source-watcher.js.map +1 -1
- package/dist/cli/connection.d.ts +2 -8
- package/dist/cli/connection.d.ts.map +1 -1
- package/dist/cli/connection.js +17 -13
- package/dist/cli/connection.js.map +1 -1
- package/dist/cli/control-plane.d.ts +7 -14
- package/dist/cli/control-plane.d.ts.map +1 -1
- package/dist/cli/control-plane.js +17 -21
- package/dist/cli/control-plane.js.map +1 -1
- package/dist/cli/dev.d.ts +13 -2
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/dev.js +28 -20
- package/dist/cli/dev.js.map +1 -1
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +15 -28
- package/dist/cli/generate.js.map +1 -1
- package/dist/cli/observe.d.ts +2 -2
- package/dist/cli/observe.d.ts.map +1 -1
- package/dist/cli/observe.js +23 -42
- package/dist/cli/observe.js.map +1 -1
- package/dist/cli/rust-runtime.js +3 -3
- package/dist/cli/start.d.ts +3 -0
- package/dist/cli/start.d.ts.map +1 -0
- package/dist/cli/start.js +12 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/cli.js +8 -35
- package/dist/cli.js.map +1 -1
- package/dist/client/actorHostGrpc.js +2 -2
- package/dist/client/clientSettings.js +1 -1
- package/dist/client/remoteClient.d.ts +3 -3
- package/dist/client/remoteClient.d.ts.map +1 -1
- package/dist/client/remoteClient.js +4 -5
- package/dist/client/remoteClient.js.map +1 -1
- package/dist/client/telemetry.js +1 -1
- package/dist/environment.d.ts.map +1 -1
- package/dist/environment.js +0 -8
- package/dist/environment.js.map +1 -1
- package/dist/errors.d.ts +3 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -0
- package/dist/errors.js.map +1 -1
- package/dist/generated/{durable_object → durable_actors}/v1/ActivateActorReply.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/ActivateActorReply.js +3 -0
- package/dist/generated/durable_actors/v1/ActivateActorReply.js.map +1 -0
- package/dist/generated/durable_actors/v1/ActivateActorRequest.d.ts +8 -0
- package/dist/generated/{durable_object → durable_actors}/v1/ActivateActorRequest.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/ActivateActorRequest.js +3 -0
- package/dist/generated/durable_actors/v1/ActivateActorRequest.js.map +1 -0
- package/dist/generated/{durable_object → durable_actors}/v1/ActorCompleted.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/ActorCompleted.js +3 -0
- package/dist/generated/durable_actors/v1/ActorCompleted.js.map +1 -0
- package/dist/generated/durable_actors/v1/ActorControlPlaneService.d.ts +21 -0
- package/dist/generated/{durable_object → durable_actors}/v1/ActorControlPlaneService.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/ActorControlPlaneService.js +3 -0
- package/dist/generated/durable_actors/v1/ActorControlPlaneService.js.map +1 -0
- package/dist/generated/{durable_object → durable_actors}/v1/ActorFailed.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/ActorFailed.js +3 -0
- package/dist/generated/durable_actors/v1/ActorFailed.js.map +1 -0
- package/dist/generated/durable_actors/v1/ActorHostService.d.ts +56 -0
- package/dist/generated/{durable_object → durable_actors}/v1/ActorHostService.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/ActorHostService.js +3 -0
- package/dist/generated/durable_actors/v1/ActorHostService.js.map +1 -0
- package/dist/generated/{durable_object → durable_actors}/v1/ActorKey.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/ActorKey.js +3 -0
- package/dist/generated/durable_actors/v1/ActorKey.js.map +1 -0
- package/dist/generated/{durable_object → durable_actors}/v1/ControlPlaneReply.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/ControlPlaneReply.js +3 -0
- package/dist/generated/durable_actors/v1/ControlPlaneReply.js.map +1 -0
- package/dist/generated/{durable_object → durable_actors}/v1/ControlPlaneRequest.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/ControlPlaneRequest.js +3 -0
- package/dist/generated/durable_actors/v1/ControlPlaneRequest.js.map +1 -0
- package/dist/generated/{durable_object → durable_actors}/v1/Empty.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/Empty.js +3 -0
- package/dist/generated/durable_actors/v1/Empty.js.map +1 -0
- package/dist/generated/durable_actors/v1/HostInvokeActorRequest.d.ts +11 -0
- package/dist/generated/{durable_object → durable_actors}/v1/HostInvokeActorRequest.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/HostInvokeActorRequest.js +3 -0
- package/dist/generated/durable_actors/v1/HostInvokeActorRequest.js.map +1 -0
- package/dist/generated/{durable_object → durable_actors}/v1/HostSocketEventRequest.d.ts +3 -3
- package/dist/generated/{durable_object → durable_actors}/v1/HostSocketEventRequest.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/HostSocketEventRequest.js +3 -0
- package/dist/generated/durable_actors/v1/HostSocketEventRequest.js.map +1 -0
- package/dist/generated/durable_actors/v1/InvokeActorReply.d.ts +16 -0
- package/dist/generated/{durable_object → durable_actors}/v1/InvokeActorReply.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/InvokeActorReply.js +3 -0
- package/dist/generated/durable_actors/v1/InvokeActorReply.js.map +1 -0
- package/dist/generated/durable_actors/v1/InvokeActorRequest.d.ts +14 -0
- package/dist/generated/{durable_object → durable_actors}/v1/InvokeActorRequest.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/InvokeActorRequest.js +3 -0
- package/dist/generated/durable_actors/v1/InvokeActorRequest.js.map +1 -0
- package/dist/generated/durable_actors/v1/PublishSocketEffectsRequest.d.ts +13 -0
- package/dist/generated/{durable_object → durable_actors}/v1/PublishSocketEffectsRequest.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/PublishSocketEffectsRequest.js +3 -0
- package/dist/generated/{durable_object → durable_actors}/v1/PublishSocketEffectsRequest.js.map +1 -1
- package/dist/generated/durable_actors/v1/ReplicaService.d.ts +42 -0
- package/dist/generated/{durable_object → durable_actors}/v1/ReplicaService.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/ReplicaService.js +3 -0
- package/dist/generated/durable_actors/v1/ReplicaService.js.map +1 -0
- package/dist/generated/durable_actors/v1/ReplicaSessionHead.d.ts +14 -0
- package/dist/generated/{durable_object → durable_actors}/v1/ReplicaSessionHead.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/ReplicaSessionHead.js +3 -0
- package/dist/generated/durable_actors/v1/ReplicaSessionHead.js.map +1 -0
- package/dist/generated/{durable_object → durable_actors}/v1/ReplicaSnapshotRef.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/ReplicaSnapshotRef.js +3 -0
- package/dist/generated/durable_actors/v1/ReplicaSnapshotRef.js.map +1 -0
- package/dist/generated/durable_actors/v1/ReplicaStreamHead.d.ts +11 -0
- package/dist/generated/{durable_object → durable_actors}/v1/ReplicaStreamHead.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/ReplicaStreamHead.js +3 -0
- package/dist/generated/durable_actors/v1/ReplicaStreamHead.js.map +1 -0
- package/dist/generated/{durable_object → durable_actors}/v1/ReplicaStreamIdentity.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/ReplicaStreamIdentity.js +3 -0
- package/dist/generated/durable_actors/v1/ReplicaStreamIdentity.js.map +1 -0
- package/dist/generated/{durable_object → durable_actors}/v1/Reroute.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/Reroute.js +3 -0
- package/dist/generated/durable_actors/v1/Reroute.js.map +1 -0
- package/dist/generated/{durable_object → durable_actors}/v1/SnapshotData.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/SnapshotData.js +3 -0
- package/dist/generated/durable_actors/v1/SnapshotData.js.map +1 -0
- package/dist/generated/durable_actors/v1/SnapshotService.d.ts +32 -0
- package/dist/generated/{durable_object → durable_actors}/v1/SnapshotService.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/SnapshotService.js +3 -0
- package/dist/generated/durable_actors/v1/SnapshotService.js.map +1 -0
- package/dist/generated/{durable_object → durable_actors}/v1/SnapshotWriteReply.d.ts.map +1 -1
- package/dist/generated/durable_actors/v1/SnapshotWriteReply.js +3 -0
- package/dist/generated/durable_actors/v1/SnapshotWriteReply.js.map +1 -0
- package/dist/generated/durable_actors.d.ts +69 -0
- package/dist/generated/{durable_object.d.ts.map → durable_actors.d.ts.map} +1 -1
- package/dist/generated/durable_actors.js +2 -0
- package/dist/generated/durable_actors.js.map +1 -0
- package/dist/generated/{durable_object.proto → durable_actors.proto} +1 -1
- package/dist/host/actor-host.js +8 -8
- package/dist/host/actor-worker.js +1 -1
- package/dist/host/actor-worker.js.map +1 -1
- package/dist/host.d.ts +1 -1
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +1 -1
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/localRuntime.d.ts +9 -1
- package/dist/localRuntime.d.ts.map +1 -1
- package/dist/localRuntime.js +4 -3
- package/dist/localRuntime.js.map +1 -1
- package/dist/proxy.d.ts +10 -1
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +8 -3
- package/dist/proxy.js.map +1 -1
- package/dist/templates/actor/README.md +4 -4
- package/dist/templates/actor/package.json +1 -2
- package/dist/templates/ai-chat/.env.example +6 -0
- package/dist/templates/ai-chat/README.md +44 -16
- package/dist/templates/ai-chat/gitignore +0 -1
- package/dist/templates/ai-chat/package.json +2 -1
- package/dist/templates/ai-chat/src/actors.ts +20 -0
- package/dist/templates/ai-chat/src/backend.ts +18 -5
- package/dist/templates/ai-chat/tsconfig.json +1 -1
- package/dist/templates/chat/.env.example +6 -0
- package/dist/templates/chat/README.md +57 -12
- package/dist/templates/chat/gitignore +0 -1
- package/dist/templates/chat/package.json +4 -3
- package/dist/templates/chat/src/backend.ts +5 -2
- package/dist/templates/documents/.env.example +6 -0
- package/dist/templates/documents/README.md +32 -17
- package/dist/templates/documents/gitignore +0 -1
- package/dist/templates/documents/package.json +4 -3
- package/dist/templates/documents/src/backend.ts +5 -2
- package/package.json +10 -7
- package/dist/cli/deploy.d.ts +0 -4
- package/dist/cli/deploy.d.ts.map +0 -1
- package/dist/cli/deploy.js +0 -39
- package/dist/cli/deploy.js.map +0 -1
- package/dist/cli/objects.d.ts +0 -4
- package/dist/cli/objects.d.ts.map +0 -1
- package/dist/cli/objects.js +0 -68
- package/dist/cli/objects.js.map +0 -1
- package/dist/generated/durable_object/v1/ActivateActorReply.js +0 -3
- package/dist/generated/durable_object/v1/ActivateActorReply.js.map +0 -1
- package/dist/generated/durable_object/v1/ActivateActorRequest.d.ts +0 -8
- package/dist/generated/durable_object/v1/ActivateActorRequest.js +0 -3
- package/dist/generated/durable_object/v1/ActivateActorRequest.js.map +0 -1
- package/dist/generated/durable_object/v1/ActorCompleted.js +0 -3
- package/dist/generated/durable_object/v1/ActorCompleted.js.map +0 -1
- package/dist/generated/durable_object/v1/ActorControlPlaneService.d.ts +0 -21
- package/dist/generated/durable_object/v1/ActorControlPlaneService.js +0 -3
- package/dist/generated/durable_object/v1/ActorControlPlaneService.js.map +0 -1
- package/dist/generated/durable_object/v1/ActorFailed.js +0 -3
- package/dist/generated/durable_object/v1/ActorFailed.js.map +0 -1
- package/dist/generated/durable_object/v1/ActorHostService.d.ts +0 -56
- package/dist/generated/durable_object/v1/ActorHostService.js +0 -3
- package/dist/generated/durable_object/v1/ActorHostService.js.map +0 -1
- package/dist/generated/durable_object/v1/ActorKey.js +0 -3
- package/dist/generated/durable_object/v1/ActorKey.js.map +0 -1
- package/dist/generated/durable_object/v1/ControlPlaneReply.js +0 -3
- package/dist/generated/durable_object/v1/ControlPlaneReply.js.map +0 -1
- package/dist/generated/durable_object/v1/ControlPlaneRequest.js +0 -3
- package/dist/generated/durable_object/v1/ControlPlaneRequest.js.map +0 -1
- package/dist/generated/durable_object/v1/Empty.js +0 -3
- package/dist/generated/durable_object/v1/Empty.js.map +0 -1
- package/dist/generated/durable_object/v1/HostInvokeActorRequest.d.ts +0 -11
- package/dist/generated/durable_object/v1/HostInvokeActorRequest.js +0 -3
- package/dist/generated/durable_object/v1/HostInvokeActorRequest.js.map +0 -1
- package/dist/generated/durable_object/v1/HostSocketEventRequest.js +0 -3
- package/dist/generated/durable_object/v1/HostSocketEventRequest.js.map +0 -1
- package/dist/generated/durable_object/v1/InvokeActorReply.d.ts +0 -16
- package/dist/generated/durable_object/v1/InvokeActorReply.js +0 -3
- package/dist/generated/durable_object/v1/InvokeActorReply.js.map +0 -1
- package/dist/generated/durable_object/v1/InvokeActorRequest.d.ts +0 -14
- package/dist/generated/durable_object/v1/InvokeActorRequest.js +0 -3
- package/dist/generated/durable_object/v1/InvokeActorRequest.js.map +0 -1
- package/dist/generated/durable_object/v1/PublishSocketEffectsRequest.d.ts +0 -13
- package/dist/generated/durable_object/v1/PublishSocketEffectsRequest.js +0 -3
- package/dist/generated/durable_object/v1/ReplicaService.d.ts +0 -42
- package/dist/generated/durable_object/v1/ReplicaService.js +0 -3
- package/dist/generated/durable_object/v1/ReplicaService.js.map +0 -1
- package/dist/generated/durable_object/v1/ReplicaSessionHead.d.ts +0 -14
- package/dist/generated/durable_object/v1/ReplicaSessionHead.js +0 -3
- package/dist/generated/durable_object/v1/ReplicaSessionHead.js.map +0 -1
- package/dist/generated/durable_object/v1/ReplicaSnapshotRef.js +0 -3
- package/dist/generated/durable_object/v1/ReplicaSnapshotRef.js.map +0 -1
- package/dist/generated/durable_object/v1/ReplicaStreamHead.d.ts +0 -11
- package/dist/generated/durable_object/v1/ReplicaStreamHead.js +0 -3
- package/dist/generated/durable_object/v1/ReplicaStreamHead.js.map +0 -1
- package/dist/generated/durable_object/v1/ReplicaStreamIdentity.js +0 -3
- package/dist/generated/durable_object/v1/ReplicaStreamIdentity.js.map +0 -1
- package/dist/generated/durable_object/v1/Reroute.js +0 -3
- package/dist/generated/durable_object/v1/Reroute.js.map +0 -1
- package/dist/generated/durable_object/v1/SnapshotData.js +0 -3
- package/dist/generated/durable_object/v1/SnapshotData.js.map +0 -1
- package/dist/generated/durable_object/v1/SnapshotService.d.ts +0 -32
- package/dist/generated/durable_object/v1/SnapshotService.js +0 -3
- package/dist/generated/durable_object/v1/SnapshotService.js.map +0 -1
- package/dist/generated/durable_object/v1/SnapshotWriteReply.js +0 -3
- package/dist/generated/durable_object/v1/SnapshotWriteReply.js.map +0 -1
- package/dist/generated/durable_object.d.ts +0 -69
- package/dist/generated/durable_object.js +0 -2
- package/dist/generated/durable_object.js.map +0 -1
- package/dist/templates/ai-chat/src/durable-objects.ts +0 -15
- /package/dist/generated/{durable_object → durable_actors}/v1/ActivateActorReply.d.ts +0 -0
- /package/dist/generated/{durable_object → durable_actors}/v1/ActorCompleted.d.ts +0 -0
- /package/dist/generated/{durable_object → durable_actors}/v1/ActorFailed.d.ts +0 -0
- /package/dist/generated/{durable_object → durable_actors}/v1/ActorKey.d.ts +0 -0
- /package/dist/generated/{durable_object → durable_actors}/v1/ControlPlaneReply.d.ts +0 -0
- /package/dist/generated/{durable_object → durable_actors}/v1/ControlPlaneRequest.d.ts +0 -0
- /package/dist/generated/{durable_object → durable_actors}/v1/Empty.d.ts +0 -0
- /package/dist/generated/{durable_object → durable_actors}/v1/ReplicaSnapshotRef.d.ts +0 -0
- /package/dist/generated/{durable_object → durable_actors}/v1/ReplicaStreamIdentity.d.ts +0 -0
- /package/dist/generated/{durable_object → durable_actors}/v1/Reroute.d.ts +0 -0
- /package/dist/generated/{durable_object → durable_actors}/v1/SnapshotData.d.ts +0 -0
- /package/dist/generated/{durable_object → durable_actors}/v1/SnapshotWriteReply.d.ts +0 -0
- /package/dist/templates/actor/src/{durable-objects.ts → actors.ts} +0 -0
- /package/dist/templates/chat/src/{durable-objects.ts → actors.ts} +0 -0
- /package/dist/templates/documents/src/{durable-objects.ts → actors.ts} +0 -0
package/dist/proxy.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
/** @module durable-actors/proxy */
|
|
1
2
|
import { z } from "zod";
|
|
3
|
+
/** Overrides for the backend's DURABLE_ACTORS environment settings. */
|
|
2
4
|
interface SocketProxyOptions {
|
|
3
5
|
readonly projectId?: string;
|
|
4
6
|
readonly controlPlaneUrl?: string;
|
|
5
7
|
readonly apiKey?: string;
|
|
8
|
+
/** Defaults to 180000 ms; accepts 1000–600000 ms. */
|
|
6
9
|
readonly setupTimeoutMs?: number;
|
|
7
10
|
}
|
|
8
11
|
interface SocketProxyDependencies {
|
|
@@ -11,6 +14,7 @@ interface SocketProxyDependencies {
|
|
|
11
14
|
interface ProxyActor<Metadata = unknown> {
|
|
12
15
|
readonly types?: Metadata;
|
|
13
16
|
}
|
|
17
|
+
/** Actor access your backend has already authorized. */
|
|
14
18
|
type SocketAuthorization<Actors extends Record<string, ProxyActor>> = {
|
|
15
19
|
[Name in keyof Actors & string]: {
|
|
16
20
|
readonly actorName: Name;
|
|
@@ -22,12 +26,13 @@ type SocketAuthorization<Actors extends Record<string, ProxyActor>> = {
|
|
|
22
26
|
}[keyof Actors & string];
|
|
23
27
|
declare const socketGrantSchema: z.ZodObject<{
|
|
24
28
|
websocketUrl: z.ZodURL;
|
|
25
|
-
transport: z.ZodLiteral<"websocket">;
|
|
26
29
|
homeRegion: z.ZodString;
|
|
27
30
|
connectByMs: z.ZodNumber;
|
|
28
31
|
authorizedUntilMs: z.ZodNumber;
|
|
29
32
|
}, z.core.$strip>;
|
|
33
|
+
/** Browser connection URL and deadlines in Unix milliseconds. Treat the URL as a credential. */
|
|
30
34
|
type SocketGrant = z.infer<typeof socketGrantSchema>;
|
|
35
|
+
/** Issues browser connection URLs from your backend. */
|
|
31
36
|
declare class SocketProxy<Actors extends Record<string, ProxyActor>> {
|
|
32
37
|
private readonly actors;
|
|
33
38
|
private readonly projectId;
|
|
@@ -36,6 +41,10 @@ declare class SocketProxy<Actors extends Record<string, ProxyActor>> {
|
|
|
36
41
|
private readonly setupTimeoutMs;
|
|
37
42
|
private readonly fetchRequest;
|
|
38
43
|
constructor(actors: Actors, options?: SocketProxyOptions, dependencies?: SocketProxyDependencies);
|
|
44
|
+
/**
|
|
45
|
+
* Call after authenticating the user and checking actor access.
|
|
46
|
+
* Pass `websocketUrl` to the browser's `new WebSocket()`.
|
|
47
|
+
*/
|
|
39
48
|
handle(authorization: SocketAuthorization<Actors>): Promise<SocketGrant>;
|
|
40
49
|
}
|
|
41
50
|
export { SocketProxy };
|
package/dist/proxy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAMvB,UAAU,kBAAkB;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CACnC;AAED,UAAU,uBAAuB;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;CAC3C;AAED,UAAU,UAAU,CAAC,QAAQ,GAAG,OAAO;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAA;CAC5B;AAED,KAAK,mBAAmB,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI;KACjE,IAAI,IAAI,MAAM,MAAM,GAAG,MAAM,GAAG;QAC7B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAA;QACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,UAAU,CAAC,MAAM,QAAQ,CAAC,GAAG,QAAQ,GAAG,KAAK,CAAA;QACrF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;QAC5B,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAA;KAC5C;CACJ,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAA;AAExB,QAAA,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAMvB,uEAAuE;AACvE,UAAU,kBAAkB;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,qDAAqD;IACrD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CACnC;AAED,UAAU,uBAAuB;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;CAC3C;AAED,UAAU,UAAU,CAAC,QAAQ,GAAG,OAAO;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAA;CAC5B;AAED,wDAAwD;AACxD,KAAK,mBAAmB,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI;KACjE,IAAI,IAAI,MAAM,MAAM,GAAG,MAAM,GAAG;QAC7B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAA;QACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,UAAU,CAAC,MAAM,QAAQ,CAAC,GAAG,QAAQ,GAAG,KAAK,CAAA;QACrF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;QAC5B,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAA;KAC5C;CACJ,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAA;AAExB,QAAA,MAAM,iBAAiB;;;;;iBAKrB,CAAA;AAEF,gGAAgG;AAChG,KAAK,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAEpD,wDAAwD;AACxD,cAAM,WAAW,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC;IAQnD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAP3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyB;gBAGjC,MAAM,EAAE,MAAM,EAC/B,OAAO,GAAE,kBAAuB,EAChC,YAAY,GAAE,uBAA4B;IAwB9C;;;OAGG;IACG,MAAM,CAAC,aAAa,EAAE,mBAAmB,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC;CA4BjF;AAaD,OAAO,EAAE,WAAW,EAAE,CAAA;AACtB,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,WAAW,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,CAAA"}
|
package/dist/proxy.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
/** @module durable-actors/proxy */
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { projectActorPath, validateActorComponent, validateProjectId } from "./actor/identity.js";
|
|
3
4
|
import { socketMetadata } from "./actor/socketValidation.js";
|
|
4
5
|
import { actorEnvironment } from "./environment.js";
|
|
5
6
|
const socketGrantSchema = z.object({
|
|
6
7
|
websocketUrl: z.url().refine(url => ["ws:", "wss:"].includes(new URL(url).protocol)),
|
|
7
|
-
transport: z.literal("websocket"),
|
|
8
8
|
homeRegion: z.string().min(1),
|
|
9
9
|
connectByMs: z.number().int(),
|
|
10
10
|
authorizedUntilMs: z.number().int()
|
|
11
11
|
});
|
|
12
|
+
/** Issues browser connection URLs from your backend. */
|
|
12
13
|
class SocketProxy {
|
|
13
14
|
actors;
|
|
14
15
|
projectId;
|
|
@@ -37,6 +38,10 @@ class SocketProxy {
|
|
|
37
38
|
this.projectId = validateProjectId(settings.projectId);
|
|
38
39
|
this.fetchRequest = dependencies.fetch ?? globalThis.fetch;
|
|
39
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Call after authenticating the user and checking actor access.
|
|
43
|
+
* Pass `websocketUrl` to the browser's `new WebSocket()`.
|
|
44
|
+
*/
|
|
40
45
|
async handle(authorization) {
|
|
41
46
|
const actorName = validateActorComponent("actor name", authorization.actorName);
|
|
42
47
|
const actorId = validateActorComponent("actor ID", authorization.actorId);
|
|
@@ -51,10 +56,10 @@ class SocketProxy {
|
|
|
51
56
|
const homeRegion = authorization.homeRegion === undefined
|
|
52
57
|
? undefined
|
|
53
58
|
: validateActorComponent("home region", authorization.homeRegion);
|
|
54
|
-
const response = await this.fetchRequest(`${this.origin}${projectActorPath(this.projectId, actorName, actorId)}/
|
|
59
|
+
const response = await this.fetchRequest(`${this.origin}${projectActorPath(this.projectId, actorName, actorId)}/find-websocket`, {
|
|
55
60
|
method: "POST",
|
|
56
61
|
headers: { authorization: `Bearer ${this.apiKey}`, "content-type": "application/json" },
|
|
57
|
-
body: JSON.stringify({
|
|
62
|
+
body: JSON.stringify({ metadata, authorizationLifetimeMs, homeRegion }),
|
|
58
63
|
signal: AbortSignal.timeout(this.setupTimeoutMs)
|
|
59
64
|
});
|
|
60
65
|
if (!response.ok)
|
package/dist/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACjG,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,9 +7,9 @@ pnpm install
|
|
|
7
7
|
pnpm exec durable-actors dev
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
Define your actors in `src/
|
|
10
|
+
Define your actors in `src/actors.ts`. The starter contains a counter with persisted state and `read()` and `increment()` methods. Each counter ID has its own saved value.
|
|
11
11
|
|
|
12
|
-
`durable-actors dev` watches your actor source and stores local state in `.durable-actors/`. It defaults to project ID `local`; choose another with `pnpm exec durable-actors dev
|
|
12
|
+
`durable-actors dev` watches your actor source and stores local state in `.durable-actors/`. It defaults to project ID `local`; choose another with `DURABLE_ACTORS_PROJECT_ID=my-project pnpm exec durable-actors dev`. If the CLI is installed globally, you can run `durable-actors dev` directly.
|
|
13
13
|
|
|
14
14
|
## Connect your application
|
|
15
15
|
|
|
@@ -17,7 +17,7 @@ In your separate application project's directory:
|
|
|
17
17
|
|
|
18
18
|
1. Run `pnpm add durable-actors`.
|
|
19
19
|
2. Copy the project ID, URL, and shared secret printed by the actor server into your application’s `.env` file.
|
|
20
|
-
3. Run `durable-actors generate`; the CLI loads `.env` automatically.
|
|
20
|
+
3. Run `pnpm exec durable-actors generate --remote`; the CLI loads `.env` automatically.
|
|
21
21
|
|
|
22
22
|
Your application backend can then use the generated client:
|
|
23
23
|
|
|
@@ -30,4 +30,4 @@ console.log(await counter.increment())
|
|
|
30
30
|
|
|
31
31
|
Start your backend with that `.env` file loaded. Update its secret after restarting the actor server. If you change the actor server's URL or port, also set `DURABLE_ACTORS_CONTROL_PLANE_URL` in the backend environment. After changing actor method signatures, rerun the printed generate command in your application.
|
|
32
32
|
|
|
33
|
-
Use `pnpm check` to check types
|
|
33
|
+
Use `pnpm check` to check types. Production deployment integrations register actor images through `PUT /v1/projects/{project_id}/deployment`; see the [HTTP API](https://github.com/TerseAI/durable-actors/blob/main/docs/reference/openapi.yaml).
|
|
@@ -7,11 +7,10 @@
|
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
9
|
"dev": "durable-actors dev",
|
|
10
|
-
"build": "durable-actors build",
|
|
11
10
|
"check": "tsc --noEmit"
|
|
12
11
|
},
|
|
13
12
|
"dependencies": {
|
|
14
|
-
"durable-actors": "0.2.
|
|
13
|
+
"durable-actors": "0.2.7"
|
|
15
14
|
},
|
|
16
15
|
"devDependencies": {
|
|
17
16
|
"@types/node": "^22.0.0",
|
|
@@ -1,41 +1,69 @@
|
|
|
1
|
-
# AI chat
|
|
1
|
+
# AI chat
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An Express + React app that streams replies with the Vercel AI SDK and saves conversations in durable actors.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Run locally
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Requires Node.js 22.19+, Bun 1.4.2+, and an OpenAI API key.
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
10
|
npx durable-actors init ai-chat-example --template ai-chat
|
|
11
11
|
cd ai-chat-example
|
|
12
12
|
npm install
|
|
13
|
+
cp .env.example .env
|
|
13
14
|
```
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
Already in the example directory? Start at `npm install`. Add `OPENAI_API_KEY` to `.env`, then start the actors:
|
|
16
17
|
|
|
17
18
|
```sh
|
|
18
|
-
|
|
19
|
+
npm run dev:actors
|
|
19
20
|
```
|
|
20
21
|
|
|
21
|
-
Wait for `
|
|
22
|
+
Wait for `Ready`. In another terminal in the same directory:
|
|
22
23
|
|
|
23
24
|
```sh
|
|
24
25
|
npm run dev
|
|
25
26
|
```
|
|
26
27
|
|
|
27
|
-
Open [
|
|
28
|
+
Open [localhost:3000](http://127.0.0.1:3000), send a message, and reload after the reply finishes. The conversation survives server restarts.
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
## Save the conversation
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
[ChatHistory](src/actors.ts) keeps one conversation per actor ID:
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- [src/Chat.tsx](src/Chat.tsx) loads the lobby history and uses `useChat` to send messages and render streaming replies.
|
|
34
|
+
```ts
|
|
35
|
+
import { Actor, Persisted } from "durable-actors"
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
export class ChatHistory extends Actor {
|
|
38
|
+
@Persisted private messages: ChatMessage[] = []
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
async load() {
|
|
41
|
+
return this.messages
|
|
42
|
+
}
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
async append(message: ChatMessage) {
|
|
45
|
+
this.messages.push(message)
|
|
46
|
+
return this.messages
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type ChatMessage = {
|
|
51
|
+
id: string
|
|
52
|
+
role: "system" | "user" | "assistant"
|
|
53
|
+
parts: { type: "text"; text: string }[]
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Stream the reply
|
|
58
|
+
|
|
59
|
+
The [Express backend](src/backend.ts) appends the user message, sends the saved conversation to the model, and streams the reply. It saves the assistant message when the reply completes.
|
|
60
|
+
|
|
61
|
+
The [React client](src/Chat.tsx) loads saved messages and uses `useChat` to display the stream. This demo saves text only; in-progress streams are not resumed after a reload.
|
|
62
|
+
|
|
63
|
+
The lobby is shared and has no authentication. Add authentication and chat ownership checks before using it for private conversations.
|
|
64
|
+
|
|
65
|
+
## Development
|
|
66
|
+
|
|
67
|
+
Both processes read `.env`; actor state lives in `.durable-actors/`. Actor code reloads automatically; restart `npm run dev` after editing the actor class imported by the backend. For multiple examples, set distinct `PORT`, `DURABLE_ACTORS_PORT`, and matching control-plane URLs; see [Run the examples together](../README.md).
|
|
68
|
+
|
|
69
|
+
`npm run build` checks TypeScript and builds the frontend.
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
9
|
"dev": "tsx --env-file=.env src/backend.ts",
|
|
10
|
+
"dev:actors": "durable-actors dev",
|
|
10
11
|
"build": "tsc --noEmit && vite build"
|
|
11
12
|
},
|
|
12
13
|
"dependencies": {
|
|
@@ -14,7 +15,7 @@
|
|
|
14
15
|
"@ai-sdk/react": "^4.0.100",
|
|
15
16
|
"ai": "^7.0.97",
|
|
16
17
|
"express": "^5.2.1",
|
|
17
|
-
"durable-actors": "0.2.
|
|
18
|
+
"durable-actors": "0.2.7",
|
|
18
19
|
"react": "^19.3.0",
|
|
19
20
|
"react-dom": "^19.3.0"
|
|
20
21
|
},
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Actor, Persisted } from "durable-actors"
|
|
2
|
+
|
|
3
|
+
export class ChatHistory extends Actor {
|
|
4
|
+
@Persisted private messages: ChatMessage[] = []
|
|
5
|
+
|
|
6
|
+
async load() {
|
|
7
|
+
return this.messages
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async append(message: ChatMessage) {
|
|
11
|
+
this.messages.push(message)
|
|
12
|
+
return this.messages
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type ChatMessage = {
|
|
17
|
+
id: string
|
|
18
|
+
role: "system" | "user" | "assistant"
|
|
19
|
+
parts: { type: "text"; text: string }[]
|
|
20
|
+
}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { openai } from "@ai-sdk/openai"
|
|
2
2
|
import { convertToModelMessages, generateId, pipeUIMessageStreamToResponse, streamText, toUIMessageStream, validateUIMessages } from "ai"
|
|
3
|
+
import type { UIMessage } from "ai"
|
|
3
4
|
import express from "express"
|
|
5
|
+
import { createServer as createHttpServer } from "node:http"
|
|
4
6
|
import { createServer } from "vite"
|
|
5
7
|
|
|
6
|
-
import { ChatHistory } from "./
|
|
8
|
+
import { ChatHistory } from "./actors.js"
|
|
9
|
+
import type { ChatMessage } from "./actors.js"
|
|
7
10
|
|
|
8
11
|
const app = express()
|
|
12
|
+
const server = createHttpServer(app)
|
|
13
|
+
const port = Number(process.env.PORT ?? 3000)
|
|
9
14
|
app.use(express.json())
|
|
10
15
|
|
|
11
16
|
app.get("/api/chat/:id", async (request, response) => {
|
|
@@ -16,7 +21,7 @@ app.post("/api/chat", async (request, response) => {
|
|
|
16
21
|
const [message] = await validateUIMessages({ messages: [request.body.messages.at(-1)] })
|
|
17
22
|
if (message.role !== "user") return response.sendStatus(400)
|
|
18
23
|
const chat = ChatHistory.get(request.body.id)
|
|
19
|
-
const messages = await chat.append(message)
|
|
24
|
+
const messages = await chat.append(historyMessage(message))
|
|
20
25
|
const result = streamText({
|
|
21
26
|
model: openai("gpt-5-mini"),
|
|
22
27
|
messages: await convertToModelMessages(messages)
|
|
@@ -28,14 +33,22 @@ app.post("/api/chat", async (request, response) => {
|
|
|
28
33
|
originalMessages: messages,
|
|
29
34
|
generateMessageId: generateId,
|
|
30
35
|
onEnd: async ({ responseMessage, outcome }) => {
|
|
31
|
-
if (outcome.status === "completed") await chat.append(responseMessage)
|
|
36
|
+
if (outcome.status === "completed") await chat.append(historyMessage(responseMessage))
|
|
32
37
|
}
|
|
33
38
|
})
|
|
34
39
|
})
|
|
35
40
|
})
|
|
36
41
|
|
|
37
42
|
const vite = await createServer({
|
|
38
|
-
server: { middlewareMode: true, fs: { deny: [".env", ".env.*", "**/.durable-actors/**", "**/.
|
|
43
|
+
server: { middlewareMode: true, hmr: { server }, fs: { deny: [".env", ".env.*", "**/.durable-actors/**", "**/.git/**"] } }
|
|
39
44
|
})
|
|
40
45
|
app.use(vite.middlewares)
|
|
41
|
-
|
|
46
|
+
server.listen(port, "127.0.0.1", () => console.log(`AI chat: http://127.0.0.1:${port}`))
|
|
47
|
+
|
|
48
|
+
function historyMessage(message: UIMessage): ChatMessage {
|
|
49
|
+
return {
|
|
50
|
+
id: message.id,
|
|
51
|
+
role: message.role,
|
|
52
|
+
parts: message.parts.filter(part => part.type === "text").map(part => ({ type: "text", text: part.text }))
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -1,29 +1,74 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Chatroom
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An Express + React chatroom backed by a durable actor. Messages appear in every connected tab, and history survives server restarts.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Run locally
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Requires Node.js 22.19+ and Bun 1.4.2+.
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
+
npx durable-actors init chat-example --template chat
|
|
11
|
+
cd chat-example
|
|
10
12
|
npm install
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
cp .env.example .env
|
|
14
|
+
npm run dev:actors
|
|
13
15
|
```
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
Already in the example directory? Start at `npm install`.
|
|
18
|
+
|
|
19
|
+
Wait for `Ready`. In another terminal in the same directory:
|
|
16
20
|
|
|
17
21
|
```sh
|
|
18
22
|
npm run dev
|
|
19
23
|
```
|
|
20
24
|
|
|
21
|
-
Open [
|
|
25
|
+
Open [localhost:3000](http://127.0.0.1:3000) in two tabs. Send a message, then restart the actor server and reload to see the saved history.
|
|
26
|
+
|
|
27
|
+
## Define the actor
|
|
28
|
+
|
|
29
|
+
[ChatRoom](src/actors.ts) saves messages and sends the updated history to everyone in the room:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { Actor, Persisted } from "durable-actors"
|
|
33
|
+
import type { ActorSocket } from "durable-actors"
|
|
34
|
+
|
|
35
|
+
export class ChatRoom extends Actor<Member, string, ChatMessage[]> {
|
|
36
|
+
@Persisted history: ChatMessage[] = []
|
|
37
|
+
|
|
38
|
+
async onConnect(socket: ActorSocket<Member, ChatMessage[]>) {
|
|
39
|
+
socket.send(this.history)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async onMessage(socket: ActorSocket<Member, ChatMessage[]>, text: string) {
|
|
43
|
+
this.history.push({ name: socket.metadata.name, text })
|
|
44
|
+
this.broadcast(this.history)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type Member = { name: string }
|
|
49
|
+
type ChatMessage = { name: string; text: string }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Each room ID has its own history.
|
|
53
|
+
|
|
54
|
+
## Connect the app
|
|
55
|
+
|
|
56
|
+
The [Express backend](src/backend.ts) issues a WebSocket URL with `actors.ChatRoom.prepareWebsocket`. The [React client](src/Chat.tsx) connects and sends JSON messages:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
const response = await fetch("/api/socket/ChatRoom/lobby", { method: "POST" })
|
|
60
|
+
if (!response.ok) throw new Error("Connection denied")
|
|
61
|
+
const { websocketUrl } = await response.json()
|
|
62
|
+
const socket = new WebSocket(websocketUrl)
|
|
63
|
+
|
|
64
|
+
socket.onopen = () => socket.send(JSON.stringify("Hello"))
|
|
65
|
+
socket.onmessage = event => console.log(JSON.parse(event.data))
|
|
66
|
+
```
|
|
22
67
|
|
|
23
|
-
|
|
68
|
+
Everyone joins as a guest. Add authentication and room access checks before issuing URLs in your app. Reload to reconnect after a disconnect.
|
|
24
69
|
|
|
25
|
-
|
|
70
|
+
## Development
|
|
26
71
|
|
|
27
|
-
|
|
72
|
+
Both processes read `.env`; saved state lives in `.durable-actors/`. Actor code reloads automatically. After changing public actor types, restart `npm run dev` to regenerate the client. For multiple examples, set distinct `PORT`, `DURABLE_ACTORS_PORT`, and matching control-plane URLs; see [Run the examples together](../README.md).
|
|
28
73
|
|
|
29
|
-
|
|
74
|
+
`npm run build` generates clients, checks TypeScript, and builds the frontend.
|
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
"node": ">=22.19.0"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"dev": "tsx src/backend.ts",
|
|
10
|
-
"
|
|
9
|
+
"dev": "durable-actors generate && tsx --env-file=.env src/backend.ts",
|
|
10
|
+
"dev:actors": "durable-actors dev",
|
|
11
|
+
"build": "durable-actors generate && tsc --noEmit && vite build"
|
|
11
12
|
},
|
|
12
13
|
"dependencies": {
|
|
13
14
|
"express": "^5.2.1",
|
|
14
|
-
"durable-actors": "0.2.
|
|
15
|
+
"durable-actors": "0.2.7",
|
|
15
16
|
"react": "^19.3.0",
|
|
16
17
|
"react-dom": "^19.3.0"
|
|
17
18
|
},
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import express from "express"
|
|
2
|
+
import { createServer as createHttpServer } from "node:http"
|
|
2
3
|
import { createServer } from "vite"
|
|
3
4
|
|
|
4
5
|
import { actors } from "../generated/index.js"
|
|
5
6
|
|
|
6
7
|
const app = express()
|
|
8
|
+
const server = createHttpServer(app)
|
|
9
|
+
const port = Number(process.env.PORT ?? 3000)
|
|
7
10
|
|
|
8
11
|
app.post("/api/socket/ChatRoom/:actorId", async (request, response) => {
|
|
9
12
|
const grant = await actors.ChatRoom.prepareWebsocket({
|
|
@@ -14,7 +17,7 @@ app.post("/api/socket/ChatRoom/:actorId", async (request, response) => {
|
|
|
14
17
|
})
|
|
15
18
|
|
|
16
19
|
const vite = await createServer({
|
|
17
|
-
server: { middlewareMode: true, fs: { deny: [".env", ".env.*", "**/.durable-actors/**", "**/.
|
|
20
|
+
server: { middlewareMode: true, hmr: { server }, fs: { deny: [".env", ".env.*", "**/.durable-actors/**", "**/.git/**"] } }
|
|
18
21
|
})
|
|
19
22
|
app.use(vite.middlewares)
|
|
20
|
-
|
|
23
|
+
server.listen(port, "127.0.0.1", () => console.log(`Chat: http://127.0.0.1:${port}`))
|
|
@@ -1,38 +1,53 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Collaborative documents
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A Tiptap editor with Yjs for concurrent edits and durable actors for saved documents. Edit together in multiple tabs, then reload to pick up where you left off.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Run locally
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Requires Node.js 22.19+ and Bun 1.4.2+.
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
10
|
npx durable-actors init documents-example --template documents
|
|
11
11
|
cd documents-example
|
|
12
12
|
npm install
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
cp .env.example .env
|
|
14
|
+
npm run dev:actors
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Already in the example directory? Start at `npm install`.
|
|
18
|
+
|
|
19
|
+
Wait for `Ready`. In another terminal in the same directory:
|
|
18
20
|
|
|
19
21
|
```sh
|
|
20
22
|
npm run dev
|
|
21
23
|
```
|
|
22
24
|
|
|
23
|
-
Open [
|
|
25
|
+
Open [localhost:3000](http://127.0.0.1:3000) in two tabs. Edit **Welcome** from both, add another document, and switch between them. Restart the servers and reload to restore saved documents.
|
|
26
|
+
|
|
27
|
+
## Save and share documents
|
|
28
|
+
|
|
29
|
+
[src/actors.ts](src/actors.ts) defines two actors:
|
|
30
|
+
|
|
31
|
+
- `Workspace` saves the document list.
|
|
32
|
+
- `Document` merges Yjs updates, saves the merged content, and broadcasts it to connected editors. Each document ID has its own actor.
|
|
24
33
|
|
|
25
|
-
|
|
34
|
+
A document joins through the [Express backend](src/backend.ts):
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { actors } from "../generated/index.js"
|
|
38
|
+
|
|
39
|
+
const grant = await actors.Document.prepareWebsocket({
|
|
40
|
+
actorId: "welcome",
|
|
41
|
+
metadata: null
|
|
42
|
+
})
|
|
43
|
+
```
|
|
26
44
|
|
|
27
|
-
|
|
45
|
+
The [collaboration client](src/collaboration.ts) opens the returned `websocketUrl` and exchanges Yjs updates. [App.tsx](src/App.tsx) handles document navigation; [Editor.tsx](src/Editor.tsx) connects Tiptap to the shared document.
|
|
28
46
|
|
|
29
|
-
|
|
30
|
-
- [src/backend.ts](src/backend.ts): issues WebSocket credentials using the generated `prepareWebsocket` helpers and local defaults.
|
|
31
|
-
- [src/collaboration.ts](src/collaboration.ts): connects Yjs to a native WebSocket. It sends local updates and applies remote state without echoing it.
|
|
32
|
-
- [src/App.tsx](src/App.tsx) and [src/Editor.tsx](src/Editor.tsx): document navigation and Tiptap’s editor, formatting controls, and collaborative undo/redo.
|
|
47
|
+
Editing pauses while disconnected. Reload to reconnect; unsaved edits are not stored in the browser. This demo shares one workspace without authentication. Add user authentication and document access checks before issuing WebSocket URLs in your app.
|
|
33
48
|
|
|
34
|
-
|
|
49
|
+
## Development
|
|
35
50
|
|
|
36
|
-
|
|
51
|
+
Both processes read `.env`; saved state lives in `.durable-actors/`. Actor code reloads automatically. After changing public actor types, restart `npm run dev` to regenerate the client. For multiple examples, set distinct `PORT`, `DURABLE_ACTORS_PORT`, and matching control-plane URLs; see [Run the examples together](../README.md).
|
|
37
52
|
|
|
38
|
-
|
|
53
|
+
`npm run build` generates clients, checks TypeScript, and builds the frontend.
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
"node": ">=22.19.0"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"dev": "tsx src/backend.ts",
|
|
10
|
-
"
|
|
9
|
+
"dev": "durable-actors generate && tsx --env-file=.env src/backend.ts",
|
|
10
|
+
"dev:actors": "durable-actors dev",
|
|
11
|
+
"build": "durable-actors generate && tsc --noEmit && vite build"
|
|
11
12
|
},
|
|
12
13
|
"dependencies": {
|
|
13
14
|
"@tiptap/core": "^3.31.3",
|
|
@@ -18,7 +19,7 @@
|
|
|
18
19
|
"@tiptap/y-tiptap": "^3.0.7",
|
|
19
20
|
"express": "^5.2.1",
|
|
20
21
|
"lib0": "^0.2.117",
|
|
21
|
-
"durable-actors": "0.2.
|
|
22
|
+
"durable-actors": "0.2.7",
|
|
22
23
|
"react": "^19.3.0",
|
|
23
24
|
"react-dom": "^19.3.0",
|
|
24
25
|
"yjs": "^13.6.32"
|