@earendil-works/pi-server 0.84.3 → 0.85.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/CHANGELOG.md +3 -1
- package/README.md +66 -37
- package/dist/connection.d.ts +9 -5
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js.map +1 -1
- package/dist/errors.d.ts +16 -18
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +23 -27
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/listener.d.ts +1 -3
- package/dist/listener.d.ts.map +1 -1
- package/dist/listener.js.map +1 -1
- package/dist/server.d.ts +16 -6
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +268 -86
- package/dist/server.js.map +1 -1
- package/dist/session-router.d.ts +40 -0
- package/dist/session-router.d.ts.map +1 -0
- package/dist/session-router.js +264 -0
- package/dist/session-router.js.map +1 -0
- package/dist/testing/client.d.ts +6 -2
- package/dist/testing/client.d.ts.map +1 -1
- package/dist/testing/client.js +23 -3
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/host.d.ts +57 -0
- package/dist/testing/host.d.ts.map +1 -0
- package/dist/testing/host.js +189 -0
- package/dist/testing/host.js.map +1 -0
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/server.d.ts +8 -7
- package/dist/testing/server.d.ts.map +1 -1
- package/dist/testing/server.js +7 -7
- package/dist/testing/server.js.map +1 -1
- package/dist/transports/unix/address.d.ts +3 -0
- package/dist/transports/unix/address.d.ts.map +1 -0
- package/dist/transports/unix/address.js +9 -0
- package/dist/transports/unix/address.js.map +1 -0
- package/dist/transports/unix/index.d.ts +1 -0
- package/dist/transports/unix/index.d.ts.map +1 -1
- package/dist/transports/unix/index.js +1 -0
- package/dist/transports/unix/index.js.map +1 -1
- package/dist/transports/unix/listener.d.ts +2 -3
- package/dist/transports/unix/listener.d.ts.map +1 -1
- package/dist/transports/unix/listener.js +13 -24
- package/dist/transports/unix/listener.js.map +1 -1
- package/dist/transports/unix/preset.d.ts +5 -4
- package/dist/transports/unix/preset.d.ts.map +1 -1
- package/dist/transports/unix/preset.js +5 -4
- package/dist/transports/unix/preset.js.map +1 -1
- package/dist/transports/unix/types.d.ts +2 -2
- package/dist/transports/unix/types.d.ts.map +1 -1
- package/dist/transports/unix/types.js.map +1 -1
- package/dist/types.d.ts +40 -47
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +4 -3
- package/dist/protocol.d.ts +0 -22
- package/dist/protocol.d.ts.map +0 -1
- package/dist/protocol.js +0 -266
- package/dist/protocol.js.map +0 -1
- package/dist/sessions.d.ts +0 -2174
- package/dist/sessions.d.ts.map +0 -1
- package/dist/sessions.js +0 -305
- package/dist/sessions.js.map +0 -1
- package/dist/snapshots.d.ts +0 -24
- package/dist/snapshots.d.ts.map +0 -1
- package/dist/snapshots.js +0 -39
- package/dist/snapshots.js.map +0 -1
- package/dist/testing/service.d.ts +0 -59
- package/dist/testing/service.d.ts.map +0 -1
- package/dist/testing/service.js +0 -240
- package/dist/testing/service.js.map +0 -1
package/dist/testing/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { once } from "node:events";
|
|
2
2
|
import { createConnection } from "node:net";
|
|
3
3
|
import { encodeClientMessage, PROTOCOL_VERSION, ServerMessageDecoder, } from "@earendil-works/pi-protocol";
|
|
4
|
-
import { Deferred } from "./
|
|
4
|
+
import { Deferred } from "./host.js";
|
|
5
5
|
export class ProtocolTestClient {
|
|
6
6
|
messages = [];
|
|
7
7
|
channel;
|
|
@@ -9,6 +9,7 @@ export class ProtocolTestClient {
|
|
|
9
9
|
waiters = new Set();
|
|
10
10
|
closedDeferred = new Deferred();
|
|
11
11
|
requestSequence = 0;
|
|
12
|
+
attachment;
|
|
12
13
|
closedValue = false;
|
|
13
14
|
constructor(channel) {
|
|
14
15
|
this.channel = channel;
|
|
@@ -21,11 +22,21 @@ export class ProtocolTestClient {
|
|
|
21
22
|
void this.sendMessage({ type: "hello", version });
|
|
22
23
|
return response;
|
|
23
24
|
}
|
|
24
|
-
async
|
|
25
|
+
async requestService(target, call, id = `request-${++this.requestSequence}`) {
|
|
25
26
|
const response = this.next((message) => message.type === "response" && message.id === id);
|
|
26
|
-
await this.sendMessage({ type: "request", id,
|
|
27
|
+
await this.sendMessage({ type: "request", id, target, call: call });
|
|
27
28
|
return (await response);
|
|
28
29
|
}
|
|
30
|
+
attach(serverId, sessionId) {
|
|
31
|
+
return this.requestService({ serverId }, { serviceId: "pi.session-management", member: "attach", args: [sessionId] });
|
|
32
|
+
}
|
|
33
|
+
requestSessionService(serverId, sessionId, call, id) {
|
|
34
|
+
const attachment = this.attachment;
|
|
35
|
+
const target = attachment === undefined || attachment.sessionId !== sessionId
|
|
36
|
+
? { serverId, sessionId, attachmentId: "missing-attachment" }
|
|
37
|
+
: { serverId, ...attachment };
|
|
38
|
+
return this.requestService(target, call, id);
|
|
39
|
+
}
|
|
29
40
|
sendMessage(message) {
|
|
30
41
|
return this.channel.send(encodeClientMessage(message));
|
|
31
42
|
}
|
|
@@ -55,6 +66,15 @@ export class ProtocolTestClient {
|
|
|
55
66
|
receive(chunk) {
|
|
56
67
|
try {
|
|
57
68
|
for (const message of this.decoder.push(chunk)) {
|
|
69
|
+
if (message.type === "attachment") {
|
|
70
|
+
this.attachment =
|
|
71
|
+
message.attachment === null
|
|
72
|
+
? undefined
|
|
73
|
+
: {
|
|
74
|
+
sessionId: message.attachment.sessionId,
|
|
75
|
+
attachmentId: message.attachment.attachmentId,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
58
78
|
this.messages.push(message);
|
|
59
79
|
for (const waiter of this.waiters) {
|
|
60
80
|
if (!waiter.predicate(message))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/testing/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { once } from \"node:events\";\nimport { createConnection, type Socket } from \"node:net\";\nimport {\n\ttype ClientMessage,\n\ttype Command,\n\tencodeClientMessage,\n\tPROTOCOL_VERSION,\n\ttype ResponseEnvelope,\n\ttype ServerMessage,\n\tServerMessageDecoder,\n} from \"@earendil-works/pi-protocol\";\nimport { Deferred } from \"./service.ts\";\n\ninterface MessageWaiter {\n\tpredicate: (message: ServerMessage) => boolean;\n\tresolve: (message: ServerMessage) => void;\n\treject: (error: Error) => void;\n}\n\nexport interface WireChannel {\n\tsend(chunk: Uint8Array): Promise<void>;\n\tsendFragmented(chunk: Uint8Array, splitAt: number): Promise<void>;\n\tclose(): Promise<void>;\n}\n\nexport class ProtocolTestClient {\n\treadonly messages: ServerMessage[] = [];\n\tprivate readonly channel: WireChannel;\n\tprivate readonly decoder = new ServerMessageDecoder();\n\tprivate readonly waiters = new Set<MessageWaiter>();\n\tprivate readonly closedDeferred = new Deferred<void>();\n\tprivate requestSequence = 0;\n\tprivate closedValue = false;\n\n\tconstructor(channel: WireChannel) {\n\t\tthis.channel = channel;\n\t}\n\n\tget closed(): boolean {\n\t\treturn this.closedValue;\n\t}\n\n\thello(version: number = PROTOCOL_VERSION): Promise<ServerMessage> {\n\t\tconst response = this.next((message) => message.type === \"hello\" || message.type === \"hello_error\");\n\t\tvoid this.sendMessage({ type: \"hello\", version });\n\t\treturn response;\n\t}\n\n\tasync request(command: Command, id = `request-${++this.requestSequence}`): Promise<ResponseEnvelope> {\n\t\tconst response = this.next(\n\t\t\t(message): message is ResponseEnvelope => message.type === \"response\" && message.id === id,\n\t\t);\n\t\tawait this.sendMessage({ type: \"request\", id, request: command });\n\t\treturn (await response) as ResponseEnvelope;\n\t}\n\n\tsendMessage(message: ClientMessage): Promise<void> {\n\t\treturn this.channel.send(encodeClientMessage(message));\n\t}\n\n\tsendBytes(chunk: Uint8Array): Promise<void> {\n\t\treturn this.channel.send(chunk);\n\t}\n\n\tsendFragmentedMessage(message: ClientMessage, splitAt: number): Promise<void> {\n\t\treturn this.channel.sendFragmented(encodeClientMessage(message), splitAt);\n\t}\n\n\tnext(predicate: (message: ServerMessage) => boolean): Promise<ServerMessage> {\n\t\treturn this.nextFrom(0, predicate);\n\t}\n\n\tnextFrom(index: number, predicate: (message: ServerMessage) => boolean): Promise<ServerMessage> {\n\t\tconst existing = this.messages.slice(index).find(predicate);\n\t\tif (existing) return Promise.resolve(existing);\n\t\tif (this.closedValue) return Promise.reject(new Error(\"Wire client is closed\"));\n\t\treturn new Promise((resolve, reject) => this.waiters.add({ predicate, resolve, reject }));\n\t}\n\n\twaitForClose(): Promise<void> {\n\t\treturn this.closedValue ? Promise.resolve() : this.closedDeferred.promise;\n\t}\n\n\tclose(): Promise<void> {\n\t\treturn this.channel.close();\n\t}\n\n\treceive(chunk: Uint8Array): void {\n\t\ttry {\n\t\t\tfor (const message of this.decoder.push(chunk)) {\n\t\t\t\tthis.messages.push(message);\n\t\t\t\tfor (const waiter of this.waiters) {\n\t\t\t\t\tif (!waiter.predicate(message)) continue;\n\t\t\t\t\tthis.waiters.delete(waiter);\n\t\t\t\t\twaiter.resolve(message);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.fail(error instanceof Error ? error : new Error(String(error)));\n\t\t}\n\t}\n\n\tmarkClosed(): void {\n\t\tif (this.closedValue) return;\n\t\tthis.closedValue = true;\n\t\tthis.closedDeferred.resolve(undefined);\n\t\tthis.fail(new Error(\"Wire connection closed\"));\n\t}\n\n\tfail(error: Error): void {\n\t\tfor (const waiter of this.waiters) waiter.reject(error);\n\t\tthis.waiters.clear();\n\t}\n}\n\nexport async function connectUnixTestClient(path: string): Promise<ProtocolTestClient> {\n\tconst socket = createConnection(path);\n\tawait once(socket, \"connect\");\n\tconst client = new ProtocolTestClient({\n\t\tsend: (chunk) => writeSocket(socket, chunk),\n\t\tasync sendFragmented(chunk, splitAt) {\n\t\t\tawait writeSocket(socket, chunk.subarray(0, splitAt));\n\t\t\tawait writeSocket(socket, chunk.subarray(splitAt));\n\t\t},\n\t\tasync close() {\n\t\t\tif (socket.destroyed) return;\n\t\t\tconst closed = once(socket, \"close\");\n\t\t\tsocket.destroy();\n\t\t\tawait closed;\n\t\t},\n\t});\n\tsocket.on(\"data\", (chunk) => {\n\t\tclient.receive(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));\n\t});\n\tsocket.on(\"error\", (error) => client.fail(error));\n\tsocket.once(\"close\", () => client.markClosed());\n\treturn client;\n}\n\nfunction writeSocket(socket: Socket, chunk: Uint8Array): Promise<void> {\n\treturn new Promise<void>((resolve, reject) => {\n\t\tsocket.write(chunk, (error) => {\n\t\t\tif (error) reject(error);\n\t\t\telse resolve();\n\t\t});\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/testing/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { once } from \"node:events\";\nimport { createConnection, type Socket } from \"node:net\";\nimport type { JsonValue, ServiceCall } from \"@earendil-works/chord\";\nimport {\n\ttype ClientMessage,\n\tencodeClientMessage,\n\tPROTOCOL_VERSION,\n\ttype ResponseEnvelope,\n\ttype RpcTarget,\n\ttype ServerMessage,\n\tServerMessageDecoder,\n} from \"@earendil-works/pi-protocol\";\nimport { Deferred } from \"./host.ts\";\n\ninterface MessageWaiter {\n\tpredicate: (message: ServerMessage) => boolean;\n\tresolve: (message: ServerMessage) => void;\n\treject: (error: Error) => void;\n}\n\nexport interface WireChannel {\n\tsend(chunk: Uint8Array): Promise<void>;\n\tsendFragmented(chunk: Uint8Array, splitAt: number): Promise<void>;\n\tclose(): Promise<void>;\n}\n\nexport class ProtocolTestClient {\n\treadonly messages: ServerMessage[] = [];\n\tprivate readonly channel: WireChannel;\n\tprivate readonly decoder = new ServerMessageDecoder();\n\tprivate readonly waiters = new Set<MessageWaiter>();\n\tprivate readonly closedDeferred = new Deferred<void>();\n\tprivate requestSequence = 0;\n\tprivate attachment: { sessionId: string; attachmentId: string } | undefined;\n\tprivate closedValue = false;\n\n\tconstructor(channel: WireChannel) {\n\t\tthis.channel = channel;\n\t}\n\n\tget closed(): boolean {\n\t\treturn this.closedValue;\n\t}\n\n\thello(version: number = PROTOCOL_VERSION): Promise<ServerMessage> {\n\t\tconst response = this.next((message) => message.type === \"hello\" || message.type === \"hello_error\");\n\t\tvoid this.sendMessage({ type: \"hello\", version });\n\t\treturn response;\n\t}\n\n\tasync requestService(\n\t\ttarget: RpcTarget,\n\t\tcall: ServiceCall,\n\t\tid = `request-${++this.requestSequence}`,\n\t): Promise<ResponseEnvelope> {\n\t\tconst response = this.next(\n\t\t\t(message): message is ResponseEnvelope => message.type === \"response\" && message.id === id,\n\t\t);\n\t\tawait this.sendMessage({ type: \"request\", id, target, call: call as unknown as JsonValue });\n\t\treturn (await response) as ResponseEnvelope;\n\t}\n\n\tattach(serverId: string, sessionId: string): Promise<ResponseEnvelope> {\n\t\treturn this.requestService(\n\t\t\t{ serverId },\n\t\t\t{ serviceId: \"pi.session-management\", member: \"attach\", args: [sessionId] },\n\t\t);\n\t}\n\n\trequestSessionService(\n\t\tserverId: string,\n\t\tsessionId: string,\n\t\tcall: ServiceCall,\n\t\tid?: string,\n\t): Promise<ResponseEnvelope> {\n\t\tconst attachment = this.attachment;\n\t\tconst target: RpcTarget =\n\t\t\tattachment === undefined || attachment.sessionId !== sessionId\n\t\t\t\t? { serverId, sessionId, attachmentId: \"missing-attachment\" }\n\t\t\t\t: { serverId, ...attachment };\n\t\treturn this.requestService(target, call, id);\n\t}\n\n\tsendMessage(message: ClientMessage): Promise<void> {\n\t\treturn this.channel.send(encodeClientMessage(message));\n\t}\n\n\tsendBytes(chunk: Uint8Array): Promise<void> {\n\t\treturn this.channel.send(chunk);\n\t}\n\n\tsendFragmentedMessage(message: ClientMessage, splitAt: number): Promise<void> {\n\t\treturn this.channel.sendFragmented(encodeClientMessage(message), splitAt);\n\t}\n\n\tnext(predicate: (message: ServerMessage) => boolean): Promise<ServerMessage> {\n\t\treturn this.nextFrom(0, predicate);\n\t}\n\n\tnextFrom(index: number, predicate: (message: ServerMessage) => boolean): Promise<ServerMessage> {\n\t\tconst existing = this.messages.slice(index).find(predicate);\n\t\tif (existing) return Promise.resolve(existing);\n\t\tif (this.closedValue) return Promise.reject(new Error(\"Wire client is closed\"));\n\t\treturn new Promise((resolve, reject) => this.waiters.add({ predicate, resolve, reject }));\n\t}\n\n\twaitForClose(): Promise<void> {\n\t\treturn this.closedValue ? Promise.resolve() : this.closedDeferred.promise;\n\t}\n\n\tclose(): Promise<void> {\n\t\treturn this.channel.close();\n\t}\n\n\treceive(chunk: Uint8Array): void {\n\t\ttry {\n\t\t\tfor (const message of this.decoder.push(chunk)) {\n\t\t\t\tif (message.type === \"attachment\") {\n\t\t\t\t\tthis.attachment =\n\t\t\t\t\t\tmessage.attachment === null\n\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\tsessionId: message.attachment.sessionId,\n\t\t\t\t\t\t\t\t\tattachmentId: message.attachment.attachmentId,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tthis.messages.push(message);\n\t\t\t\tfor (const waiter of this.waiters) {\n\t\t\t\t\tif (!waiter.predicate(message)) continue;\n\t\t\t\t\tthis.waiters.delete(waiter);\n\t\t\t\t\twaiter.resolve(message);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.fail(error instanceof Error ? error : new Error(String(error)));\n\t\t}\n\t}\n\n\tmarkClosed(): void {\n\t\tif (this.closedValue) return;\n\t\tthis.closedValue = true;\n\t\tthis.closedDeferred.resolve(undefined);\n\t\tthis.fail(new Error(\"Wire connection closed\"));\n\t}\n\n\tfail(error: Error): void {\n\t\tfor (const waiter of this.waiters) waiter.reject(error);\n\t\tthis.waiters.clear();\n\t}\n}\n\nexport async function connectUnixTestClient(path: string): Promise<ProtocolTestClient> {\n\tconst socket = createConnection(path);\n\tawait once(socket, \"connect\");\n\tconst client = new ProtocolTestClient({\n\t\tsend: (chunk) => writeSocket(socket, chunk),\n\t\tasync sendFragmented(chunk, splitAt) {\n\t\t\tawait writeSocket(socket, chunk.subarray(0, splitAt));\n\t\t\tawait writeSocket(socket, chunk.subarray(splitAt));\n\t\t},\n\t\tasync close() {\n\t\t\tif (socket.destroyed) return;\n\t\t\tconst closed = once(socket, \"close\");\n\t\t\tsocket.destroy();\n\t\t\tawait closed;\n\t\t},\n\t});\n\tsocket.on(\"data\", (chunk) => {\n\t\tclient.receive(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));\n\t});\n\tsocket.on(\"error\", (error) => client.fail(error));\n\tsocket.once(\"close\", () => client.markClosed());\n\treturn client;\n}\n\nfunction writeSocket(socket: Socket, chunk: Uint8Array): Promise<void> {\n\treturn new Promise<void>((resolve, reject) => {\n\t\tsocket.write(chunk, (error) => {\n\t\t\tif (error) reject(error);\n\t\t\telse resolve();\n\t\t});\n\t});\n}\n"]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { JsonValue, ServiceCall } from "@earendil-works/chord";
|
|
2
|
+
import type { Context, Session, SessionMetadata } from "@earendil-works/pi-agent-core";
|
|
3
|
+
import { MemorySessionRepo } from "@earendil-works/pi-agent-core";
|
|
4
|
+
import type { RoutedServerServiceHost, RoutedSessionHandle, ServerHost } from "../types.ts";
|
|
5
|
+
export declare class Deferred<T> {
|
|
6
|
+
readonly promise: Promise<T>;
|
|
7
|
+
private resolvePromise;
|
|
8
|
+
constructor();
|
|
9
|
+
resolve(value: T): void;
|
|
10
|
+
}
|
|
11
|
+
interface OpenGate {
|
|
12
|
+
entered: Deferred<void>;
|
|
13
|
+
release: Deferred<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare class TestHarness {
|
|
16
|
+
#private;
|
|
17
|
+
readonly session: Session;
|
|
18
|
+
readonly closed: Deferred<void>;
|
|
19
|
+
readonly terminated: Promise<Error | undefined>;
|
|
20
|
+
attachedClients: number;
|
|
21
|
+
attachmentReleaseCount: number;
|
|
22
|
+
closeCount: number;
|
|
23
|
+
readonly serviceCalls: ServiceCall[];
|
|
24
|
+
failAttachmentRelease?: Error;
|
|
25
|
+
failClose?: Error;
|
|
26
|
+
nextServiceError?: Error;
|
|
27
|
+
nextServiceResult: JsonValue | undefined;
|
|
28
|
+
private nextCloseGate?;
|
|
29
|
+
private nextServiceGate?;
|
|
30
|
+
constructor(session: Session);
|
|
31
|
+
attachClient(_context: Context): {
|
|
32
|
+
invokeService: TestHarness["invokeService"];
|
|
33
|
+
release(context: Context): void;
|
|
34
|
+
};
|
|
35
|
+
invokeService(call: ServiceCall): Promise<JsonValue | undefined>;
|
|
36
|
+
close(context: Context): Promise<void>;
|
|
37
|
+
terminate(error: Error): Promise<void>;
|
|
38
|
+
gateNextClose(): OpenGate;
|
|
39
|
+
gateNextServiceCall(): OpenGate;
|
|
40
|
+
}
|
|
41
|
+
export declare function createTestServerServices(): RoutedServerServiceHost;
|
|
42
|
+
export declare class TestServerHost implements ServerHost {
|
|
43
|
+
readonly serverServices: RoutedServerServiceHost;
|
|
44
|
+
readonly repo: MemorySessionRepo;
|
|
45
|
+
readonly harnesses: Map<string, TestHarness[]>;
|
|
46
|
+
openSessionCount: number;
|
|
47
|
+
nextOpenSessionError?: Error;
|
|
48
|
+
nextHarnessCloseError?: Error;
|
|
49
|
+
private nextOpenSessionGate?;
|
|
50
|
+
resolveSession(sessionId: string, context: Context): Promise<SessionMetadata>;
|
|
51
|
+
openSession(metadata: SessionMetadata, context: Context): Promise<RoutedSessionHandle>;
|
|
52
|
+
seed(id?: string, parentSessionId?: string): Promise<SessionMetadata>;
|
|
53
|
+
gateNextOpenSession(): OpenGate;
|
|
54
|
+
latestHarness(id: string): TestHarness;
|
|
55
|
+
}
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/testing/host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAsB,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAEtF,OAAO,KAAK,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE5F,qBAAa,QAAQ,CAAC,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAC7B,OAAO,CAAC,cAAc,CAAsB;IAE5C,cAIC;IAED,OAAO,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAEtB;CACD;AAED,UAAU,QAAQ;IACjB,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,qBAAa,WAAW;;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,iBAAwB;IAEvC,QAAQ,CAAC,UAAU,6BAA6B;IAChD,eAAe,SAAK;IACpB,sBAAsB,SAAK;IAC3B,UAAU,SAAK;IACf,QAAQ,CAAC,YAAY,EAAE,WAAW,EAAE,CAAM;IAC1C,qBAAqB,CAAC,EAAE,KAAK,CAAC;IAC9B,SAAS,CAAC,EAAE,KAAK,CAAC;IAClB,gBAAgB,CAAC,EAAE,KAAK,CAAC;IACzB,iBAAiB,EAAE,SAAS,GAAG,SAAS,CAAgB;IACxD,OAAO,CAAC,aAAa,CAAC,CAAW;IACjC,OAAO,CAAC,eAAe,CAAC,CAAW;IAEnC,YAAY,OAAO,EAAE,OAAO,EAE3B;IAED,YAAY,CAAC,QAAQ,EAAE,OAAO,GAAG;QAChC,aAAa,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC;QAC5C,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;KAChC,CAaA;IAEK,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAgBrE;IAEK,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB3C;IAEK,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAG3C;IAED,aAAa,IAAI,QAAQ,CAIxB;IAED,mBAAmB,IAAI,QAAQ,CAI9B;CACD;AAED,wBAAgB,wBAAwB,IAAI,uBAAuB,CA8BlE;AAED,qBAAa,cAAe,YAAW,UAAU;IAChD,QAAQ,CAAC,cAAc,0BAA8B;IACrD,QAAQ,CAAC,IAAI,oBAA2C;IACxD,QAAQ,CAAC,SAAS,6BAAoC;IACtD,gBAAgB,SAAK;IACrB,oBAAoB,CAAC,EAAE,KAAK,CAAC;IAC7B,qBAAqB,CAAC,EAAE,KAAK,CAAC;IAC9B,OAAO,CAAC,mBAAmB,CAAC,CAAW;IAEjC,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAKlF;IAEK,WAAW,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA4B3F;IAEK,IAAI,CAAC,EAAE,SAAc,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAK/E;IAED,mBAAmB,IAAI,QAAQ,CAI9B;IAED,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,CAIrC;CACD","sourcesContent":["import type { JsonValue, ServiceCall } from \"@earendil-works/chord\";\nimport type { Context, Session, SessionMetadata } from \"@earendil-works/pi-agent-core\";\nimport { BACKGROUND_CONTEXT, MemorySessionRepo } from \"@earendil-works/pi-agent-core\";\nimport { SessionAmbiguousError, SessionNotFoundError } from \"../errors.ts\";\nimport type { RoutedServerServiceHost, RoutedSessionHandle, ServerHost } from \"../types.ts\";\n\nexport class Deferred<T> {\n\treadonly promise: Promise<T>;\n\tprivate resolvePromise!: (value: T) => void;\n\n\tconstructor() {\n\t\tthis.promise = new Promise<T>((resolve) => {\n\t\t\tthis.resolvePromise = resolve;\n\t\t});\n\t}\n\n\tresolve(value: T): void {\n\t\tthis.resolvePromise(value);\n\t}\n}\n\ninterface OpenGate {\n\tentered: Deferred<void>;\n\trelease: Deferred<void>;\n}\n\nexport class TestHarness {\n\treadonly session: Session;\n\treadonly closed = new Deferred<void>();\n\treadonly #termination = new Deferred<Error | undefined>();\n\treadonly terminated = this.#termination.promise;\n\tattachedClients = 0;\n\tattachmentReleaseCount = 0;\n\tcloseCount = 0;\n\treadonly serviceCalls: ServiceCall[] = [];\n\tfailAttachmentRelease?: Error;\n\tfailClose?: Error;\n\tnextServiceError?: Error;\n\tnextServiceResult: JsonValue | undefined = { ok: true };\n\tprivate nextCloseGate?: OpenGate;\n\tprivate nextServiceGate?: OpenGate;\n\n\tconstructor(session: Session) {\n\t\tthis.session = session;\n\t}\n\n\tattachClient(_context: Context): {\n\t\tinvokeService: TestHarness[\"invokeService\"];\n\t\trelease(context: Context): void;\n\t} {\n\t\tthis.attachedClients += 1;\n\t\tlet released = false;\n\t\treturn {\n\t\t\tinvokeService: (call) => this.invokeService(call),\n\t\t\trelease: (_context) => {\n\t\t\t\tif (released) return;\n\t\t\t\tthis.attachmentReleaseCount += 1;\n\t\t\t\tif (this.failAttachmentRelease) throw this.failAttachmentRelease;\n\t\t\t\treleased = true;\n\t\t\t\tthis.attachedClients -= 1;\n\t\t\t},\n\t\t};\n\t}\n\n\tasync invokeService(call: ServiceCall): Promise<JsonValue | undefined> {\n\t\tthis.serviceCalls.push(call);\n\t\tif (this.nextServiceError) {\n\t\t\tconst error = this.nextServiceError;\n\t\t\tthis.nextServiceError = undefined;\n\t\t\tthrow error;\n\t\t}\n\t\tconst gate = this.nextServiceGate;\n\t\tif (gate) {\n\t\t\tthis.nextServiceGate = undefined;\n\t\t\tgate.entered.resolve(undefined);\n\t\t\tawait gate.release.promise;\n\t\t}\n\t\tconst result = this.nextServiceResult;\n\t\tthis.nextServiceResult = { ok: true };\n\t\treturn result;\n\t}\n\n\tasync close(context: Context): Promise<void> {\n\t\tthis.closeCount += 1;\n\t\tconst gate = this.nextCloseGate;\n\t\tif (gate) {\n\t\t\tthis.nextCloseGate = undefined;\n\t\t\tgate.entered.resolve(undefined);\n\t\t\tawait gate.release.promise;\n\t\t}\n\t\tif (this.failClose) {\n\t\t\tconst error = this.failClose;\n\t\t\tthis.failClose = undefined;\n\t\t\tthrow error;\n\t\t}\n\t\tawait this.session.close(context);\n\t\tthis.closed.resolve(undefined);\n\t\tthis.#termination.resolve(undefined);\n\t}\n\n\tasync terminate(error: Error): Promise<void> {\n\t\tawait this.session.close(BACKGROUND_CONTEXT);\n\t\tthis.#termination.resolve(error);\n\t}\n\n\tgateNextClose(): OpenGate {\n\t\tconst gate = { entered: new Deferred<void>(), release: new Deferred<void>() };\n\t\tthis.nextCloseGate = gate;\n\t\treturn gate;\n\t}\n\n\tgateNextServiceCall(): OpenGate {\n\t\tconst gate = { entered: new Deferred<void>(), release: new Deferred<void>() };\n\t\tthis.nextServiceGate = gate;\n\t\treturn gate;\n\t}\n}\n\nexport function createTestServerServices(): RoutedServerServiceHost {\n\treturn {\n\t\tattachClient(presentation) {\n\t\t\treturn {\n\t\t\t\tasync invokeService(call, _publish, context) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tcall.instance === undefined &&\n\t\t\t\t\t\tcall.serviceId === \"pi.session-management\" &&\n\t\t\t\t\t\tcall.member === \"attach\" &&\n\t\t\t\t\t\tcall.args.length === 1 &&\n\t\t\t\t\t\ttypeof call.args[0] === \"string\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tawait presentation.attachSession(call.args[0], context);\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\tcall.instance === undefined &&\n\t\t\t\t\t\tcall.serviceId === \"pi.session-management\" &&\n\t\t\t\t\t\tcall.member === \"detach\" &&\n\t\t\t\t\t\tcall.args.length === 0\n\t\t\t\t\t) {\n\t\t\t\t\t\tawait presentation.detachSession(context);\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tthrow new Error(`Unsupported test server service ${call.serviceId}.${call.member}`);\n\t\t\t\t},\n\t\t\t\trelease() {},\n\t\t\t};\n\t\t},\n\t};\n}\n\nexport class TestServerHost implements ServerHost {\n\treadonly serverServices = createTestServerServices();\n\treadonly repo = new MemorySessionRepo({ now: () => 1 });\n\treadonly harnesses = new Map<string, TestHarness[]>();\n\topenSessionCount = 0;\n\tnextOpenSessionError?: Error;\n\tnextHarnessCloseError?: Error;\n\tprivate nextOpenSessionGate?: OpenGate;\n\n\tasync resolveSession(sessionId: string, context: Context): Promise<SessionMetadata> {\n\t\tconst matches = (await this.repo.list(undefined, context)).filter(({ id }) => id === sessionId);\n\t\tif (matches.length === 0) throw new SessionNotFoundError(`Unknown session: ${sessionId}`);\n\t\tif (matches.length > 1) throw new SessionAmbiguousError();\n\t\treturn matches[0]!;\n\t}\n\n\tasync openSession(metadata: SessionMetadata, context: Context): Promise<RoutedSessionHandle> {\n\t\tthis.openSessionCount += 1;\n\t\tconst gate = this.nextOpenSessionGate;\n\t\tif (gate) {\n\t\t\tthis.nextOpenSessionGate = undefined;\n\t\t\tgate.entered.resolve(undefined);\n\t\t\tawait gate.release.promise;\n\t\t}\n\t\tconst session = await this.repo.open(metadata, context);\n\t\ttry {\n\t\t\tif (this.nextOpenSessionError) {\n\t\t\t\tconst error = this.nextOpenSessionError;\n\t\t\t\tthis.nextOpenSessionError = undefined;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tconst harness = new TestHarness(session);\n\t\t\tif (this.nextHarnessCloseError) {\n\t\t\t\tharness.failClose = this.nextHarnessCloseError;\n\t\t\t\tthis.nextHarnessCloseError = undefined;\n\t\t\t}\n\t\t\tconst harnesses = this.harnesses.get(metadata.id) ?? [];\n\t\t\tharnesses.push(harness);\n\t\t\tthis.harnesses.set(metadata.id, harnesses);\n\t\t\treturn harness;\n\t\t} catch (error) {\n\t\t\tawait session.close(context);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync seed(id = \"session-1\", parentSessionId?: string): Promise<SessionMetadata> {\n\t\tconst session = await this.repo.create({ id, parentSessionId }, BACKGROUND_CONTEXT);\n\t\tconst metadata = session.metadata;\n\t\tawait session.close(BACKGROUND_CONTEXT);\n\t\treturn metadata;\n\t}\n\n\tgateNextOpenSession(): OpenGate {\n\t\tconst gate = { entered: new Deferred<void>(), release: new Deferred<void>() };\n\t\tthis.nextOpenSessionGate = gate;\n\t\treturn gate;\n\t}\n\n\tlatestHarness(id: string): TestHarness {\n\t\tconst harnesses = this.harnesses.get(id);\n\t\tif (!harnesses?.length) throw new Error(`No harness for ${id}`);\n\t\treturn harnesses.at(-1)!;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { BACKGROUND_CONTEXT, MemorySessionRepo } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { SessionAmbiguousError, SessionNotFoundError } from "../errors.js";
|
|
3
|
+
export class Deferred {
|
|
4
|
+
promise;
|
|
5
|
+
resolvePromise;
|
|
6
|
+
constructor() {
|
|
7
|
+
this.promise = new Promise((resolve) => {
|
|
8
|
+
this.resolvePromise = resolve;
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
resolve(value) {
|
|
12
|
+
this.resolvePromise(value);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export class TestHarness {
|
|
16
|
+
session;
|
|
17
|
+
closed = new Deferred();
|
|
18
|
+
#termination = new Deferred();
|
|
19
|
+
terminated = this.#termination.promise;
|
|
20
|
+
attachedClients = 0;
|
|
21
|
+
attachmentReleaseCount = 0;
|
|
22
|
+
closeCount = 0;
|
|
23
|
+
serviceCalls = [];
|
|
24
|
+
failAttachmentRelease;
|
|
25
|
+
failClose;
|
|
26
|
+
nextServiceError;
|
|
27
|
+
nextServiceResult = { ok: true };
|
|
28
|
+
nextCloseGate;
|
|
29
|
+
nextServiceGate;
|
|
30
|
+
constructor(session) {
|
|
31
|
+
this.session = session;
|
|
32
|
+
}
|
|
33
|
+
attachClient(_context) {
|
|
34
|
+
this.attachedClients += 1;
|
|
35
|
+
let released = false;
|
|
36
|
+
return {
|
|
37
|
+
invokeService: (call) => this.invokeService(call),
|
|
38
|
+
release: (_context) => {
|
|
39
|
+
if (released)
|
|
40
|
+
return;
|
|
41
|
+
this.attachmentReleaseCount += 1;
|
|
42
|
+
if (this.failAttachmentRelease)
|
|
43
|
+
throw this.failAttachmentRelease;
|
|
44
|
+
released = true;
|
|
45
|
+
this.attachedClients -= 1;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async invokeService(call) {
|
|
50
|
+
this.serviceCalls.push(call);
|
|
51
|
+
if (this.nextServiceError) {
|
|
52
|
+
const error = this.nextServiceError;
|
|
53
|
+
this.nextServiceError = undefined;
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
const gate = this.nextServiceGate;
|
|
57
|
+
if (gate) {
|
|
58
|
+
this.nextServiceGate = undefined;
|
|
59
|
+
gate.entered.resolve(undefined);
|
|
60
|
+
await gate.release.promise;
|
|
61
|
+
}
|
|
62
|
+
const result = this.nextServiceResult;
|
|
63
|
+
this.nextServiceResult = { ok: true };
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
async close(context) {
|
|
67
|
+
this.closeCount += 1;
|
|
68
|
+
const gate = this.nextCloseGate;
|
|
69
|
+
if (gate) {
|
|
70
|
+
this.nextCloseGate = undefined;
|
|
71
|
+
gate.entered.resolve(undefined);
|
|
72
|
+
await gate.release.promise;
|
|
73
|
+
}
|
|
74
|
+
if (this.failClose) {
|
|
75
|
+
const error = this.failClose;
|
|
76
|
+
this.failClose = undefined;
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
await this.session.close(context);
|
|
80
|
+
this.closed.resolve(undefined);
|
|
81
|
+
this.#termination.resolve(undefined);
|
|
82
|
+
}
|
|
83
|
+
async terminate(error) {
|
|
84
|
+
await this.session.close(BACKGROUND_CONTEXT);
|
|
85
|
+
this.#termination.resolve(error);
|
|
86
|
+
}
|
|
87
|
+
gateNextClose() {
|
|
88
|
+
const gate = { entered: new Deferred(), release: new Deferred() };
|
|
89
|
+
this.nextCloseGate = gate;
|
|
90
|
+
return gate;
|
|
91
|
+
}
|
|
92
|
+
gateNextServiceCall() {
|
|
93
|
+
const gate = { entered: new Deferred(), release: new Deferred() };
|
|
94
|
+
this.nextServiceGate = gate;
|
|
95
|
+
return gate;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export function createTestServerServices() {
|
|
99
|
+
return {
|
|
100
|
+
attachClient(presentation) {
|
|
101
|
+
return {
|
|
102
|
+
async invokeService(call, _publish, context) {
|
|
103
|
+
if (call.instance === undefined &&
|
|
104
|
+
call.serviceId === "pi.session-management" &&
|
|
105
|
+
call.member === "attach" &&
|
|
106
|
+
call.args.length === 1 &&
|
|
107
|
+
typeof call.args[0] === "string") {
|
|
108
|
+
await presentation.attachSession(call.args[0], context);
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
if (call.instance === undefined &&
|
|
112
|
+
call.serviceId === "pi.session-management" &&
|
|
113
|
+
call.member === "detach" &&
|
|
114
|
+
call.args.length === 0) {
|
|
115
|
+
await presentation.detachSession(context);
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
throw new Error(`Unsupported test server service ${call.serviceId}.${call.member}`);
|
|
119
|
+
},
|
|
120
|
+
release() { },
|
|
121
|
+
};
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
export class TestServerHost {
|
|
126
|
+
serverServices = createTestServerServices();
|
|
127
|
+
repo = new MemorySessionRepo({ now: () => 1 });
|
|
128
|
+
harnesses = new Map();
|
|
129
|
+
openSessionCount = 0;
|
|
130
|
+
nextOpenSessionError;
|
|
131
|
+
nextHarnessCloseError;
|
|
132
|
+
nextOpenSessionGate;
|
|
133
|
+
async resolveSession(sessionId, context) {
|
|
134
|
+
const matches = (await this.repo.list(undefined, context)).filter(({ id }) => id === sessionId);
|
|
135
|
+
if (matches.length === 0)
|
|
136
|
+
throw new SessionNotFoundError(`Unknown session: ${sessionId}`);
|
|
137
|
+
if (matches.length > 1)
|
|
138
|
+
throw new SessionAmbiguousError();
|
|
139
|
+
return matches[0];
|
|
140
|
+
}
|
|
141
|
+
async openSession(metadata, context) {
|
|
142
|
+
this.openSessionCount += 1;
|
|
143
|
+
const gate = this.nextOpenSessionGate;
|
|
144
|
+
if (gate) {
|
|
145
|
+
this.nextOpenSessionGate = undefined;
|
|
146
|
+
gate.entered.resolve(undefined);
|
|
147
|
+
await gate.release.promise;
|
|
148
|
+
}
|
|
149
|
+
const session = await this.repo.open(metadata, context);
|
|
150
|
+
try {
|
|
151
|
+
if (this.nextOpenSessionError) {
|
|
152
|
+
const error = this.nextOpenSessionError;
|
|
153
|
+
this.nextOpenSessionError = undefined;
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
const harness = new TestHarness(session);
|
|
157
|
+
if (this.nextHarnessCloseError) {
|
|
158
|
+
harness.failClose = this.nextHarnessCloseError;
|
|
159
|
+
this.nextHarnessCloseError = undefined;
|
|
160
|
+
}
|
|
161
|
+
const harnesses = this.harnesses.get(metadata.id) ?? [];
|
|
162
|
+
harnesses.push(harness);
|
|
163
|
+
this.harnesses.set(metadata.id, harnesses);
|
|
164
|
+
return harness;
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
await session.close(context);
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async seed(id = "session-1", parentSessionId) {
|
|
172
|
+
const session = await this.repo.create({ id, parentSessionId }, BACKGROUND_CONTEXT);
|
|
173
|
+
const metadata = session.metadata;
|
|
174
|
+
await session.close(BACKGROUND_CONTEXT);
|
|
175
|
+
return metadata;
|
|
176
|
+
}
|
|
177
|
+
gateNextOpenSession() {
|
|
178
|
+
const gate = { entered: new Deferred(), release: new Deferred() };
|
|
179
|
+
this.nextOpenSessionGate = gate;
|
|
180
|
+
return gate;
|
|
181
|
+
}
|
|
182
|
+
latestHarness(id) {
|
|
183
|
+
const harnesses = this.harnesses.get(id);
|
|
184
|
+
if (!harnesses?.length)
|
|
185
|
+
throw new Error(`No harness for ${id}`);
|
|
186
|
+
return harnesses.at(-1);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/testing/host.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACtF,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAG3E,MAAM,OAAO,QAAQ;IACX,OAAO,CAAa;IACrB,cAAc,CAAsB;IAE5C,cAAc;QACb,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YAC1C,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;QAAA,CAC9B,CAAC,CAAC;IAAA,CACH;IAED,OAAO,CAAC,KAAQ,EAAQ;QACvB,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAAA,CAC3B;CACD;AAOD,MAAM,OAAO,WAAW;IACd,OAAO,CAAU;IACjB,MAAM,GAAG,IAAI,QAAQ,EAAQ,CAAC;IAC9B,YAAY,GAAG,IAAI,QAAQ,EAAqB,CAAC;IACjD,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC;IAChD,eAAe,GAAG,CAAC,CAAC;IACpB,sBAAsB,GAAG,CAAC,CAAC;IAC3B,UAAU,GAAG,CAAC,CAAC;IACN,YAAY,GAAkB,EAAE,CAAC;IAC1C,qBAAqB,CAAS;IAC9B,SAAS,CAAS;IAClB,gBAAgB,CAAS;IACzB,iBAAiB,GAA0B,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IAChD,aAAa,CAAY;IACzB,eAAe,CAAY;IAEnC,YAAY,OAAgB,EAAE;QAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAAA,CACvB;IAED,YAAY,CAAC,QAAiB,EAG5B;QACD,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC;QAC1B,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,OAAO;YACN,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;YACjD,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC;gBACtB,IAAI,QAAQ;oBAAE,OAAO;gBACrB,IAAI,CAAC,sBAAsB,IAAI,CAAC,CAAC;gBACjC,IAAI,IAAI,CAAC,qBAAqB;oBAAE,MAAM,IAAI,CAAC,qBAAqB,CAAC;gBACjE,QAAQ,GAAG,IAAI,CAAC;gBAChB,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC;YAAA,CAC1B;SACD,CAAC;IAAA,CACF;IAED,KAAK,CAAC,aAAa,CAAC,IAAiB,EAAkC;QACtE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC;YACpC,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;YAClC,MAAM,KAAK,CAAC;QACb,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC;QAClC,IAAI,IAAI,EAAE,CAAC;YACV,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;YACjC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAChC,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;QAC5B,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC;QACtC,IAAI,CAAC,iBAAiB,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC;IAAA,CACd;IAED,KAAK,CAAC,KAAK,CAAC,OAAgB,EAAiB;QAC5C,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;QACrB,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC;QAChC,IAAI,IAAI,EAAE,CAAC;YACV,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAC/B,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAChC,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;QAC5B,CAAC;QACD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;YAC3B,MAAM,KAAK,CAAC;QACb,CAAC;QACD,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC/B,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAAA,CACrC;IAED,KAAK,CAAC,SAAS,CAAC,KAAY,EAAiB;QAC5C,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAC7C,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAAA,CACjC;IAED,aAAa,GAAa;QACzB,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,IAAI,QAAQ,EAAQ,EAAE,OAAO,EAAE,IAAI,QAAQ,EAAQ,EAAE,CAAC;QAC9E,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,OAAO,IAAI,CAAC;IAAA,CACZ;IAED,mBAAmB,GAAa;QAC/B,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,IAAI,QAAQ,EAAQ,EAAE,OAAO,EAAE,IAAI,QAAQ,EAAQ,EAAE,CAAC;QAC9E,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,OAAO,IAAI,CAAC;IAAA,CACZ;CACD;AAED,MAAM,UAAU,wBAAwB,GAA4B;IACnE,OAAO;QACN,YAAY,CAAC,YAAY,EAAE;YAC1B,OAAO;gBACN,KAAK,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE;oBAC5C,IACC,IAAI,CAAC,QAAQ,KAAK,SAAS;wBAC3B,IAAI,CAAC,SAAS,KAAK,uBAAuB;wBAC1C,IAAI,CAAC,MAAM,KAAK,QAAQ;wBACxB,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;wBACtB,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAC/B,CAAC;wBACF,MAAM,YAAY,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;wBACxD,OAAO,IAAI,CAAC;oBACb,CAAC;oBACD,IACC,IAAI,CAAC,QAAQ,KAAK,SAAS;wBAC3B,IAAI,CAAC,SAAS,KAAK,uBAAuB;wBAC1C,IAAI,CAAC,MAAM,KAAK,QAAQ;wBACxB,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EACrB,CAAC;wBACF,MAAM,YAAY,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;wBAC1C,OAAO,IAAI,CAAC;oBACb,CAAC;oBACD,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;gBAAA,CACpF;gBACD,OAAO,GAAG,EAAC,CAAC;aACZ,CAAC;QAAA,CACF;KACD,CAAC;AAAA,CACF;AAED,MAAM,OAAO,cAAc;IACjB,cAAc,GAAG,wBAAwB,EAAE,CAAC;IAC5C,IAAI,GAAG,IAAI,iBAAiB,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAC/C,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;IACtD,gBAAgB,GAAG,CAAC,CAAC;IACrB,oBAAoB,CAAS;IAC7B,qBAAqB,CAAS;IACtB,mBAAmB,CAAY;IAEvC,KAAK,CAAC,cAAc,CAAC,SAAiB,EAAE,OAAgB,EAA4B;QACnF,MAAM,OAAO,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;QAChG,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,oBAAoB,CAAC,oBAAoB,SAAS,EAAE,CAAC,CAAC;QAC1F,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,qBAAqB,EAAE,CAAC;QAC1D,OAAO,OAAO,CAAC,CAAC,CAAE,CAAC;IAAA,CACnB;IAED,KAAK,CAAC,WAAW,CAAC,QAAyB,EAAE,OAAgB,EAAgC;QAC5F,IAAI,CAAC,gBAAgB,IAAI,CAAC,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,mBAAmB,CAAC;QACtC,IAAI,IAAI,EAAE,CAAC;YACV,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;YACrC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAChC,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;QAC5B,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACxD,IAAI,CAAC;YACJ,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;gBAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,oBAAoB,CAAC;gBACxC,IAAI,CAAC,oBAAoB,GAAG,SAAS,CAAC;gBACtC,MAAM,KAAK,CAAC;YACb,CAAC;YACD,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;YACzC,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBAChC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,qBAAqB,CAAC;gBAC/C,IAAI,CAAC,qBAAqB,GAAG,SAAS,CAAC;YACxC,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;YACxD,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACxB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;YAC3C,OAAO,OAAO,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7B,MAAM,KAAK,CAAC;QACb,CAAC;IAAA,CACD;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,WAAW,EAAE,eAAwB,EAA4B;QAChF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,eAAe,EAAE,EAAE,kBAAkB,CAAC,CAAC;QACpF,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QAClC,MAAM,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACxC,OAAO,QAAQ,CAAC;IAAA,CAChB;IAED,mBAAmB,GAAa;QAC/B,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,IAAI,QAAQ,EAAQ,EAAE,OAAO,EAAE,IAAI,QAAQ,EAAQ,EAAE,CAAC;QAC9E,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;QAChC,OAAO,IAAI,CAAC;IAAA,CACZ;IAED,aAAa,CAAC,EAAU,EAAe;QACtC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,SAAS,EAAE,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;QAChE,OAAO,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC;IAAA,CACzB;CACD","sourcesContent":["import type { JsonValue, ServiceCall } from \"@earendil-works/chord\";\nimport type { Context, Session, SessionMetadata } from \"@earendil-works/pi-agent-core\";\nimport { BACKGROUND_CONTEXT, MemorySessionRepo } from \"@earendil-works/pi-agent-core\";\nimport { SessionAmbiguousError, SessionNotFoundError } from \"../errors.ts\";\nimport type { RoutedServerServiceHost, RoutedSessionHandle, ServerHost } from \"../types.ts\";\n\nexport class Deferred<T> {\n\treadonly promise: Promise<T>;\n\tprivate resolvePromise!: (value: T) => void;\n\n\tconstructor() {\n\t\tthis.promise = new Promise<T>((resolve) => {\n\t\t\tthis.resolvePromise = resolve;\n\t\t});\n\t}\n\n\tresolve(value: T): void {\n\t\tthis.resolvePromise(value);\n\t}\n}\n\ninterface OpenGate {\n\tentered: Deferred<void>;\n\trelease: Deferred<void>;\n}\n\nexport class TestHarness {\n\treadonly session: Session;\n\treadonly closed = new Deferred<void>();\n\treadonly #termination = new Deferred<Error | undefined>();\n\treadonly terminated = this.#termination.promise;\n\tattachedClients = 0;\n\tattachmentReleaseCount = 0;\n\tcloseCount = 0;\n\treadonly serviceCalls: ServiceCall[] = [];\n\tfailAttachmentRelease?: Error;\n\tfailClose?: Error;\n\tnextServiceError?: Error;\n\tnextServiceResult: JsonValue | undefined = { ok: true };\n\tprivate nextCloseGate?: OpenGate;\n\tprivate nextServiceGate?: OpenGate;\n\n\tconstructor(session: Session) {\n\t\tthis.session = session;\n\t}\n\n\tattachClient(_context: Context): {\n\t\tinvokeService: TestHarness[\"invokeService\"];\n\t\trelease(context: Context): void;\n\t} {\n\t\tthis.attachedClients += 1;\n\t\tlet released = false;\n\t\treturn {\n\t\t\tinvokeService: (call) => this.invokeService(call),\n\t\t\trelease: (_context) => {\n\t\t\t\tif (released) return;\n\t\t\t\tthis.attachmentReleaseCount += 1;\n\t\t\t\tif (this.failAttachmentRelease) throw this.failAttachmentRelease;\n\t\t\t\treleased = true;\n\t\t\t\tthis.attachedClients -= 1;\n\t\t\t},\n\t\t};\n\t}\n\n\tasync invokeService(call: ServiceCall): Promise<JsonValue | undefined> {\n\t\tthis.serviceCalls.push(call);\n\t\tif (this.nextServiceError) {\n\t\t\tconst error = this.nextServiceError;\n\t\t\tthis.nextServiceError = undefined;\n\t\t\tthrow error;\n\t\t}\n\t\tconst gate = this.nextServiceGate;\n\t\tif (gate) {\n\t\t\tthis.nextServiceGate = undefined;\n\t\t\tgate.entered.resolve(undefined);\n\t\t\tawait gate.release.promise;\n\t\t}\n\t\tconst result = this.nextServiceResult;\n\t\tthis.nextServiceResult = { ok: true };\n\t\treturn result;\n\t}\n\n\tasync close(context: Context): Promise<void> {\n\t\tthis.closeCount += 1;\n\t\tconst gate = this.nextCloseGate;\n\t\tif (gate) {\n\t\t\tthis.nextCloseGate = undefined;\n\t\t\tgate.entered.resolve(undefined);\n\t\t\tawait gate.release.promise;\n\t\t}\n\t\tif (this.failClose) {\n\t\t\tconst error = this.failClose;\n\t\t\tthis.failClose = undefined;\n\t\t\tthrow error;\n\t\t}\n\t\tawait this.session.close(context);\n\t\tthis.closed.resolve(undefined);\n\t\tthis.#termination.resolve(undefined);\n\t}\n\n\tasync terminate(error: Error): Promise<void> {\n\t\tawait this.session.close(BACKGROUND_CONTEXT);\n\t\tthis.#termination.resolve(error);\n\t}\n\n\tgateNextClose(): OpenGate {\n\t\tconst gate = { entered: new Deferred<void>(), release: new Deferred<void>() };\n\t\tthis.nextCloseGate = gate;\n\t\treturn gate;\n\t}\n\n\tgateNextServiceCall(): OpenGate {\n\t\tconst gate = { entered: new Deferred<void>(), release: new Deferred<void>() };\n\t\tthis.nextServiceGate = gate;\n\t\treturn gate;\n\t}\n}\n\nexport function createTestServerServices(): RoutedServerServiceHost {\n\treturn {\n\t\tattachClient(presentation) {\n\t\t\treturn {\n\t\t\t\tasync invokeService(call, _publish, context) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tcall.instance === undefined &&\n\t\t\t\t\t\tcall.serviceId === \"pi.session-management\" &&\n\t\t\t\t\t\tcall.member === \"attach\" &&\n\t\t\t\t\t\tcall.args.length === 1 &&\n\t\t\t\t\t\ttypeof call.args[0] === \"string\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tawait presentation.attachSession(call.args[0], context);\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\tcall.instance === undefined &&\n\t\t\t\t\t\tcall.serviceId === \"pi.session-management\" &&\n\t\t\t\t\t\tcall.member === \"detach\" &&\n\t\t\t\t\t\tcall.args.length === 0\n\t\t\t\t\t) {\n\t\t\t\t\t\tawait presentation.detachSession(context);\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tthrow new Error(`Unsupported test server service ${call.serviceId}.${call.member}`);\n\t\t\t\t},\n\t\t\t\trelease() {},\n\t\t\t};\n\t\t},\n\t};\n}\n\nexport class TestServerHost implements ServerHost {\n\treadonly serverServices = createTestServerServices();\n\treadonly repo = new MemorySessionRepo({ now: () => 1 });\n\treadonly harnesses = new Map<string, TestHarness[]>();\n\topenSessionCount = 0;\n\tnextOpenSessionError?: Error;\n\tnextHarnessCloseError?: Error;\n\tprivate nextOpenSessionGate?: OpenGate;\n\n\tasync resolveSession(sessionId: string, context: Context): Promise<SessionMetadata> {\n\t\tconst matches = (await this.repo.list(undefined, context)).filter(({ id }) => id === sessionId);\n\t\tif (matches.length === 0) throw new SessionNotFoundError(`Unknown session: ${sessionId}`);\n\t\tif (matches.length > 1) throw new SessionAmbiguousError();\n\t\treturn matches[0]!;\n\t}\n\n\tasync openSession(metadata: SessionMetadata, context: Context): Promise<RoutedSessionHandle> {\n\t\tthis.openSessionCount += 1;\n\t\tconst gate = this.nextOpenSessionGate;\n\t\tif (gate) {\n\t\t\tthis.nextOpenSessionGate = undefined;\n\t\t\tgate.entered.resolve(undefined);\n\t\t\tawait gate.release.promise;\n\t\t}\n\t\tconst session = await this.repo.open(metadata, context);\n\t\ttry {\n\t\t\tif (this.nextOpenSessionError) {\n\t\t\t\tconst error = this.nextOpenSessionError;\n\t\t\t\tthis.nextOpenSessionError = undefined;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tconst harness = new TestHarness(session);\n\t\t\tif (this.nextHarnessCloseError) {\n\t\t\t\tharness.failClose = this.nextHarnessCloseError;\n\t\t\t\tthis.nextHarnessCloseError = undefined;\n\t\t\t}\n\t\t\tconst harnesses = this.harnesses.get(metadata.id) ?? [];\n\t\t\tharnesses.push(harness);\n\t\t\tthis.harnesses.set(metadata.id, harnesses);\n\t\t\treturn harness;\n\t\t} catch (error) {\n\t\t\tawait session.close(context);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync seed(id = \"session-1\", parentSessionId?: string): Promise<SessionMetadata> {\n\t\tconst session = await this.repo.create({ id, parentSessionId }, BACKGROUND_CONTEXT);\n\t\tconst metadata = session.metadata;\n\t\tawait session.close(BACKGROUND_CONTEXT);\n\t\treturn metadata;\n\t}\n\n\tgateNextOpenSession(): OpenGate {\n\t\tconst gate = { entered: new Deferred<void>(), release: new Deferred<void>() };\n\t\tthis.nextOpenSessionGate = gate;\n\t\treturn gate;\n\t}\n\n\tlatestHarness(id: string): TestHarness {\n\t\tconst harnesses = this.harnesses.get(id);\n\t\tif (!harnesses?.length) throw new Error(`No harness for ${id}`);\n\t\treturn harnesses.at(-1)!;\n\t}\n}\n"]}
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { WireChannel } from "./client.ts";
|
|
2
2
|
export { connectUnixTestClient, ProtocolTestClient } from "./client.ts";
|
|
3
|
+
export { createTestServerServices, Deferred, TestHarness, TestServerHost } from "./host.ts";
|
|
3
4
|
export type { TestServer, TestServerOptions } from "./server.ts";
|
|
4
5
|
export { createTestServer } from "./server.ts";
|
|
5
|
-
export { Deferred, TEST_MODEL, TestServerService, TestSessionRuntime } from "./service.ts";
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACxE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,wBAAwB,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC5F,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC","sourcesContent":["export type { WireChannel } from \"./client.ts\";\nexport { connectUnixTestClient, ProtocolTestClient } from \"./client.ts\";\nexport { createTestServerServices, Deferred, TestHarness, TestServerHost } from \"./host.ts\";\nexport type { TestServer, TestServerOptions } from \"./server.ts\";\nexport { createTestServer } from \"./server.ts\";\n"]}
|
package/dist/testing/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { connectUnixTestClient, ProtocolTestClient } from "./client.js";
|
|
2
|
+
export { createTestServerServices, Deferred, TestHarness, TestServerHost } from "./host.js";
|
|
2
3
|
export { createTestServer } from "./server.js";
|
|
3
|
-
export { Deferred, TEST_MODEL, TestServerService, TestSessionRuntime } from "./service.js";
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,wBAAwB,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE5F,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC","sourcesContent":["export type { WireChannel } from \"./client.ts\";\nexport { connectUnixTestClient, ProtocolTestClient } from \"./client.ts\";\nexport { createTestServerServices, Deferred, TestHarness, TestServerHost } from \"./host.ts\";\nexport type { TestServer, TestServerOptions } from \"./server.ts\";\nexport { createTestServer } from \"./server.ts\";\n"]}
|
package/dist/testing/server.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
3
|
-
export interface TestServerOptions extends
|
|
4
|
-
|
|
1
|
+
import { Server } from "../server.ts";
|
|
2
|
+
import type { ServerHost, ServerOptions } from "../types.ts";
|
|
3
|
+
export interface TestServerOptions extends Omit<ServerOptions, "serverId"> {
|
|
4
|
+
host?: ServerHost;
|
|
5
|
+
serverId?: string;
|
|
5
6
|
}
|
|
6
7
|
export interface TestServer {
|
|
7
|
-
server:
|
|
8
|
-
|
|
8
|
+
server: Server;
|
|
9
|
+
host: ServerHost;
|
|
9
10
|
}
|
|
10
|
-
/** Create an unstarted
|
|
11
|
+
/** Create an unstarted Server with deterministic defaults for transport conformance tests. */
|
|
11
12
|
export declare function createTestServer(options: TestServerOptions): TestServer;
|
|
12
13
|
//# sourceMappingURL=server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/testing/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/testing/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG7D,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC;IACzE,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,CAAC;CACjB;AAED,8FAA8F;AAC9F,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAYvE","sourcesContent":["import { Server } from \"../server.ts\";\nimport type { ServerHost, ServerOptions } from \"../types.ts\";\nimport { TestServerHost } from \"./host.ts\";\n\nexport interface TestServerOptions extends Omit<ServerOptions, \"serverId\"> {\n\thost?: ServerHost;\n\tserverId?: string;\n}\n\nexport interface TestServer {\n\tserver: Server;\n\thost: ServerHost;\n}\n\n/** Create an unstarted Server with deterministic defaults for transport conformance tests. */\nexport function createTestServer(options: TestServerOptions): TestServer {\n\tconst host = options.host ?? new TestServerHost();\n\treturn {\n\t\tserver: new Server(host, {\n\t\t\tlisteners: options.listeners,\n\t\t\tmaxFrameLength: options.maxFrameLength,\n\t\t\thandshakeTimeoutMs: options.handshakeTimeoutMs,\n\t\t\tserverId: options.serverId ?? \"00000000-0000-4000-8000-000000000001\",\n\t\t\tonError: options.onError,\n\t\t}),\n\t\thost,\n\t};\n}\n"]}
|
package/dist/testing/server.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
/** Create an unstarted
|
|
1
|
+
import { Server } from "../server.js";
|
|
2
|
+
import { TestServerHost } from "./host.js";
|
|
3
|
+
/** Create an unstarted Server with deterministic defaults for transport conformance tests. */
|
|
4
4
|
export function createTestServer(options) {
|
|
5
|
-
const
|
|
5
|
+
const host = options.host ?? new TestServerHost();
|
|
6
6
|
return {
|
|
7
|
-
server: new
|
|
7
|
+
server: new Server(host, {
|
|
8
8
|
listeners: options.listeners,
|
|
9
9
|
maxFrameLength: options.maxFrameLength,
|
|
10
10
|
handshakeTimeoutMs: options.handshakeTimeoutMs,
|
|
11
|
-
serverId: options.serverId,
|
|
11
|
+
serverId: options.serverId ?? "00000000-0000-4000-8000-000000000001",
|
|
12
12
|
onError: options.onError,
|
|
13
13
|
}),
|
|
14
|
-
|
|
14
|
+
host,
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
17
|
//# sourceMappingURL=server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/testing/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/testing/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAY3C,8FAA8F;AAC9F,MAAM,UAAU,gBAAgB,CAAC,OAA0B,EAAc;IACxE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,cAAc,EAAE,CAAC;IAClD,OAAO;QACN,MAAM,EAAE,IAAI,MAAM,CAAC,IAAI,EAAE;YACxB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,cAAc,EAAE,OAAO,CAAC,cAAc;YACtC,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;YAC9C,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,sCAAsC;YACpE,OAAO,EAAE,OAAO,CAAC,OAAO;SACxB,CAAC;QACF,IAAI;KACJ,CAAC;AAAA,CACF","sourcesContent":["import { Server } from \"../server.ts\";\nimport type { ServerHost, ServerOptions } from \"../types.ts\";\nimport { TestServerHost } from \"./host.ts\";\n\nexport interface TestServerOptions extends Omit<ServerOptions, \"serverId\"> {\n\thost?: ServerHost;\n\tserverId?: string;\n}\n\nexport interface TestServer {\n\tserver: Server;\n\thost: ServerHost;\n}\n\n/** Create an unstarted Server with deterministic defaults for transport conformance tests. */\nexport function createTestServer(options: TestServerOptions): TestServer {\n\tconst host = options.host ?? new TestServerHost();\n\treturn {\n\t\tserver: new Server(host, {\n\t\t\tlisteners: options.listeners,\n\t\t\tmaxFrameLength: options.maxFrameLength,\n\t\t\thandshakeTimeoutMs: options.handshakeTimeoutMs,\n\t\t\tserverId: options.serverId ?? \"00000000-0000-4000-8000-000000000001\",\n\t\t\tonError: options.onError,\n\t\t}),\n\t\thost,\n\t};\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../../../src/transports/unix/address.ts"],"names":[],"mappings":"AAEA,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAKnF","sourcesContent":["import { join } from \"node:path\";\n\n/** Derive the local Unix socket path for one logical server identity. */\nexport function getUnixSocketPath(serverId: string, serverDirectory: string): string {\n\tif (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(serverId)) {\n\t\tthrow new TypeError(\"Unix serverId must be a canonical lowercase UUIDv4\");\n\t}\n\treturn join(serverDirectory, `${serverId}.sock`);\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
/** Derive the local Unix socket path for one logical server identity. */
|
|
3
|
+
export function getUnixSocketPath(serverId, serverDirectory) {
|
|
4
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(serverId)) {
|
|
5
|
+
throw new TypeError("Unix serverId must be a canonical lowercase UUIDv4");
|
|
6
|
+
}
|
|
7
|
+
return join(serverDirectory, `${serverId}.sock`);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=address.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address.js","sourceRoot":"","sources":["../../../src/transports/unix/address.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,yEAAyE;AACzE,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,eAAuB,EAAU;IACpF,IAAI,CAAC,uEAAuE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7F,MAAM,IAAI,SAAS,CAAC,oDAAoD,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,IAAI,CAAC,eAAe,EAAE,GAAG,QAAQ,OAAO,CAAC,CAAC;AAAA,CACjD","sourcesContent":["import { join } from \"node:path\";\n\n/** Derive the local Unix socket path for one logical server identity. */\nexport function getUnixSocketPath(serverId: string, serverDirectory: string): string {\n\tif (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(serverId)) {\n\t\tthrow new TypeError(\"Unix serverId must be a canonical lowercase UUIDv4\");\n\t}\n\treturn join(serverDirectory, `${serverId}.sock`);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/transports/unix/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC","sourcesContent":["export { createUnixListener } from \"./listener.ts\";\nexport { createUnixServer } from \"./preset.ts\";\nexport type { UnixListenerOptions, UnixServerOptions } from \"./types.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/transports/unix/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC","sourcesContent":["export { getUnixSocketPath } from \"./address.ts\";\nexport { createUnixListener } from \"./listener.ts\";\nexport { createUnixServer } from \"./preset.ts\";\nexport type { UnixListenerOptions, UnixServerOptions } from \"./types.ts\";\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/transports/unix/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC","sourcesContent":["export { createUnixListener } from \"./listener.ts\";\nexport { createUnixServer } from \"./preset.ts\";\nexport type { UnixListenerOptions, UnixServerOptions } from \"./types.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/transports/unix/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC","sourcesContent":["export { getUnixSocketPath } from \"./address.ts\";\nexport { createUnixListener } from \"./listener.ts\";\nexport { createUnixServer } from \"./preset.ts\";\nexport type { UnixListenerOptions, UnixServerOptions } from \"./types.ts\";\n"]}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { type Socket } from "node:net";
|
|
2
2
|
import type { ByteConnection } from "../../connection.ts";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ServerListener } from "../../listener.ts";
|
|
4
4
|
import type { UnixListenerOptions } from "./types.ts";
|
|
5
|
-
export declare function validateUnixSocketPath(path: string, description?: string): void;
|
|
6
5
|
/** @internal Exported only for transport-level verification. */
|
|
7
6
|
export declare class UnixByteConnection implements ByteConnection {
|
|
8
7
|
private readonly socket;
|
|
@@ -21,5 +20,5 @@ export declare class UnixByteConnection implements ByteConnection {
|
|
|
21
20
|
markClosed(): void;
|
|
22
21
|
private write;
|
|
23
22
|
}
|
|
24
|
-
export declare function createUnixListener(options: UnixListenerOptions):
|
|
23
|
+
export declare function createUnixListener(options: UnixListenerOptions): ServerListener;
|
|
25
24
|
//# sourceMappingURL=listener.d.ts.map
|