@covenant-rpc/server 0.1.3

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 ADDED
@@ -0,0 +1,17 @@
1
+ # @covenant-rpc/server
2
+
3
+ ## 0.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 8061179: Initial publish
8
+ - Updated dependencies [8061179]
9
+ - @covenant-rpc/request-serializer@1.0.3
10
+ - @covenant-rpc/core@0.1.3
11
+ - @covenant-rpc/ion@1.0.3
12
+
13
+ ## 0.1.2
14
+
15
+ ### Patch Changes
16
+
17
+ - Fix all packages
@@ -0,0 +1,9 @@
1
+ import type { CovenantServer } from "../server";
2
+
3
+ export function vanillaAdapter(server: CovenantServer<any, any, any, any>) {
4
+ return async (request: Request) => {
5
+ const res = await server.handle(request);
6
+ return res;
7
+ }
8
+ }
9
+
package/lib/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ export { CovenantServer } from "./server";
2
+ export { Logger } from "./logger";
3
+ export { vanillaAdapter } from "./adapters/vanilla";
4
+ export { Sidekick, type SidekickClient } from "./sidekick";
5
+ export { httpServerToSidekick, httpSidekickToServer } from "./interfaces/http";
6
+ export { emptyServerToSidekick } from "./interfaces/empty";
7
+ export { directClientToServer } from "./interfaces/direct";
8
+ export { mockClientToSidekick } from "./interfaces/mock";
9
+
10
+ // Re-export types from core for convenience
11
+ export type { LoggerLevel, Prefix } from "@covenant-rpc/core/logger";
@@ -0,0 +1,116 @@
1
+ import type { ClientToServerConnection, ServerToSidekickConnection, ClientToSidekickConnection } from "@covenant-rpc/core/interfaces";
2
+ import type { ProcedureRequestBody, ProcedureResponse } from "@covenant-rpc/core/procedure";
3
+ import type { CovenantServer } from "../server";
4
+ import { v } from "@covenant-rpc/core/validation";
5
+ import { procedureResponseSchema } from "@covenant-rpc/core/procedure";
6
+ import { channelConnectionRequestSchema, channelConnectionResponseSchema, type ChannelConnectionRequest, type ChannelConnectionResponse } from "@covenant-rpc/core/channel";
7
+ import ION from "@covenant-rpc/ion";
8
+
9
+
10
+ export function directClientToServer(
11
+ server: CovenantServer<any, any, any, any>,
12
+ extraHeaders: Record<string, string>
13
+ ): ClientToServerConnection {
14
+ const getHeaders = () => {
15
+ const h = new Headers();
16
+ h.set("Content-Type", "application/json");
17
+
18
+ for (const k in extraHeaders) {
19
+ h.set(k, extraHeaders[k]!);
20
+ }
21
+ return h;
22
+ }
23
+
24
+ const getUrl = (type: string) => {
25
+ const url = new URL("localhost:3000");
26
+ url.searchParams.set("type", type);
27
+ return url.toString();
28
+ }
29
+
30
+ return {
31
+ async sendConnectionRequest(body: ChannelConnectionRequest): Promise<ChannelConnectionResponse> {
32
+ try {
33
+ const request = new Request(getUrl("connect"), {
34
+ body: ION.stringify(body),
35
+ method: "POST",
36
+ headers: getHeaders(),
37
+ });
38
+
39
+ const response = await server.handle(request);
40
+ const responseText = await response.text();
41
+ const responseBody = ION.parse(responseText);
42
+ const connectionResponse = v.parseSafe(responseBody, channelConnectionResponseSchema);
43
+
44
+ if (connectionResponse === null) {
45
+ return {
46
+ channel: body.channel,
47
+ params: body.params,
48
+ result: {
49
+ type: "ERROR",
50
+ error: {
51
+ channel: body.channel,
52
+ params: body.params,
53
+ fault: "server",
54
+ message: `Bad response from server: ${JSON.stringify(responseBody)}`,
55
+ },
56
+ },
57
+ };
58
+ }
59
+
60
+ return connectionResponse;
61
+ } catch (e) {
62
+ return {
63
+ channel: body.channel,
64
+ params: body.params,
65
+ result: {
66
+ type: "ERROR",
67
+ error: {
68
+ channel: body.channel,
69
+ params: body.params,
70
+ fault: "server",
71
+ message: `Unknown error connecting to channel: ${e}`,
72
+ },
73
+ },
74
+ };
75
+ }
76
+ },
77
+ async runProcedure(body: ProcedureRequestBody) {
78
+ try {
79
+ const request = new Request(getUrl("procedure"), {
80
+ body: ION.stringify(body),
81
+ method: "POST",
82
+ headers: getHeaders(),
83
+ });
84
+
85
+ const response = await server.handle(request);
86
+ const responseText = await response.text();
87
+ const responseBody = ION.parse(responseText);
88
+ const procedureResponse = v.parseSafe(responseBody, procedureResponseSchema)
89
+
90
+ if (procedureResponse === null) {
91
+ return {
92
+ status: "ERR",
93
+ error: {
94
+ code: 500,
95
+ message: `Bad response from server: ${responseBody}`,
96
+ }
97
+ }
98
+ }
99
+
100
+ return procedureResponse;
101
+ } catch (e) {
102
+ return {
103
+ status: "ERR",
104
+ error: {
105
+ code: 400,
106
+ message: `Unknown error fetching from the server: ${e}`,
107
+ }
108
+ }
109
+ }
110
+ }
111
+
112
+ }
113
+
114
+ }
115
+
116
+
@@ -0,0 +1,9 @@
1
+ import type { ServerToSidekickConnection } from "@covenant-rpc/core/interfaces";
2
+
3
+ export function emptyServerToSidekick(): ServerToSidekickConnection {
4
+ return {
5
+ addConnection: async () => null,
6
+ update: async () => null,
7
+ postMessage: async () => null,
8
+ }
9
+ }
@@ -0,0 +1,111 @@
1
+ import type { ServerToSidekickConnection, SidekickToServerConnection } from "@covenant-rpc/core/interfaces";
2
+ import type { ChannelConnectionPayload, ServerMessage } from "@covenant-rpc/core/channel";
3
+ import ION from "@covenant-rpc/ion";
4
+
5
+ export function httpSidekickToServer(baseUrl: string, key: string): SidekickToServerConnection {
6
+ const getUrl = (type: string) => {
7
+ const url = new URL(baseUrl);
8
+ url.searchParams.set("type", type);
9
+ return url.toString();
10
+ };
11
+
12
+ return {
13
+ async sendMessage(message) {
14
+ const json = ION.stringify(message);
15
+
16
+ const res = await fetch(getUrl("channel"), {
17
+ headers: {
18
+ "Authorization": `Bearer ${key}`,
19
+ "Content-Type": "application/json",
20
+ },
21
+ method: "POST",
22
+ body: json,
23
+ });
24
+
25
+ if (res.ok) {
26
+ return null;
27
+ }
28
+
29
+ return {
30
+ channel: message.channel,
31
+ params: message.params,
32
+ fault: "server",
33
+ message: `Failed to send message to server. Received: ${res.status} - ${res.statusText}`,
34
+ }
35
+ },
36
+ }
37
+ }
38
+
39
+
40
+ class HttpServerToSidekick implements ServerToSidekickConnection {
41
+ private url: URL;
42
+ private key: string;
43
+
44
+ constructor(url: string, key: string) {
45
+ this.url = new URL(url);
46
+ this.key = key;
47
+ }
48
+
49
+ async addConnection(payload: ChannelConnectionPayload): Promise<Error | null> {
50
+ const url = new URL(this.url.toString());
51
+ url.pathname = "/connection";
52
+
53
+ const res = await fetch(url.toString(), {
54
+ body: ION.stringify(payload),
55
+ headers: {
56
+ "Content-Type": "application/json",
57
+ "Authorization": `Bearer ${this.key}`,
58
+ },
59
+ method: "POST",
60
+ });
61
+
62
+ if (!res.ok) {
63
+ return new Error(`Error posting connection from sidekick: ${res.status} - ${res.statusText}`);
64
+ }
65
+ return null;
66
+ }
67
+
68
+ async update(resources: string[]): Promise<Error | null> {
69
+ const url = new URL(this.url.toString());
70
+ url.pathname = "/resources";
71
+
72
+ const res = await fetch(url.toString(), {
73
+ body: ION.stringify({
74
+ resources: resources,
75
+ }),
76
+ headers: {
77
+ "Content-Type": "application/json",
78
+ "Authorization": `Bearer ${this.key}`,
79
+ },
80
+ method: "POST",
81
+ });
82
+
83
+ if (!res.ok) {
84
+ return new Error(`Error posting resources from sidekick: ${res.status} - ${res.statusText}`);
85
+ }
86
+ return null;
87
+ }
88
+
89
+ async postMessage(message: ServerMessage): Promise<Error | null> {
90
+ const url = new URL(this.url.toString());
91
+ url.pathname = "/message";
92
+
93
+ const res = await fetch(url.toString(), {
94
+ body: ION.stringify(message),
95
+ headers: {
96
+ "Content-Type": "application/json",
97
+ "Authorization": `Bearer ${this.key}`,
98
+ },
99
+ method: "POST",
100
+ });
101
+
102
+ if (!res.ok) {
103
+ return new Error(`Error posting message from sidekick: ${res.status} - ${res.statusText}`);
104
+ }
105
+ return null;
106
+ }
107
+ }
108
+
109
+ export function httpServerToSidekick(url: string, key: string): ServerToSidekickConnection {
110
+ return new HttpServerToSidekick(url, key);
111
+ }
@@ -0,0 +1,32 @@
1
+ import type { ClientToSidekickConnection, ServerToSidekickConnection } from "@covenant-rpc/core/interfaces";
2
+ import type { Sidekick, SidekickClient } from "../sidekick";
3
+ import type { SidekickIncomingMessage, SidekickOutgoingMessage } from "@covenant-rpc/core/sidekick/protocol";
4
+
5
+
6
+ export function mockServerToSidekick(sidekick: Sidekick): ServerToSidekickConnection {
7
+ return {
8
+ async addConnection(p) {
9
+ sidekick.addConnection(p);
10
+ return null;
11
+ },
12
+ async update(resources: string[]) {
13
+ await sidekick.updateResources(resources);
14
+ return null;
15
+ },
16
+ async postMessage(m) {
17
+ await sidekick.postServerMessage(m);
18
+ return null;
19
+ }
20
+ }
21
+ }
22
+
23
+ export function mockClientToSidekick(sidekick: Sidekick, client: SidekickClient): ClientToSidekickConnection {
24
+ return {
25
+ sendMessage(message: SidekickIncomingMessage) {
26
+ sidekick.handleClientMessage(client, message);
27
+ },
28
+ onMessage(v) {
29
+
30
+ }
31
+ }
32
+ }
package/lib/logger.ts ADDED
@@ -0,0 +1,79 @@
1
+ import type { Logger as ILogger, LoggerLevel, Prefix } from "@covenant-rpc/core/logger";
2
+
3
+ const loggerLevels: Record<LoggerLevel, number> = { "slient": 0, "error": 1, "warn": 2, "info": 3, "debug": 4 };
4
+
5
+ function levelSatisfies(currentLevel: LoggerLevel, maxLevel: LoggerLevel): boolean {
6
+ return loggerLevels[currentLevel] <= loggerLevels[maxLevel];
7
+ }
8
+
9
+ export class Logger implements ILogger {
10
+ prefixes: Prefix[];
11
+ level: LoggerLevel
12
+
13
+ constructor(level: LoggerLevel, prefixes: Prefix[] = []) {
14
+ this.prefixes = prefixes;
15
+ this.level = level;
16
+ }
17
+
18
+ sublogger(prefix: Prefix): Logger {
19
+ return new Logger(this.level, [...this.prefixes, prefix]);
20
+ }
21
+
22
+ pushPrefix(prefix: Prefix): Logger {
23
+ this.prefixes.push(prefix);
24
+ return this
25
+ }
26
+
27
+ clone(): Logger {
28
+ return new Logger(this.level, [...this.prefixes]);
29
+ }
30
+
31
+ debug(text: string): void {
32
+ if (!levelSatisfies("debug", this.level)) {
33
+ return;
34
+ }
35
+
36
+ console.log(`${this.getPrefix()}DEBUG: ${text}`);
37
+ }
38
+
39
+ info(text: string): void {
40
+ if (!levelSatisfies("info", this.level)) {
41
+ return;
42
+ }
43
+
44
+ console.log(`${this.getPrefix()}INFO: ${text}`);
45
+ }
46
+
47
+ error(text: string): void {
48
+ if (!levelSatisfies("error", this.level)) {
49
+ return;
50
+ }
51
+
52
+ console.log(`${this.getPrefix()}ERROR: ${text}`);
53
+ }
54
+
55
+ warn(text: string): void {
56
+ if (!levelSatisfies("error", this.level)) {
57
+ return;
58
+ }
59
+
60
+ console.log(`${this.getPrefix()}WARNING: ${text}`);
61
+ }
62
+
63
+ fatal(text: string): never {
64
+ console.log("---------------------------------");
65
+ console.log(`${this.getPrefix()}FATAL: ${text}`);
66
+ console.log("---------------------------------");
67
+ throw new Error(`FATAL: ${text}`);
68
+ }
69
+
70
+ private getPrefix(): string {
71
+ if (this.prefixes.length === 0) {
72
+ return "";
73
+ }
74
+ const strs = this.prefixes.map(p => typeof p === "string" ? p : p());
75
+ return `[${strs.join(" |> ")}] `;
76
+
77
+ }
78
+ }
79
+