@claude-studio/shared 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/dist/__tests__/protocol.test.d.ts +2 -0
- package/dist/__tests__/protocol.test.d.ts.map +1 -0
- package/dist/__tests__/protocol.test.js +70 -0
- package/dist/__tests__/protocol.test.js.map +1 -0
- package/dist/element-selection.d.ts +27 -0
- package/dist/element-selection.d.ts.map +1 -0
- package/dist/element-selection.js +2 -0
- package/dist/element-selection.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +42 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +11 -0
- package/dist/protocol.js.map +1 -0
- package/package.json +20 -0
- package/src/__tests__/protocol.test.ts +81 -0
- package/src/element-selection.ts +21 -0
- package/src/index.ts +2 -0
- package/src/protocol.ts +30 -0
- package/tsconfig.json +8 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/protocol.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { parseClientMessage, serializeServerMessage } from "../protocol.js";
|
|
3
|
+
describe("parseClientMessage", () => {
|
|
4
|
+
it("parses a ping message", () => {
|
|
5
|
+
const msg = parseClientMessage('{"type":"ping"}');
|
|
6
|
+
expect(msg).toEqual({ type: "ping" });
|
|
7
|
+
});
|
|
8
|
+
it("parses a prompt message with element data", () => {
|
|
9
|
+
const raw = JSON.stringify({
|
|
10
|
+
type: "prompt",
|
|
11
|
+
route: "/about",
|
|
12
|
+
element: {
|
|
13
|
+
tagName: "h1", id: "", classList: ["title"], cssSelector: "h1.title",
|
|
14
|
+
textContent: "About Us", outerHTML: '<h1 class="title">About Us</h1>',
|
|
15
|
+
attributes: {}, boundingRect: { top: 100, left: 50, width: 400, height: 40 },
|
|
16
|
+
computedStyles: { color: "black", backgroundColor: "white", fontSize: "32px", fontFamily: "sans-serif", padding: "0px", margin: "0px" },
|
|
17
|
+
parentChain: ["body", "main"], siblingCount: 2, childCount: 0
|
|
18
|
+
},
|
|
19
|
+
prompt: "Make this red",
|
|
20
|
+
});
|
|
21
|
+
const msg = parseClientMessage(raw);
|
|
22
|
+
expect(msg.type).toBe("prompt");
|
|
23
|
+
});
|
|
24
|
+
it("parses a raw_prompt message", () => {
|
|
25
|
+
const msg = parseClientMessage('{"type":"raw_prompt","prompt":"hello"}');
|
|
26
|
+
expect(msg).toEqual({ type: "raw_prompt", prompt: "hello" });
|
|
27
|
+
});
|
|
28
|
+
it("parses a reset_session message", () => {
|
|
29
|
+
const msg = parseClientMessage('{"type":"reset_session"}');
|
|
30
|
+
expect(msg).toEqual({ type: "reset_session" });
|
|
31
|
+
});
|
|
32
|
+
it("throws on invalid JSON", () => {
|
|
33
|
+
expect(() => parseClientMessage("not json")).toThrow();
|
|
34
|
+
});
|
|
35
|
+
it("throws on missing type field", () => {
|
|
36
|
+
expect(() => parseClientMessage('{"data":"hello"}')).toThrow("missing type");
|
|
37
|
+
});
|
|
38
|
+
it("throws on non-object message", () => {
|
|
39
|
+
expect(() => parseClientMessage('"just a string"')).toThrow("missing type");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe("serializeServerMessage", () => {
|
|
43
|
+
it("serializes a connected message", () => {
|
|
44
|
+
const json = serializeServerMessage({ type: "connected", clientId: "abc-123" });
|
|
45
|
+
const parsed = JSON.parse(json);
|
|
46
|
+
expect(parsed.type).toBe("connected");
|
|
47
|
+
expect(parsed.clientId).toBe("abc-123");
|
|
48
|
+
});
|
|
49
|
+
it("serializes a streaming message", () => {
|
|
50
|
+
const json = serializeServerMessage({ type: "ai_streaming", chunk: "Hello " });
|
|
51
|
+
expect(JSON.parse(json).chunk).toBe("Hello ");
|
|
52
|
+
});
|
|
53
|
+
it("serializes a pong message", () => {
|
|
54
|
+
const json = serializeServerMessage({ type: "pong" });
|
|
55
|
+
expect(JSON.parse(json).type).toBe("pong");
|
|
56
|
+
});
|
|
57
|
+
it("serializes an ai_complete message", () => {
|
|
58
|
+
const json = serializeServerMessage({
|
|
59
|
+
type: "ai_complete", result: "Done", sessionId: "s1", cost: 0.05, turns: 3
|
|
60
|
+
});
|
|
61
|
+
const parsed = JSON.parse(json);
|
|
62
|
+
expect(parsed.sessionId).toBe("s1");
|
|
63
|
+
expect(parsed.cost).toBe(0.05);
|
|
64
|
+
});
|
|
65
|
+
it("serializes an ai_error message", () => {
|
|
66
|
+
const json = serializeServerMessage({ type: "ai_error", error: "something broke" });
|
|
67
|
+
expect(JSON.parse(json).error).toBe("something broke");
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=protocol.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.test.js","sourceRoot":"","sources":["../../src/__tests__/protocol.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface ElementSelection {
|
|
2
|
+
tagName: string;
|
|
3
|
+
id: string;
|
|
4
|
+
classList: string[];
|
|
5
|
+
cssSelector: string;
|
|
6
|
+
textContent: string;
|
|
7
|
+
outerHTML: string;
|
|
8
|
+
attributes: Record<string, string>;
|
|
9
|
+
boundingRect: {
|
|
10
|
+
top: number;
|
|
11
|
+
left: number;
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
};
|
|
15
|
+
computedStyles: {
|
|
16
|
+
color: string;
|
|
17
|
+
backgroundColor: string;
|
|
18
|
+
fontSize: string;
|
|
19
|
+
fontFamily: string;
|
|
20
|
+
padding: string;
|
|
21
|
+
margin: string;
|
|
22
|
+
};
|
|
23
|
+
parentChain: string[];
|
|
24
|
+
siblingCount: number;
|
|
25
|
+
childCount: number;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=element-selection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-selection.d.ts","sourceRoot":"","sources":["../src/element-selection.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,YAAY,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1E,cAAc,EAAE;QACd,KAAK,EAAE,MAAM,CAAA;QACb,eAAe,EAAE,MAAM,CAAA;QACvB,QAAQ,EAAE,MAAM,CAAA;QAChB,UAAU,EAAE,MAAM,CAAA;QAClB,OAAO,EAAE,MAAM,CAAA;QACf,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;IACD,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;CACnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-selection.js","sourceRoot":"","sources":["../src/element-selection.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,wBAAwB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,wBAAwB,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ElementSelection } from "./element-selection.js";
|
|
2
|
+
export type ClientMessage = {
|
|
3
|
+
type: "ping";
|
|
4
|
+
} | {
|
|
5
|
+
type: "prompt";
|
|
6
|
+
route: string;
|
|
7
|
+
element: ElementSelection;
|
|
8
|
+
prompt: string;
|
|
9
|
+
} | {
|
|
10
|
+
type: "raw_prompt";
|
|
11
|
+
prompt: string;
|
|
12
|
+
} | {
|
|
13
|
+
type: "reset_session";
|
|
14
|
+
};
|
|
15
|
+
export type ServerMessage = {
|
|
16
|
+
type: "connected";
|
|
17
|
+
clientId: string;
|
|
18
|
+
} | {
|
|
19
|
+
type: "pong";
|
|
20
|
+
} | {
|
|
21
|
+
type: "ai_streaming";
|
|
22
|
+
chunk: string;
|
|
23
|
+
} | {
|
|
24
|
+
type: "tool_use";
|
|
25
|
+
tool: string;
|
|
26
|
+
input: Record<string, unknown>;
|
|
27
|
+
} | {
|
|
28
|
+
type: "ai_complete";
|
|
29
|
+
result: string;
|
|
30
|
+
sessionId: string;
|
|
31
|
+
cost: number;
|
|
32
|
+
turns: number;
|
|
33
|
+
} | {
|
|
34
|
+
type: "ai_error";
|
|
35
|
+
error: string;
|
|
36
|
+
} | {
|
|
37
|
+
type: "session_reset";
|
|
38
|
+
newSessionId: string;
|
|
39
|
+
};
|
|
40
|
+
export declare function parseClientMessage(raw: string): ClientMessage;
|
|
41
|
+
export declare function serializeServerMessage(msg: ServerMessage): string;
|
|
42
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAG9D,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5E;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAA;AAG7B,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACvF;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAM7D;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAEjE"}
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function parseClientMessage(raw) {
|
|
2
|
+
const msg = JSON.parse(raw);
|
|
3
|
+
if (!msg || typeof msg.type !== "string") {
|
|
4
|
+
throw new Error("Invalid message: missing type field");
|
|
5
|
+
}
|
|
6
|
+
return msg;
|
|
7
|
+
}
|
|
8
|
+
export function serializeServerMessage(msg) {
|
|
9
|
+
return JSON.stringify(msg);
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAmBA,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC3B,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;IACxD,CAAC;IACD,OAAO,GAAoB,CAAA;AAC7B,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,GAAkB;IACvD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;AAC5B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@claude-studio/shared",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"test": "vitest run"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"typescript": "^5.0.0",
|
|
18
|
+
"vitest": "^3.0.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest"
|
|
2
|
+
import { parseClientMessage, serializeServerMessage } from "../protocol.js"
|
|
3
|
+
|
|
4
|
+
describe("parseClientMessage", () => {
|
|
5
|
+
it("parses a ping message", () => {
|
|
6
|
+
const msg = parseClientMessage('{"type":"ping"}')
|
|
7
|
+
expect(msg).toEqual({ type: "ping" })
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it("parses a prompt message with element data", () => {
|
|
11
|
+
const raw = JSON.stringify({
|
|
12
|
+
type: "prompt",
|
|
13
|
+
route: "/about",
|
|
14
|
+
element: {
|
|
15
|
+
tagName: "h1", id: "", classList: ["title"], cssSelector: "h1.title",
|
|
16
|
+
textContent: "About Us", outerHTML: '<h1 class="title">About Us</h1>',
|
|
17
|
+
attributes: {}, boundingRect: { top: 100, left: 50, width: 400, height: 40 },
|
|
18
|
+
computedStyles: { color: "black", backgroundColor: "white", fontSize: "32px", fontFamily: "sans-serif", padding: "0px", margin: "0px" },
|
|
19
|
+
parentChain: ["body", "main"], siblingCount: 2, childCount: 0
|
|
20
|
+
},
|
|
21
|
+
prompt: "Make this red",
|
|
22
|
+
})
|
|
23
|
+
const msg = parseClientMessage(raw)
|
|
24
|
+
expect(msg.type).toBe("prompt")
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it("parses a raw_prompt message", () => {
|
|
28
|
+
const msg = parseClientMessage('{"type":"raw_prompt","prompt":"hello"}')
|
|
29
|
+
expect(msg).toEqual({ type: "raw_prompt", prompt: "hello" })
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("parses a reset_session message", () => {
|
|
33
|
+
const msg = parseClientMessage('{"type":"reset_session"}')
|
|
34
|
+
expect(msg).toEqual({ type: "reset_session" })
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it("throws on invalid JSON", () => {
|
|
38
|
+
expect(() => parseClientMessage("not json")).toThrow()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it("throws on missing type field", () => {
|
|
42
|
+
expect(() => parseClientMessage('{"data":"hello"}')).toThrow("missing type")
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it("throws on non-object message", () => {
|
|
46
|
+
expect(() => parseClientMessage('"just a string"')).toThrow("missing type")
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe("serializeServerMessage", () => {
|
|
51
|
+
it("serializes a connected message", () => {
|
|
52
|
+
const json = serializeServerMessage({ type: "connected", clientId: "abc-123" })
|
|
53
|
+
const parsed = JSON.parse(json)
|
|
54
|
+
expect(parsed.type).toBe("connected")
|
|
55
|
+
expect(parsed.clientId).toBe("abc-123")
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it("serializes a streaming message", () => {
|
|
59
|
+
const json = serializeServerMessage({ type: "ai_streaming", chunk: "Hello " })
|
|
60
|
+
expect(JSON.parse(json).chunk).toBe("Hello ")
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it("serializes a pong message", () => {
|
|
64
|
+
const json = serializeServerMessage({ type: "pong" })
|
|
65
|
+
expect(JSON.parse(json).type).toBe("pong")
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it("serializes an ai_complete message", () => {
|
|
69
|
+
const json = serializeServerMessage({
|
|
70
|
+
type: "ai_complete", result: "Done", sessionId: "s1", cost: 0.05, turns: 3
|
|
71
|
+
})
|
|
72
|
+
const parsed = JSON.parse(json)
|
|
73
|
+
expect(parsed.sessionId).toBe("s1")
|
|
74
|
+
expect(parsed.cost).toBe(0.05)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it("serializes an ai_error message", () => {
|
|
78
|
+
const json = serializeServerMessage({ type: "ai_error", error: "something broke" })
|
|
79
|
+
expect(JSON.parse(json).error).toBe("something broke")
|
|
80
|
+
})
|
|
81
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface ElementSelection {
|
|
2
|
+
tagName: string
|
|
3
|
+
id: string
|
|
4
|
+
classList: string[]
|
|
5
|
+
cssSelector: string
|
|
6
|
+
textContent: string
|
|
7
|
+
outerHTML: string
|
|
8
|
+
attributes: Record<string, string>
|
|
9
|
+
boundingRect: { top: number; left: number; width: number; height: number }
|
|
10
|
+
computedStyles: {
|
|
11
|
+
color: string
|
|
12
|
+
backgroundColor: string
|
|
13
|
+
fontSize: string
|
|
14
|
+
fontFamily: string
|
|
15
|
+
padding: string
|
|
16
|
+
margin: string
|
|
17
|
+
}
|
|
18
|
+
parentChain: string[]
|
|
19
|
+
siblingCount: number
|
|
20
|
+
childCount: number
|
|
21
|
+
}
|
package/src/index.ts
ADDED
package/src/protocol.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ElementSelection } from "./element-selection.js"
|
|
2
|
+
|
|
3
|
+
// --- Client → Server ---
|
|
4
|
+
export type ClientMessage =
|
|
5
|
+
| { type: "ping" }
|
|
6
|
+
| { type: "prompt"; route: string; element: ElementSelection; prompt: string }
|
|
7
|
+
| { type: "raw_prompt"; prompt: string }
|
|
8
|
+
| { type: "reset_session" }
|
|
9
|
+
|
|
10
|
+
// --- Server → Client ---
|
|
11
|
+
export type ServerMessage =
|
|
12
|
+
| { type: "connected"; clientId: string }
|
|
13
|
+
| { type: "pong" }
|
|
14
|
+
| { type: "ai_streaming"; chunk: string }
|
|
15
|
+
| { type: "tool_use"; tool: string; input: Record<string, unknown> }
|
|
16
|
+
| { type: "ai_complete"; result: string; sessionId: string; cost: number; turns: number }
|
|
17
|
+
| { type: "ai_error"; error: string }
|
|
18
|
+
| { type: "session_reset"; newSessionId: string }
|
|
19
|
+
|
|
20
|
+
export function parseClientMessage(raw: string): ClientMessage {
|
|
21
|
+
const msg = JSON.parse(raw)
|
|
22
|
+
if (!msg || typeof msg.type !== "string") {
|
|
23
|
+
throw new Error("Invalid message: missing type field")
|
|
24
|
+
}
|
|
25
|
+
return msg as ClientMessage
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function serializeServerMessage(msg: ServerMessage): string {
|
|
29
|
+
return JSON.stringify(msg)
|
|
30
|
+
}
|
package/tsconfig.json
ADDED