@agent-glue/glue 0.1.0
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/.turbo/turbo-build.log +5 -0
- package/.turbo/turbo-clean$colon$build.log +1 -0
- package/.turbo/turbo-format$colon$fix.log +46 -0
- package/.turbo/turbo-format.log +13 -0
- package/.turbo/turbo-lint$colon$fix.log +9 -0
- package/.turbo/turbo-lint.log +5 -0
- package/.turbo/turbo-test.log +27 -0
- package/.turbo/turbo-typecheck.log +5 -0
- package/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/dist/Actor.d.ts +51 -0
- package/dist/Actor.js +30 -0
- package/dist/actor.js +32 -0
- package/dist/actors/examples/timer.d.ts +1 -0
- package/dist/actors/examples/timer.js +1 -0
- package/dist/actors/langchain/llm.d.ts +40 -0
- package/dist/actors/langchain/llm.js +52 -0
- package/dist/constructors.js +1 -0
- package/dist/effects.js +66 -0
- package/dist/emitter.js +19 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/message.js +18 -0
- package/dist/message.test.js +37 -0
- package/dist/server/memory.js +46 -0
- package/dist/server/protocol.js +4 -0
- package/dist/server/websocket.js +72 -0
- package/dist/server.js +3 -0
- package/dist/transformers.d.ts +41 -0
- package/dist/transformers.js +17 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types.js +1 -0
- package/dist/utils/TypedEmitter.d.ts +15 -0
- package/dist/utils/TypedEmitter.js +16 -0
- package/eslint.config.js +4 -0
- package/nodemon.json +7 -0
- package/package.json +75 -0
- package/src/actor.ts +69 -0
- package/src/effects.ts +88 -0
- package/src/emitter.ts +22 -0
- package/src/index.ts +1 -0
- package/src/message.test.ts +52 -0
- package/src/message.ts +54 -0
- package/src/server/memory.ts +77 -0
- package/src/server/protocol.ts +36 -0
- package/src/server/websocket.ts +137 -0
- package/src/server.ts +3 -0
- package/src/transformers.ts +66 -0
- package/src/types.ts +7 -0
- package/src/utils/TypedEmitter.ts +30 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
> @agent-glue/glue@0.1.0 format:fix /home/gareth/Documents/Personal/2025/agent-glue/packages/agent-glue
|
|
4
|
+
> prettier --write .
|
|
5
|
+
|
|
6
|
+
CHANGELOG.md[2K[1G[90mCHANGELOG.md[39m 44ms
|
|
7
|
+
dist/Actor.d.ts[2K[1G[90mdist/Actor.d.ts[39m 233ms
|
|
8
|
+
dist/actor.js[2K[1G[90mdist/actor.js[39m 66ms
|
|
9
|
+
dist/Actor.js[2K[1G[90mdist/Actor.js[39m 9ms
|
|
10
|
+
dist/actors/examples/timer.d.ts[2K[1G[90mdist/actors/examples/timer.d.ts[39m 4ms
|
|
11
|
+
dist/actors/examples/timer.js[2K[1G[90mdist/actors/examples/timer.js[39m 2ms
|
|
12
|
+
dist/actors/langchain/llm.d.ts[2K[1G[90mdist/actors/langchain/llm.d.ts[39m 19ms
|
|
13
|
+
dist/actors/langchain/llm.js[2K[1G[90mdist/actors/langchain/llm.js[39m 24ms
|
|
14
|
+
dist/constructors.js[2K[1G[90mdist/constructors.js[39m 4ms
|
|
15
|
+
dist/effects.js[2K[1G[90mdist/effects.js[39m 18ms
|
|
16
|
+
dist/emitter.js[2K[1G[90mdist/emitter.js[39m 7ms
|
|
17
|
+
dist/index.d.ts[2K[1G[90mdist/index.d.ts[39m 4ms
|
|
18
|
+
dist/index.js[2K[1G[90mdist/index.js[39m 2ms
|
|
19
|
+
dist/message.js[2K[1G[90mdist/message.js[39m 5ms
|
|
20
|
+
dist/message.test.js[2K[1G[90mdist/message.test.js[39m 10ms
|
|
21
|
+
dist/server.js[2K[1G[90mdist/server.js[39m 2ms
|
|
22
|
+
dist/server/memory.js[2K[1G[90mdist/server/memory.js[39m 10ms
|
|
23
|
+
dist/server/protocol.js[2K[1G[90mdist/server/protocol.js[39m 4ms
|
|
24
|
+
dist/server/websocket.js[2K[1G[90mdist/server/websocket.js[39m 14ms
|
|
25
|
+
dist/transformers.d.ts[2K[1G[90mdist/transformers.d.ts[39m 14ms
|
|
26
|
+
dist/transformers.js[2K[1G[90mdist/transformers.js[39m 4ms
|
|
27
|
+
dist/types.js[2K[1G[90mdist/types.js[39m 2ms
|
|
28
|
+
dist/utils/TypedEmitter.d.ts[2K[1G[90mdist/utils/TypedEmitter.d.ts[39m 6ms
|
|
29
|
+
dist/utils/TypedEmitter.js[2K[1G[90mdist/utils/TypedEmitter.js[39m 2ms
|
|
30
|
+
eslint.config.js[2K[1G[90meslint.config.js[39m 3ms
|
|
31
|
+
nodemon.json[2K[1G[90mnodemon.json[39m 3ms
|
|
32
|
+
package.json[2K[1G[90mpackage.json[39m 3ms
|
|
33
|
+
src/actor.ts[2K[1G[90msrc/actor.ts[39m 22ms
|
|
34
|
+
src/effects.ts[2K[1G[90msrc/effects.ts[39m 23ms
|
|
35
|
+
src/emitter.ts[2K[1G[90msrc/emitter.ts[39m 10ms
|
|
36
|
+
src/index.ts[2K[1G[90msrc/index.ts[39m 2ms
|
|
37
|
+
src/message.test.ts[2K[1G[90msrc/message.test.ts[39m 16ms
|
|
38
|
+
src/message.ts[2K[1G[90msrc/message.ts[39m 14ms
|
|
39
|
+
src/server.ts[2K[1G[90msrc/server.ts[39m 2ms
|
|
40
|
+
src/server/memory.ts[2K[1G[90msrc/server/memory.ts[39m 18ms
|
|
41
|
+
src/server/protocol.ts[2K[1G[90msrc/server/protocol.ts[39m 7ms
|
|
42
|
+
src/server/websocket.ts[2K[1G[90msrc/server/websocket.ts[39m 23ms
|
|
43
|
+
src/transformers.ts[2K[1G[90msrc/transformers.ts[39m 10ms
|
|
44
|
+
src/types.ts[2K[1G[90msrc/types.ts[39m 3ms
|
|
45
|
+
src/utils/TypedEmitter.ts[2K[1G[90msrc/utils/TypedEmitter.ts[39m 5ms
|
|
46
|
+
tsconfig.json[2K[1G[90mtsconfig.json[39m 2ms
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
> @agent-glue/glue@0.1.0 format /home/gareth/Documents/Personal/2025/agent-glue/packages/agent-glue
|
|
4
|
+
> prettier --check .
|
|
5
|
+
|
|
6
|
+
Checking formatting...
|
|
7
|
+
CHANGELOG.md[2K[1Gdist/Actor.d.ts[2K[1Gdist/actor.js[2K[1G[[33mwarn[39m] dist/actor.js
|
|
8
|
+
dist/Actor.js[2K[1G[[33mwarn[39m] dist/Actor.js
|
|
9
|
+
dist/actors/examples/timer.d.ts[2K[1Gdist/actors/examples/timer.js[2K[1Gdist/actors/langchain/llm.d.ts[2K[1Gdist/actors/langchain/llm.js[2K[1Gdist/constructors.js[2K[1Gdist/effects.js[2K[1G[[33mwarn[39m] dist/effects.js
|
|
10
|
+
dist/emitter.js[2K[1G[[33mwarn[39m] dist/emitter.js
|
|
11
|
+
dist/index.d.ts[2K[1Gdist/index.js[2K[1Gdist/message.js[2K[1G[[33mwarn[39m] dist/message.js
|
|
12
|
+
dist/message.test.js[2K[1G[[33mwarn[39m] dist/message.test.js
|
|
13
|
+
dist/server.js[2K[1Gdist/server/memory.js
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
> @agent-glue/glue@0.0.1 test /home/gareth/Documents/Personal/2025/agent-glue/packages/agent-glue
|
|
4
|
+
> vitest
|
|
5
|
+
|
|
6
|
+
[?25l
|
|
7
|
+
[1m[7m[34m DEV [39m[27m[22m [34mv3.0.4 [39m[90m/home/gareth/Documents/Personal/2025/agent-glue/packages/agent-glue[39m
|
|
8
|
+
|
|
9
|
+
[?2026h
|
|
10
|
+
[1m[33m ❯ [39m[22msrc/message.test.ts[2m [queued][22m
|
|
11
|
+
|
|
12
|
+
[2m Test Files [22m[1m[32m0 passed[39m[22m[90m (1)[39m
|
|
13
|
+
[2m Tests [22m[1m[32m0 passed[39m[22m[90m (0)[39m
|
|
14
|
+
[2m Start at [22m03:32:17
|
|
15
|
+
[2m Duration [22m200ms
|
|
16
|
+
[?2026l[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K [32m✓[39m src/message.test.ts [2m([22m[2m3 tests[22m[2m)[22m[90m 5[2mms[22m[39m
|
|
17
|
+
[32m✓[39m defineMessage[2m > [22mshould create a message definition and type guard
|
|
18
|
+
[32m✓[39m defineMessage[2m > [22mis[2m > [22mshould allow checking if a message is of the defined type
|
|
19
|
+
[32m✓[39m defineMessage[2m > [22mmessage[2m > [22mshould provide a message builder function
|
|
20
|
+
|
|
21
|
+
[2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m
|
|
22
|
+
[2m Tests [22m [1m[32m3 passed[39m[22m[90m (3)[39m
|
|
23
|
+
[2m Start at [22m 03:32:17
|
|
24
|
+
[2m Duration [22m 401ms[2m (transform 64ms, setup 0ms, collect 64ms, tests 5ms, environment 0ms, prepare 98ms)[22m
|
|
25
|
+
|
|
26
|
+
[1m[7m[32m PASS [39m[27m[22m [32mWaiting for file changes...[39m
|
|
27
|
+
[2mpress [22m[1mh[22m[2m to show help[22m[2m, [22m[2mpress [22m[1mq[22m[2m to quit[22m
|
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Gareth Andrew
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/Actor.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface Message {
|
|
2
|
+
id: string;
|
|
3
|
+
type: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function isMessageType<T extends Message>(
|
|
6
|
+
type: T["type"]
|
|
7
|
+
): (message: Message) => message is T;
|
|
8
|
+
export declare function message<T extends Message>(
|
|
9
|
+
type: T["type"]
|
|
10
|
+
): (
|
|
11
|
+
message: Omit<T, "id" | "type"> & {
|
|
12
|
+
id?: string;
|
|
13
|
+
type?: string;
|
|
14
|
+
}
|
|
15
|
+
) => T;
|
|
16
|
+
export interface ActorInterface<
|
|
17
|
+
Commands extends Message,
|
|
18
|
+
Events extends Message
|
|
19
|
+
> {
|
|
20
|
+
send(command: Commands): void;
|
|
21
|
+
connect(handler: (event: Events) => void): void;
|
|
22
|
+
disconnect(handler: (event: Events) => void): void;
|
|
23
|
+
}
|
|
24
|
+
export declare abstract class Actor<
|
|
25
|
+
Commands extends Message = never,
|
|
26
|
+
Events extends Message = never
|
|
27
|
+
> implements ActorInterface<Commands, Events>
|
|
28
|
+
{
|
|
29
|
+
private readonly emitter;
|
|
30
|
+
protected emit(event: Events): void;
|
|
31
|
+
send(_command: Commands): void;
|
|
32
|
+
connect(handler: (event: Events) => void): void;
|
|
33
|
+
disconnect(handler: (event: Events) => void): void;
|
|
34
|
+
}
|
|
35
|
+
export type CommandsType<T extends ActorInterface<Message, Message>> =
|
|
36
|
+
T extends ActorInterface<infer C, Message> ? C : never;
|
|
37
|
+
export type ActorFunction<
|
|
38
|
+
Commands extends Message = never,
|
|
39
|
+
Events extends Message = never
|
|
40
|
+
> = (emit: (event: Events) => void) => (command: Commands) => void;
|
|
41
|
+
export declare class FunctionActor<
|
|
42
|
+
Commands extends Message = never,
|
|
43
|
+
Events extends Message = never
|
|
44
|
+
> extends Actor<Commands, Events> {
|
|
45
|
+
private readonly fn;
|
|
46
|
+
constructor(fn: ActorFunction<Commands, Events>);
|
|
47
|
+
send(command: Commands): void;
|
|
48
|
+
}
|
|
49
|
+
export declare function actor<Commands extends Message, Events extends Message>(
|
|
50
|
+
fn: ActorFunction<Commands, Events>
|
|
51
|
+
): ActorInterface<Commands, Events>;
|
package/dist/Actor.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { TypedEmitter } from "./utils/TypedEmitter.js";
|
|
2
|
+
export class Actor {
|
|
3
|
+
emitter = new TypedEmitter();
|
|
4
|
+
emit(event) {
|
|
5
|
+
this.emitter.emit("event", event);
|
|
6
|
+
}
|
|
7
|
+
send(_command) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
connect(handler) {
|
|
11
|
+
this.emitter.on("event", handler);
|
|
12
|
+
}
|
|
13
|
+
disconnect(handler) {
|
|
14
|
+
this.emitter.off("event", handler);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class FunctionActor extends Actor {
|
|
18
|
+
fn;
|
|
19
|
+
constructor(fn) {
|
|
20
|
+
super();
|
|
21
|
+
this.fn = fn;
|
|
22
|
+
}
|
|
23
|
+
send(command) {
|
|
24
|
+
const emit = (event) => this.emit(event);
|
|
25
|
+
this.fn(emit)(command);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function actor(fn) {
|
|
29
|
+
return new FunctionActor(fn);
|
|
30
|
+
}
|
package/dist/actor.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Emitter } from "./emitter.js";
|
|
2
|
+
export class Actor {
|
|
3
|
+
emitter = new Emitter();
|
|
4
|
+
emit(event) {
|
|
5
|
+
this.emitter.emit(event);
|
|
6
|
+
}
|
|
7
|
+
send(_command) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
connect(handler) {
|
|
11
|
+
return this.emitter.connect(handler);
|
|
12
|
+
}
|
|
13
|
+
disconnect(handler) {
|
|
14
|
+
this.emitter.disconnect(handler);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class FunctionActor extends Actor {
|
|
18
|
+
constructor(fn) {
|
|
19
|
+
super();
|
|
20
|
+
this.send = fn(this.emit.bind(this));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function actor(fn) {
|
|
24
|
+
return new FunctionActor(fn);
|
|
25
|
+
}
|
|
26
|
+
export function actorClass(handler) {
|
|
27
|
+
return class extends FunctionActor {
|
|
28
|
+
constructor() {
|
|
29
|
+
super(handler);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
2
|
+
import { AIMessage, BaseMessage } from "@langchain/core/messages";
|
|
3
|
+
import { Actor, Message } from "../../Actor.js";
|
|
4
|
+
export declare const GENERATE_COMMAND_TYPE = "langchain.llm.generate";
|
|
5
|
+
export interface GenerateCommand extends Message {
|
|
6
|
+
type: typeof GENERATE_COMMAND_TYPE;
|
|
7
|
+
model: BaseChatModel;
|
|
8
|
+
messages: BaseMessage[];
|
|
9
|
+
}
|
|
10
|
+
export declare const generateCommand: (
|
|
11
|
+
message: Omit<GenerateCommand, "type" | "id"> & {
|
|
12
|
+
id?: string;
|
|
13
|
+
type?: string;
|
|
14
|
+
}
|
|
15
|
+
) => GenerateCommand;
|
|
16
|
+
export declare const isGenerateCommand: (
|
|
17
|
+
message: Message
|
|
18
|
+
) => message is GenerateCommand;
|
|
19
|
+
export declare const RESPONSE_EVENT_TYPE = "langchain.llm.response";
|
|
20
|
+
export interface ResponseEvent extends Message {
|
|
21
|
+
type: typeof RESPONSE_EVENT_TYPE;
|
|
22
|
+
requestId: string;
|
|
23
|
+
response: AIMessage;
|
|
24
|
+
}
|
|
25
|
+
export declare const responseEvent: (
|
|
26
|
+
message: Omit<ResponseEvent, "type" | "id"> & {
|
|
27
|
+
id?: string;
|
|
28
|
+
type?: string;
|
|
29
|
+
}
|
|
30
|
+
) => ResponseEvent;
|
|
31
|
+
export declare const isResponseEvent: (
|
|
32
|
+
message: Message
|
|
33
|
+
) => message is ResponseEvent;
|
|
34
|
+
export type LLMCommands = GenerateCommand;
|
|
35
|
+
export type LLMEvents = ResponseEvent;
|
|
36
|
+
export declare class LLMActor extends Actor<LLMCommands, LLMEvents> {
|
|
37
|
+
send(cmd: LLMCommands): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export declare const withPromptCaching: any;
|
|
40
|
+
export declare const wihMessageTruncation: any;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { AIMessage } from "@langchain/core/messages";
|
|
2
|
+
import { Actor, isMessageType, message } from "../../Actor.js";
|
|
3
|
+
import { applyCachingToMessages } from "../../llm/messageTools.js";
|
|
4
|
+
import {
|
|
5
|
+
commandTransformer,
|
|
6
|
+
eventTransformer
|
|
7
|
+
} from "../../glue/transformers.js";
|
|
8
|
+
export const GENERATE_COMMAND_TYPE = "langchain.llm.generate";
|
|
9
|
+
export const generateCommand = message(GENERATE_COMMAND_TYPE);
|
|
10
|
+
export const isGenerateCommand = isMessageType(GENERATE_COMMAND_TYPE);
|
|
11
|
+
export const RESPONSE_EVENT_TYPE = "langchain.llm.response";
|
|
12
|
+
export const responseEvent = message(RESPONSE_EVENT_TYPE);
|
|
13
|
+
export const isResponseEvent = isMessageType(RESPONSE_EVENT_TYPE);
|
|
14
|
+
export class LLMActor extends Actor {
|
|
15
|
+
async send(cmd) {
|
|
16
|
+
const msg = await cmd.model.invoke(cmd.messages);
|
|
17
|
+
this.emit({
|
|
18
|
+
type: "langchain.llm.response",
|
|
19
|
+
requestId: cmd.id,
|
|
20
|
+
response: msg
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export const withPromptCaching = commandTransformer((cmd) => {
|
|
25
|
+
if (!isGenerateCommand(cmd)) {
|
|
26
|
+
return cmd;
|
|
27
|
+
}
|
|
28
|
+
return generateCommand({
|
|
29
|
+
...cmd,
|
|
30
|
+
messages: applyCachingToMessages(cmd.messages)
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
const truncateMaxTokens = (message) => {
|
|
34
|
+
if (!isResponseEvent(message)) {
|
|
35
|
+
return message;
|
|
36
|
+
}
|
|
37
|
+
let response = message.response;
|
|
38
|
+
const stopReason = response.response_metadata["stop_reason"];
|
|
39
|
+
if (stopReason === "max_tokens") {
|
|
40
|
+
// In all cases so far, when we hit max_tokens there are multiple tool calls so we can just skip the last one and assume
|
|
41
|
+
// the agent will try again in the next message
|
|
42
|
+
response = new AIMessage({
|
|
43
|
+
content: response.content && response.content.slice(0, -1),
|
|
44
|
+
tool_calls: response.tool_calls && response.tool_calls.slice(0, -1)
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return responseEvent({
|
|
48
|
+
...message,
|
|
49
|
+
response
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
export const wihMessageTruncation = eventTransformer(truncateMaxTokens);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/effects.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
function defaultDiscriminator(m) {
|
|
2
|
+
return m === m;
|
|
3
|
+
}
|
|
4
|
+
export function take(actor, discriminator = defaultDiscriminator) {
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
const handler = (message) => {
|
|
7
|
+
if (discriminator(message)) {
|
|
8
|
+
actor.disconnect(handler);
|
|
9
|
+
resolve(message);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
actor.connect(handler);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export async function* takeEvery(actor) {
|
|
16
|
+
const eventQueue = [];
|
|
17
|
+
let resolve = null;
|
|
18
|
+
const disconnect = actor.connect((message) => {
|
|
19
|
+
if (resolve) {
|
|
20
|
+
resolve(message);
|
|
21
|
+
resolve = null;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
eventQueue.push(message);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
try {
|
|
28
|
+
while (true) {
|
|
29
|
+
if (eventQueue.length > 0) {
|
|
30
|
+
yield eventQueue.shift();
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
yield new Promise((res) => {
|
|
34
|
+
resolve = res;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
disconnect();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export async function* takeIf(actor, discriminator) {
|
|
44
|
+
for await (const event of takeEvery(actor)) {
|
|
45
|
+
if (discriminator(event)) {
|
|
46
|
+
yield event;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export async function* takeUntil(actor, discriminator, endCondition = () => true) {
|
|
51
|
+
for await (const event of takeEvery(actor)) {
|
|
52
|
+
if (endCondition(event)) {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
if (discriminator(event)) {
|
|
56
|
+
yield event;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export function on(actor, discriminator, handler) {
|
|
61
|
+
return actor.connect((message) => {
|
|
62
|
+
if (discriminator(message)) {
|
|
63
|
+
handler(message);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
package/dist/emitter.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { TypedEmitter } from "./utils/TypedEmitter.js";
|
|
2
|
+
export class Emitter {
|
|
3
|
+
emitter = new TypedEmitter();
|
|
4
|
+
emit(event) {
|
|
5
|
+
process.nextTick(() => this.emitter.emit("event", event));
|
|
6
|
+
}
|
|
7
|
+
connect(handler) {
|
|
8
|
+
this.emitter.on("event", handler);
|
|
9
|
+
return () => this.emitter.off("event", handler);
|
|
10
|
+
}
|
|
11
|
+
disconnect(handler) {
|
|
12
|
+
if (!handler) {
|
|
13
|
+
this.emitter.removeAllListeners();
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
this.emitter.off("event", handler);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const foo = "foo";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const foo = "foo";
|
package/dist/message.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { v7 as uuid } from "uuid";
|
|
2
|
+
export function isMessageType(type) {
|
|
3
|
+
return (message) => message.type === type;
|
|
4
|
+
}
|
|
5
|
+
export function message(type) {
|
|
6
|
+
return (message) => {
|
|
7
|
+
return { ...message, type, id: uuid() };
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function defineMessage(type) {
|
|
11
|
+
const factory = message(type);
|
|
12
|
+
const isMessage = isMessageType(type);
|
|
13
|
+
factory.is = isMessage;
|
|
14
|
+
factory.isMessage = isMessage;
|
|
15
|
+
factory.type = type;
|
|
16
|
+
factory.message = factory;
|
|
17
|
+
return factory;
|
|
18
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, expectTypeOf, it } from "vitest";
|
|
2
|
+
import { defineMessage } from "./message.js";
|
|
3
|
+
describe("defineMessage", () => {
|
|
4
|
+
it("should create a message definition and type guard", () => {
|
|
5
|
+
const TestMessage = defineMessage("test.type");
|
|
6
|
+
const msg = TestMessage({ data: "test" });
|
|
7
|
+
expect(msg.type).toBe("test.type");
|
|
8
|
+
expect(TestMessage.type).toBe("test.type");
|
|
9
|
+
expect(TestMessage.is(msg)).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
describe("is", () => {
|
|
12
|
+
it("should allow checking if a message is of the defined type", () => {
|
|
13
|
+
const TestMessage = defineMessage("test.type");
|
|
14
|
+
const OtherMessage = defineMessage("other.type");
|
|
15
|
+
const msg = TestMessage({ data: "test" });
|
|
16
|
+
expect(TestMessage.is(msg)).toBe(true);
|
|
17
|
+
expect(OtherMessage.is(msg)).toBe(false);
|
|
18
|
+
const unknownMsg = msg;
|
|
19
|
+
if (TestMessage.is(unknownMsg)) {
|
|
20
|
+
expectTypeOf(unknownMsg).toEqualTypeOf();
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
expectTypeOf(unknownMsg).toEqualTypeOf();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe("message", () => {
|
|
28
|
+
it("should provide a message builder function", () => {
|
|
29
|
+
const TestMessage = defineMessage("test.type");
|
|
30
|
+
const msg = TestMessage.message({ data: "test" });
|
|
31
|
+
expectTypeOf(msg).toEqualTypeOf();
|
|
32
|
+
expect(msg.type).toBe("test.type");
|
|
33
|
+
expect(msg.data).toBe("test");
|
|
34
|
+
expect(TestMessage.is(msg)).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Actor } from "../actor.js";
|
|
2
|
+
import { defineMessage } from "../message.js";
|
|
3
|
+
import { event, isConnect, isSend } from "./protocol.js";
|
|
4
|
+
export const { message: localConnect, isMessage: isLocalConnect, type: LocalConnectType } = defineMessage("local-connect");
|
|
5
|
+
export const { message: localRegister, isMessage: isLocalRegister, type: LocalRegisterType } = defineMessage("local-register");
|
|
6
|
+
export class LocalServer extends Actor {
|
|
7
|
+
store = new Map();
|
|
8
|
+
send(command) {
|
|
9
|
+
if (isSend(command)) {
|
|
10
|
+
const { destination, message } = command;
|
|
11
|
+
const actor = this.store.get(destination);
|
|
12
|
+
if (actor) {
|
|
13
|
+
actor.send(message);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
else if (isLocalConnect(command)) {
|
|
17
|
+
const { destination, callback } = command;
|
|
18
|
+
const actor = this.store.get(destination);
|
|
19
|
+
if (actor) {
|
|
20
|
+
actor.connect(callback);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
else if (isLocalRegister(command)) {
|
|
24
|
+
const { name, actor } = command;
|
|
25
|
+
this.store.set(name, actor);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export class MemoryClient extends Actor {
|
|
30
|
+
server;
|
|
31
|
+
constructor(server) {
|
|
32
|
+
super();
|
|
33
|
+
this.server = server;
|
|
34
|
+
}
|
|
35
|
+
send(command) {
|
|
36
|
+
if (isSend(command)) {
|
|
37
|
+
this.server.send(command);
|
|
38
|
+
}
|
|
39
|
+
else if (isConnect(command)) {
|
|
40
|
+
this.server.send(localConnect({
|
|
41
|
+
destination: command.destination,
|
|
42
|
+
callback: (msg) => this.emit(event({ origin: command.destination, message: msg }))
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { defineMessage } from "../message.js";
|
|
2
|
+
export const { message: send, isMessage: isSend, type: SendType } = defineMessage("send");
|
|
3
|
+
export const { message: connect, isMessage: isConnect, type: ConnectType } = defineMessage("connect");
|
|
4
|
+
export const { message: event, isMessage: isEvent, type: EventType } = defineMessage("event");
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Actor } from "../actor.js";
|
|
2
|
+
import { defineMessage } from "../message.js";
|
|
3
|
+
export const { message: sendCommand, isMessage: isSendCommand, type: SendCommandType } = defineMessage("send-command");
|
|
4
|
+
export const { message: openEvent, isMessage: isOpenEvent, type: OpenEventType } = defineMessage("open-event");
|
|
5
|
+
export const { message: closeEvent, isMessage: isCloseEvent, type: CloseEventType } = defineMessage("close-event");
|
|
6
|
+
export const { message: errorEvent, isMessage: isErrorEvent, type: ErrorEventType } = defineMessage("error-event");
|
|
7
|
+
export const { message: messageEvent, isMessage: isMessageEvent, type: MessageEventType } = defineMessage("message-event");
|
|
8
|
+
export class WebsocketClient extends Actor {
|
|
9
|
+
ws;
|
|
10
|
+
constructor(ws) {
|
|
11
|
+
super();
|
|
12
|
+
this.ws = ws;
|
|
13
|
+
ws.addEventListener("open", () => {
|
|
14
|
+
this.emit(openEvent({}));
|
|
15
|
+
});
|
|
16
|
+
ws.addEventListener("message", (event) => {
|
|
17
|
+
this.emit(messageEvent({ message: event.data }));
|
|
18
|
+
});
|
|
19
|
+
ws.addEventListener("close", () => {
|
|
20
|
+
this.emit(closeEvent({}));
|
|
21
|
+
});
|
|
22
|
+
ws.addEventListener("error", (event) => {
|
|
23
|
+
this.emit(errorEvent({ error: event }));
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
send(command) {
|
|
27
|
+
if (isSendCommand(command)) {
|
|
28
|
+
this.ws.send(command.message);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class WebsocketClientWrapper extends Actor {
|
|
33
|
+
ws;
|
|
34
|
+
client;
|
|
35
|
+
constructor(ws) {
|
|
36
|
+
super();
|
|
37
|
+
this.ws = ws;
|
|
38
|
+
this.client = new WebsocketClient(ws);
|
|
39
|
+
this.client.connect((event) => {
|
|
40
|
+
if (isMessageEvent(event)) {
|
|
41
|
+
const message = JSON.parse(event.message);
|
|
42
|
+
this.emit(message);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
send(command) {
|
|
47
|
+
this.client.send(sendCommand({ message: JSON.stringify(command) }));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export const { message: connectionEvent, isMessage: isConnectionEvent, type: ConnectionEventType } = defineMessage("connection-event");
|
|
51
|
+
export class WebsocketServer extends Actor {
|
|
52
|
+
wss;
|
|
53
|
+
constructor(wss) {
|
|
54
|
+
super();
|
|
55
|
+
this.wss = wss;
|
|
56
|
+
this.wss.on("connection", (ws) => {
|
|
57
|
+
this.emit(connectionEvent({ ws }));
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export class WebsocketServerWrapper {
|
|
62
|
+
constructor(ws, actor) {
|
|
63
|
+
//const client = new WebsocketClient(ws);
|
|
64
|
+
const wrapper = new WebsocketClientWrapper(ws);
|
|
65
|
+
wrapper.connect((command) => {
|
|
66
|
+
actor.send(command);
|
|
67
|
+
});
|
|
68
|
+
actor.connect((event) => {
|
|
69
|
+
wrapper.send(event);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
package/dist/server.js
ADDED