@baskerhq/localstage 1.0.1-beta.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,205 @@
1
+ (function () {
2
+ "use strict";
3
+
4
+ // Configuration
5
+ const config = {
6
+ port: (Number.parseInt(window.location.port) + 1) || "9293",
7
+ reconnectInterval: 2000,
8
+ maxReconnectAttempts: 50,
9
+ pingInterval: 25000,
10
+ };
11
+
12
+ let socket = null;
13
+ let reconnectAttempts = 0;
14
+ let pingInterval = null;
15
+ let isReconnecting = false;
16
+
17
+ function log(message, level) {
18
+ level = level || "info";
19
+ if (level === "error") {
20
+ console.error(`[LocalStage Live Reload] ${message}`);
21
+ }
22
+ else {
23
+ console.warn(`[LocalStage Live Reload] ${message}`);
24
+ }
25
+ }
26
+
27
+ function connect() {
28
+ if (socket && socket.readyState === WebSocket.OPEN) {
29
+ return;
30
+ }
31
+
32
+ const wsUrl = `ws://${window.location.hostname}:${config.port}/livereload`;
33
+
34
+ try {
35
+ socket = new WebSocket(wsUrl);
36
+ }
37
+ catch (error) {
38
+ log(`Failed to create WebSocket: ${error.message}`, "error");
39
+ scheduleReconnect();
40
+ return;
41
+ }
42
+
43
+ socket.onopen = function () {
44
+ log("Connected to live reload server");
45
+ reconnectAttempts = 0;
46
+ isReconnecting = false;
47
+ startPingInterval();
48
+ };
49
+
50
+ socket.onmessage = function (event) {
51
+ try {
52
+ const message = JSON.parse(event.data);
53
+ handleMessage(message);
54
+ }
55
+ catch (error) {
56
+ log(`Failed to parse message: ${error.message}`, "error");
57
+ }
58
+ };
59
+
60
+ socket.onclose = function (_event) {
61
+ log("Disconnected from live reload server");
62
+ stopPingInterval();
63
+
64
+ if (!isReconnecting) {
65
+ scheduleReconnect();
66
+ }
67
+ };
68
+
69
+ socket.onerror = function (error) {
70
+ log(`WebSocket error occurred: ${error?.message || "unknown"}`, "error");
71
+ };
72
+ }
73
+
74
+ function handleMessage(message) {
75
+ switch (message.type) {
76
+ case "reload":
77
+ log(`Reloading page due to file changes: ${(message.data.files || []).join(", ")}`);
78
+ showReloadNotification();
79
+
80
+ // Small delay to ensure any pending requests complete
81
+ setTimeout(() => {
82
+ window.location.reload();
83
+ }, 100);
84
+ break;
85
+
86
+ case "ping":
87
+ // Respond to server ping
88
+ if (socket && socket.readyState === WebSocket.OPEN) {
89
+ socket.send(JSON.stringify({
90
+ type: "pong",
91
+ data: { timestamp: Date.now() },
92
+ }));
93
+ }
94
+ break;
95
+
96
+ case "pong":
97
+ // Server responded to our ping, connection is healthy
98
+ break;
99
+
100
+ default:
101
+ log(`Unknown message type: ${message.type}`, "warn");
102
+ }
103
+ }
104
+
105
+ function scheduleReconnect() {
106
+ if (reconnectAttempts >= config.maxReconnectAttempts) {
107
+ log("Max reconnection attempts reached. Please refresh the page manually.", "error");
108
+ return;
109
+ }
110
+
111
+ isReconnecting = true;
112
+ reconnectAttempts++;
113
+
114
+ log(`Attempting to reconnect in ${config.reconnectInterval / 1000}s... (attempt ${reconnectAttempts}/${config.maxReconnectAttempts})`);
115
+
116
+ setTimeout(() => {
117
+ connect();
118
+ }, config.reconnectInterval);
119
+ }
120
+
121
+ function startPingInterval() {
122
+ stopPingInterval();
123
+
124
+ pingInterval = setInterval(() => {
125
+ if (socket && socket.readyState === WebSocket.OPEN) {
126
+ socket.send(JSON.stringify({
127
+ type: "ping",
128
+ data: { timestamp: Date.now() },
129
+ }));
130
+ }
131
+ }, config.pingInterval);
132
+ }
133
+
134
+ function stopPingInterval() {
135
+ if (pingInterval) {
136
+ clearInterval(pingInterval);
137
+ pingInterval = null;
138
+ }
139
+ }
140
+
141
+ function showReloadNotification() {
142
+ const notification = document.createElement("div");
143
+ notification.style.cssText = [
144
+ "position: fixed",
145
+ "top: 50%",
146
+ "left: 50%",
147
+ "transform: translate(-50%, -50%)",
148
+ "padding: 16px 24px",
149
+ "background: rgba(0, 0, 0, 0.8)",
150
+ "color: white",
151
+ "border-radius: 8px",
152
+ "font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
153
+ "font-size: 14px",
154
+ "font-weight: 500",
155
+ "z-index: 10001",
156
+ "backdrop-filter: blur(4px)",
157
+ ].join(";");
158
+
159
+ notification.textContent = "🔄 Reloading...";
160
+
161
+ document.body.appendChild(notification);
162
+
163
+ setTimeout(() => {
164
+ if (notification.parentNode) {
165
+ notification.parentNode.removeChild(notification);
166
+ }
167
+ }, 2000);
168
+ }
169
+
170
+ // Initialize when DOM is ready
171
+ function initialize() {
172
+ log("Initializing live reload client");
173
+ connect();
174
+ }
175
+
176
+ // Handle page visibility changes
177
+ document.addEventListener("visibilitychange", () => {
178
+ if (document.hidden) {
179
+ // Page is hidden, stop pinging
180
+ stopPingInterval();
181
+ }
182
+ else {
183
+ // Page is visible again, restart pinging if connected
184
+ if (socket && socket.readyState === WebSocket.OPEN) {
185
+ startPingInterval();
186
+ }
187
+ }
188
+ });
189
+
190
+ // Handle page unload
191
+ window.addEventListener("beforeunload", () => {
192
+ if (socket) {
193
+ socket.close();
194
+ }
195
+ stopPingInterval();
196
+ });
197
+
198
+ // Start when DOM is ready
199
+ if (document.readyState === "loading") {
200
+ document.addEventListener("DOMContentLoaded", initialize);
201
+ }
202
+ else {
203
+ initialize();
204
+ }
205
+ })();
package/package.json ADDED
@@ -0,0 +1,102 @@
1
+ {
2
+ "name": "@baskerhq/localstage",
3
+ "type": "module",
4
+ "version": "1.0.1-beta.0",
5
+ "description": "Local development server for Basker themes - CLI tool for theme development",
6
+ "author": "Basker Ltd.",
7
+ "license": "MIT",
8
+ "homepage": "https://github.com/BaskerHQ/basker-cms/tree/main/packages/localstage",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/BaskerHQ/basker-cms.git",
12
+ "directory": "packages/localstage"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/BaskerHQ/basker-cms/issues"
16
+ },
17
+ "keywords": [
18
+ "basker",
19
+ "cms",
20
+ "theme",
21
+ "development",
22
+ "liquid",
23
+ "hono",
24
+ "local",
25
+ "cli"
26
+ ],
27
+ "sideEffects": false,
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.js",
32
+ "default": "./dist/index.js"
33
+ }
34
+ },
35
+ "main": "./dist/index.js",
36
+ "bin": {
37
+ "localstage": "./dist/cli.js",
38
+ "basker": "./dist/basker.js"
39
+ },
40
+ "files": [
41
+ "README.md",
42
+ "dist/**/*",
43
+ "package.json"
44
+ ],
45
+ "engines": {
46
+ "node": ">=22.12.0"
47
+ },
48
+ "scripts": {
49
+ "dev": "LOCALSTAGE_THEME_PATH=./example_theme tsx watch src/cli.ts",
50
+ "dev:localstage": "LOCALSTAGE_THEME_PATH=./example_theme tsx watch src/cli.ts",
51
+ "start": "node dist/cli.js",
52
+ "cli": "tsx src/cli.ts",
53
+ "build": "tsup",
54
+ "link:global": "pnpm build && pnpm link --global",
55
+ "install:local": "bash ./scripts/install-local-cli.sh",
56
+ "prepublish:check": "bash ./scripts/prepublish-check.sh",
57
+ "release:cli": "bash ./scripts/release-cli.sh",
58
+ "prepublishOnly": "npm run build",
59
+ "lint": "eslint .",
60
+ "lint:fix": "eslint . --fix",
61
+ "typecheck": "tsc --noEmit",
62
+ "test": "vitest run --passWithNoTests"
63
+ },
64
+ "dependencies": {
65
+ "@hono/node-server": "1.19.9",
66
+ "@oclif/core": "4.2.5",
67
+ "@payloadcms/sdk": "3.72.0",
68
+ "chokidar": "^3.5.3",
69
+ "date-fns": "4.1.0",
70
+ "hono": "4.11.4",
71
+ "liquidjs": "10.24.0",
72
+ "ws": "^8.18.0"
73
+ },
74
+ "devDependencies": {
75
+ "@antfu/eslint-config": "4.16.1",
76
+ "@baskerhq/frontstage": "workspace:*",
77
+ "@baskerhq/liquidjs": "workspace:*",
78
+ "@baskerhq/ui": "workspace:^",
79
+ "@types/node": "22.13.14",
80
+ "eslint": "^9.34.0",
81
+ "eslint-plugin-import-x": "^4.15.2",
82
+ "eslint-plugin-jsdoc": "^51.2.3",
83
+ "eslint-plugin-tsdoc": "^0.4.0",
84
+ "javascript-obfuscator": "^4.1.0",
85
+ "tsup": "^8.5.1",
86
+ "tsx": "^4.19.4",
87
+ "typescript": "5.8.3"
88
+ },
89
+ "oclif": {
90
+ "bin": "basker",
91
+ "binAliases": [
92
+ "localstage"
93
+ ],
94
+ "commands": "./dist/commands",
95
+ "topicSeparator": " ",
96
+ "topics": {
97
+ "theme": {
98
+ "description": "Theme development commands"
99
+ }
100
+ }
101
+ }
102
+ }