@jay-framework/editor-server 0.6.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/index.cjs ADDED
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
+ var __publicField = (obj, key, value) => {
5
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
6
+ return value;
7
+ };
8
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
9
+ const socket_io = require("socket.io");
10
+ const http = require("http");
11
+ const getPort = require("get-port");
12
+ const editorProtocol = require("@jay-framework/editor-protocol");
13
+ class EditorServer {
14
+ constructor(options) {
15
+ __publicField(this, "io", null);
16
+ __publicField(this, "httpServer", null);
17
+ __publicField(this, "port", null);
18
+ __publicField(this, "editorId", null);
19
+ __publicField(this, "portRange");
20
+ __publicField(this, "onEditorId");
21
+ __publicField(this, "handlers", {});
22
+ this.portRange = options.portRange || [3101, 3200];
23
+ this.onEditorId = options.onEditorId;
24
+ this.editorId = options.editorId || null;
25
+ }
26
+ async start() {
27
+ this.port = await getPort({ port: this.portRange });
28
+ this.httpServer = http.createServer((req, res) => {
29
+ if (req.url?.startsWith("/editor-connect")) {
30
+ this.handlePortDiscovery(req, res);
31
+ } else {
32
+ res.writeHead(404);
33
+ res.end("Not Found");
34
+ }
35
+ });
36
+ this.io = new socket_io.Server(this.httpServer, {
37
+ cors: {
38
+ origin: "*",
39
+ methods: ["GET", "POST"]
40
+ }
41
+ });
42
+ this.setupSocketHandlers();
43
+ return new Promise((resolve, reject) => {
44
+ this.httpServer.listen(this.port, () => {
45
+ console.log(`Editor server started on port ${this.port}`);
46
+ resolve({ port: this.port, editorId: this.editorId || "init" });
47
+ });
48
+ this.httpServer.on("error", reject);
49
+ });
50
+ }
51
+ async stop() {
52
+ if (this.io) {
53
+ this.io.close();
54
+ }
55
+ if (this.httpServer) {
56
+ this.httpServer.close();
57
+ }
58
+ }
59
+ // DevServerProtocol implementation
60
+ onPublish(callback) {
61
+ this.handlers.publish = callback;
62
+ }
63
+ onSaveImage(callback) {
64
+ this.handlers.saveImage = callback;
65
+ }
66
+ onHasImage(callback) {
67
+ this.handlers.hasImage = callback;
68
+ }
69
+ handlePortDiscovery(req, res) {
70
+ const url = new URL(req.url, `http://localhost:${this.port}`);
71
+ const tabId = url.searchParams.get("id");
72
+ if (!tabId) {
73
+ res.writeHead(400, { "Content-Type": "application/json" });
74
+ res.end(JSON.stringify({ error: "Missing tab ID" }));
75
+ return;
76
+ }
77
+ if (!this.editorId) {
78
+ this.editorId = tabId;
79
+ this.onEditorId && this.onEditorId(tabId);
80
+ }
81
+ const response = {
82
+ status: this.editorId === tabId ? "configured" : "init",
83
+ id: this.editorId,
84
+ port: this.port
85
+ };
86
+ res.writeHead(200, { "Content-Type": "application/json" });
87
+ res.end(JSON.stringify(response));
88
+ }
89
+ setupSocketHandlers() {
90
+ if (!this.io)
91
+ return;
92
+ this.io.on("connection", (socket) => {
93
+ console.log(`Editor connected: ${socket.id}`);
94
+ socket.on("protocol-message", async (message) => {
95
+ try {
96
+ const response = await this.handleProtocolMessage(message);
97
+ socket.emit("protocol-response", response);
98
+ } catch (error) {
99
+ const errorPayload = {
100
+ type: message.payload.type,
101
+ success: false,
102
+ error: error instanceof Error ? error.message : "Unknown error"
103
+ };
104
+ const errorResponse = editorProtocol.createProtocolResponse(message.id, errorPayload);
105
+ socket.emit("protocol-response", errorResponse);
106
+ }
107
+ });
108
+ socket.on("disconnect", () => {
109
+ console.log(`Editor disconnected: ${socket.id}`);
110
+ });
111
+ });
112
+ }
113
+ async handleProtocolMessage(message) {
114
+ const { id, payload } = message;
115
+ switch (payload.type) {
116
+ case "publish":
117
+ if (!this.handlers.publish) {
118
+ throw new Error("Publish handler not registered");
119
+ }
120
+ const publishResult = await this.handlers.publish(payload);
121
+ return editorProtocol.createProtocolResponse(id, publishResult);
122
+ case "saveImage":
123
+ if (!this.handlers.saveImage) {
124
+ throw new Error("Save image handler not registered");
125
+ }
126
+ const saveResult = await this.handlers.saveImage(payload);
127
+ return editorProtocol.createProtocolResponse(id, saveResult);
128
+ case "hasImage":
129
+ if (!this.handlers.hasImage) {
130
+ throw new Error("Has image handler not registered");
131
+ }
132
+ const hasResult = await this.handlers.hasImage(payload);
133
+ return editorProtocol.createProtocolResponse(id, hasResult);
134
+ default:
135
+ throw new Error(`Unknown message type: ${payload.type}`);
136
+ }
137
+ }
138
+ }
139
+ function createEditorServer(options) {
140
+ return new EditorServer(options);
141
+ }
142
+ exports.EditorServer = EditorServer;
143
+ exports.createEditorServer = createEditorServer;
@@ -0,0 +1,31 @@
1
+ import { DevServerProtocol, PublishMessage, PublishResponse, SaveImageMessage, SaveImageResponse, HasImageMessage, HasImageResponse } from '@jay-framework/editor-protocol';
2
+
3
+ interface EditorServerOptions {
4
+ editorId?: string;
5
+ onEditorId?: (editorId: string) => void;
6
+ portRange?: [number, number];
7
+ }
8
+ declare class EditorServer implements DevServerProtocol {
9
+ private io;
10
+ private httpServer;
11
+ private port;
12
+ private editorId;
13
+ private portRange;
14
+ private onEditorId;
15
+ private handlers;
16
+ constructor(options: EditorServerOptions);
17
+ start(): Promise<{
18
+ port: number;
19
+ editorId: string;
20
+ }>;
21
+ stop(): Promise<void>;
22
+ onPublish(callback: (params: PublishMessage) => Promise<PublishResponse>): void;
23
+ onSaveImage(callback: (params: SaveImageMessage) => Promise<SaveImageResponse>): void;
24
+ onHasImage(callback: (params: HasImageMessage) => Promise<HasImageResponse>): void;
25
+ private handlePortDiscovery;
26
+ private setupSocketHandlers;
27
+ private handleProtocolMessage;
28
+ }
29
+ declare function createEditorServer(options: EditorServerOptions): EditorServer;
30
+
31
+ export { EditorServer, type EditorServerOptions, createEditorServer };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@jay-framework/editor-server",
3
+ "version": "0.6.0",
4
+ "type": "module",
5
+ "license": "Apache-2.0",
6
+ "main": "dist/index.cjs",
7
+ "files": [
8
+ "dist",
9
+ "readme.md"
10
+ ],
11
+ "scripts": {
12
+ "build": "npm run build:js && npm run build:types",
13
+ "build:watch": "npm run build:js -- --watch & npm run build:types -- --watch",
14
+ "build:js": "vite build",
15
+ "build:types": "tsup lib/index.ts --dts-only --format esm",
16
+ "build:check-types": "tsc",
17
+ "clean": "rimraf dist",
18
+ "confirm": "npm run clean && npm run build && npm run build:check-types && npm run test",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest"
21
+ },
22
+ "dependencies": {
23
+ "@jay-framework/editor-protocol": "^0.6.0",
24
+ "get-port": "^7.0.0",
25
+ "socket.io": "^4.7.4",
26
+ "uuid": "^9.0.1",
27
+ "yaml": "^2.3.4"
28
+ },
29
+ "devDependencies": {
30
+ "@jay-framework/dev-environment": "^0.6.0",
31
+ "@jay-framework/editor-client": "^0.6.0",
32
+ "@jay-framework/jay-cli": "^0.6.0",
33
+ "@types/express": "^5.0.2",
34
+ "@types/node": "^22.15.21",
35
+ "@types/uuid": "^9.0.7",
36
+ "rimraf": "^5.0.5",
37
+ "tsup": "^8.0.1",
38
+ "typescript": "^5.3.3",
39
+ "vite": "^5.0.11",
40
+ "vitest": "^1.2.1"
41
+ }
42
+ }
package/readme.md ADDED
@@ -0,0 +1,71 @@
1
+ # @jay-framework/editor-server
2
+
3
+ Socket.io server implementation for Jay dev servers to handle editor integration.
4
+
5
+ ## Overview
6
+
7
+ This package provides a Socket.io server that can be integrated into Jay dev servers to handle real-time communication with editor applications. It includes:
8
+
9
+ - Port discovery and automatic port allocation
10
+ - Socket.io server setup with CORS support
11
+ - Protocol message handling for publish, saveImage, and hasImage operations
12
+ - Default protocol handlers for file operations
13
+ - Configuration management via `.jay` files
14
+ - Memory filesystem support for testing
15
+
16
+ ## Usage
17
+
18
+ ```typescript
19
+ import { createEditorServer, createDefaultHandlers } from '@jay-framework/editor-server';
20
+
21
+ // Create the editor server
22
+ const server = createEditorServer({
23
+ projectRoot: '/path/to/project',
24
+ portRange: [3101, 3200],
25
+ });
26
+
27
+ const handlePublish: EditorProtocol['publish'] = () => {}; // callback implementation
28
+ const handleSaveImage: EditorProtocol['saveImage'] = () => {}; // callback implementation
29
+ const handleHasImage: EditorProtocol['hasImage'] = () => {}; // callback implementation
30
+
31
+ // Register protocol handlers
32
+ server.onPublish(handlePublish);
33
+ server.onSaveImage(handleSaveImage);
34
+ server.onHasImage(handleHasImage);
35
+
36
+ // Start the server
37
+ const { port, editorId } = await server.start();
38
+ console.log(`Editor server running on port ${port} with ID ${editorId}`);
39
+
40
+ // Stop the server when done
41
+ await server.stop();
42
+ ```
43
+
44
+ ## Features
45
+
46
+ ### Port Discovery
47
+
48
+ - Automatically finds available ports in the configured range
49
+ - Provides HTTP endpoint for editor port discovery
50
+ - Supports both "init" mode and "configured" mode
51
+
52
+ ### Protocol Support
53
+
54
+ - **Publish**: Saves jay-html files to the project
55
+ - **Save Image**: Saves base64 image data to assets directory
56
+ - **Has Image**: Checks if an image already exists
57
+
58
+ ### Protocol Message Structure
59
+
60
+ All messages use a wrapper structure with `id`, `timestamp`, and `payload` fields for reliable communication.
61
+
62
+ ### Configuration
63
+
64
+ - optional `editorId` config
65
+ - if omitted, starts with `init` mode supporting discovery of `editorId` and callback `onEditorId` to save `editorId`
66
+ - if provided, only accepts connection with the same `editorId`
67
+ - Supports custom port ranges
68
+
69
+ ## Integration with Dev Server
70
+
71
+ This package is designed to be integrated into the existing `@jay-framework/dev-server` package to provide editor integration capabilities.