@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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=protocol.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=element-selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-selection.js","sourceRoot":"","sources":["../src/element-selection.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ export * from "./protocol.js";
2
+ export * from "./element-selection.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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,3 @@
1
+ export * from "./protocol.js";
2
+ export * from "./element-selection.js";
3
+ //# sourceMappingURL=index.js.map
@@ -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"}
@@ -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
@@ -0,0 +1,2 @@
1
+ export * from "./protocol.js"
2
+ export * from "./element-selection.js"
@@ -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
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src"]
8
+ }
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from "vitest/config"
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ include: ["src/**/*.test.ts"],
6
+ },
7
+ })