@magneticjs/cli 0.1.3 → 0.1.5

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/cli.js CHANGED
@@ -151,21 +151,24 @@ import { build } from "esbuild";
151
151
  import { join as join2, dirname as dirname2 } from "node:path";
152
152
  import { mkdirSync, existsSync as existsSync2, statSync as statSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, copyFileSync } from "node:fs";
153
153
  import { createRequire } from "node:module";
154
- function copyTransportWasm(appDir, monorepoRoot) {
154
+ function copyFrameworkAsset(filename, appDir, monorepoRoot) {
155
155
  const publicDir = join2(appDir, "public");
156
- const dest = join2(publicDir, "transport.wasm");
156
+ const dest = join2(publicDir, filename);
157
157
  if (existsSync2(dest)) return true;
158
158
  const candidates = [];
159
159
  if (monorepoRoot) {
160
- candidates.push(join2(monorepoRoot, "js/packages/magnetic-server/wasm/transport.wasm"));
160
+ candidates.push(join2(monorepoRoot, "js/packages/magnetic-server/wasm", filename));
161
+ if (filename === "magnetic.js") {
162
+ candidates.push(join2(monorepoRoot, "js/packages/sdk-web-runtime/dist/magnetic.min.js"));
163
+ }
161
164
  }
162
165
  try {
163
166
  const require2 = createRequire(join2(appDir, "package.json"));
164
167
  const serverPkg = require2.resolve("@magneticjs/server");
165
- candidates.push(join2(dirname2(serverPkg), "..", "wasm", "transport.wasm"));
168
+ candidates.push(join2(dirname2(serverPkg), "..", "wasm", filename));
166
169
  } catch {
167
170
  }
168
- candidates.push(join2(import.meta.dirname || __dirname, "..", "wasm", "transport.wasm"));
171
+ candidates.push(join2(import.meta.dirname || __dirname, "..", "wasm", filename));
169
172
  for (const src of candidates) {
170
173
  if (existsSync2(src)) {
171
174
  mkdirSync(publicDir, { recursive: true });
@@ -175,6 +178,12 @@ function copyTransportWasm(appDir, monorepoRoot) {
175
178
  }
176
179
  return false;
177
180
  }
181
+ function copyTransportWasm(appDir, monorepoRoot) {
182
+ return copyFrameworkAsset("transport.wasm", appDir, monorepoRoot);
183
+ }
184
+ function copyClientRuntime(appDir, monorepoRoot) {
185
+ return copyFrameworkAsset("magnetic.js", appDir, monorepoRoot);
186
+ }
178
187
  async function bundleApp(opts) {
179
188
  const outDir = opts.outDir || join2(opts.appDir, "dist");
180
189
  const outFile = opts.outFile || "app.js";
@@ -182,6 +191,7 @@ async function bundleApp(opts) {
182
191
  if (!existsSync2(outDir)) {
183
192
  mkdirSync(outDir, { recursive: true });
184
193
  }
194
+ copyClientRuntime(opts.appDir, opts.monorepoRoot);
185
195
  copyTransportWasm(opts.appDir, opts.monorepoRoot);
186
196
  const alias = {};
187
197
  if (opts.monorepoRoot) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magneticjs/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Magnetic CLI — build, dev, and deploy server-driven UI apps",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,8 +4,8 @@
4
4
  * for the current platform during `npm install @magneticjs/cli`.
5
5
  *
6
6
  * Update strategy:
7
- * - Version is read from ../package.json (matches @magneticjs/cli version)
8
- * - When user runs `npm update @magneticjs/cli`, this script re-runs
7
+ * - SERVER_VERSION is hardcoded (decoupled from npm package version)
8
+ * - Only bump SERVER_VERSION when a new binary is uploaded to GitHub Releases
9
9
  * - Binary version is tracked in bin/.version
10
10
  * - If version mismatch → re-download; if match → skip
11
11
  *
@@ -26,18 +26,18 @@ const binDir = join(pkgDir, 'bin');
26
26
  const binPath = join(binDir, 'magnetic-v8-server');
27
27
  const versionFile = join(binDir, '.version');
28
28
 
29
- // Read version from package.json (stays in sync with npm version)
30
- const pkg = JSON.parse(readFileSync(join(pkgDir, 'package.json'), 'utf-8'));
31
- const version = pkg.version;
29
+ // Server binary version decoupled from npm package version.
30
+ // Only bump this when a new binary is uploaded to GitHub Releases.
31
+ const SERVER_VERSION = '0.1.0';
32
32
 
33
- // Check if installed binary matches current CLI version
33
+ // Check if installed binary matches current server version
34
34
  if (existsSync(binPath) && existsSync(versionFile)) {
35
35
  const installed = readFileSync(versionFile, 'utf-8').trim();
36
- if (installed === version) {
37
- console.log(`[magnetic] Server binary v${version} already installed, skipping`);
36
+ if (installed === SERVER_VERSION) {
37
+ console.log(`[magnetic] Server binary v${SERVER_VERSION} already installed, skipping`);
38
38
  process.exit(0);
39
39
  }
40
- console.log(`[magnetic] Server binary outdated (${installed} → ${version}), updating...`);
40
+ console.log(`[magnetic] Server binary outdated (${installed} → ${SERVER_VERSION}), updating...`);
41
41
  }
42
42
 
43
43
  // Determine platform target
@@ -60,7 +60,7 @@ if (!target) {
60
60
  }
61
61
 
62
62
  const baseUrl = process.env.MAGNETIC_BINARY_URL ||
63
- `https://github.com/inventhq/magnetic/releases/download/v${version}`;
63
+ `https://github.com/inventhq/magnetic/releases/download/v${SERVER_VERSION}`;
64
64
  const filename = `magnetic-v8-server-${target}.tar.gz`;
65
65
  const url = `${baseUrl}/${filename}`;
66
66
 
@@ -91,8 +91,8 @@ try {
91
91
 
92
92
  if (downloaded && existsSync(binPath)) {
93
93
  chmodSync(binPath, 0o755);
94
- writeFileSync(versionFile, version);
95
- console.log(`[magnetic] ✓ Server binary v${version} installed: ${binPath}`);
94
+ writeFileSync(versionFile, SERVER_VERSION);
95
+ console.log(`[magnetic] ✓ Server binary v${SERVER_VERSION} installed: ${binPath}`);
96
96
  } else {
97
97
  throw new Error(`Download failed or binary not found after extraction`);
98
98
  }
package/src/bundler.ts CHANGED
@@ -27,33 +27,35 @@ export interface BundleResult {
27
27
  }
28
28
 
29
29
  /**
30
- * Copy transport.wasm into the app's public/ directory.
31
- * Looks in @magneticjs/server/wasm/ (npm) or monorepo path.
30
+ * Copy a framework asset into the app's public/ directory.
31
+ * Searches monorepo, npm-installed @magneticjs/server, and CLI's own wasm/ dir.
32
32
  */
33
- export function copyTransportWasm(appDir: string, monorepoRoot?: string): boolean {
33
+ function copyFrameworkAsset(filename: string, appDir: string, monorepoRoot?: string): boolean {
34
34
  const publicDir = join(appDir, 'public');
35
- const dest = join(publicDir, 'transport.wasm');
35
+ const dest = join(publicDir, filename);
36
36
 
37
37
  // Already there — skip
38
38
  if (existsSync(dest)) return true;
39
39
 
40
- // Candidate locations for the WASM file
41
40
  const candidates: string[] = [];
42
41
 
43
- // Monorepo path
42
+ // Monorepo paths
44
43
  if (monorepoRoot) {
45
- candidates.push(join(monorepoRoot, 'js/packages/magnetic-server/wasm/transport.wasm'));
44
+ candidates.push(join(monorepoRoot, 'js/packages/magnetic-server/wasm', filename));
45
+ if (filename === 'magnetic.js') {
46
+ candidates.push(join(monorepoRoot, 'js/packages/sdk-web-runtime/dist/magnetic.min.js'));
47
+ }
46
48
  }
47
49
 
48
50
  // npm-installed @magneticjs/server
49
51
  try {
50
52
  const require = createRequire(join(appDir, 'package.json'));
51
53
  const serverPkg = require.resolve('@magneticjs/server');
52
- candidates.push(join(dirname(serverPkg), '..', 'wasm', 'transport.wasm'));
54
+ candidates.push(join(dirname(serverPkg), '..', 'wasm', filename));
53
55
  } catch {}
54
56
 
55
57
  // CLI's own bundled copy (sibling to dist/)
56
- candidates.push(join(import.meta.dirname || __dirname, '..', 'wasm', 'transport.wasm'));
58
+ candidates.push(join(import.meta.dirname || __dirname, '..', 'wasm', filename));
57
59
 
58
60
  for (const src of candidates) {
59
61
  if (existsSync(src)) {
@@ -66,6 +68,20 @@ export function copyTransportWasm(appDir: string, monorepoRoot?: string): boolea
66
68
  return false;
67
69
  }
68
70
 
71
+ /**
72
+ * Copy transport.wasm into the app's public/ directory.
73
+ */
74
+ export function copyTransportWasm(appDir: string, monorepoRoot?: string): boolean {
75
+ return copyFrameworkAsset('transport.wasm', appDir, monorepoRoot);
76
+ }
77
+
78
+ /**
79
+ * Copy magnetic.js client runtime into the app's public/ directory.
80
+ */
81
+ export function copyClientRuntime(appDir: string, monorepoRoot?: string): boolean {
82
+ return copyFrameworkAsset('magnetic.js', appDir, monorepoRoot);
83
+ }
84
+
69
85
  /**
70
86
  * Bundle the generated bridge code into an IIFE for V8 consumption.
71
87
  * Uses esbuild with stdin so no temp file is needed.
@@ -79,7 +95,8 @@ export async function bundleApp(opts: BundleOptions): Promise<BundleResult> {
79
95
  mkdirSync(outDir, { recursive: true });
80
96
  }
81
97
 
82
- // Ensure transport.wasm is in public/
98
+ // Ensure framework assets are in public/
99
+ copyClientRuntime(opts.appDir, opts.monorepoRoot);
83
100
  copyTransportWasm(opts.appDir, opts.monorepoRoot);
84
101
 
85
102
  // Resolve @magneticjs/server — in monorepo use actual path, otherwise npm package
@@ -0,0 +1 @@
1
+ (function(d) {var M = self.Magnetic = {};var root = null;var es = null;var wasm = null;var status = "disconnected";var queue = [];var keys = {};var deb = {};var lastHash = "";var enc = new TextEncoder();M.status = function() { return status; };M.connect = function(url, mount) {root = typeof mount == "string" ? d.querySelector(mount) : mount;es = new EventSource(url);es.onmessage = function(ev) {try {var raw = ev.data;if (wasm && wasm.store) {var bytes = enc.encode(raw);if (bytes.length <= 16384) {new Uint8Array(wasm.memory.buffer).set(bytes, wasm.input_ptr());if (wasm.store(bytes.length) === 0) return;}} else {var h = fnv(raw);if (h === lastHash) return;lastHash = h;}apply(JSON.parse(raw));} catch(e) { console.error("[magnetic] SSE error:", e); }};es.onerror = function() {if (wasm) status = "offline";};status = "connected";bind();};M.disconnect = function() {if (es) { es.close(); es = null; }status = "disconnected";};function apply(snap) {if (!root || !snap || !snap.root) return;var n = snap.root;if (n.key && keys[n.key] && keys[n.key].parentNode === root) {patch(keys[n.key], n);return;}root.textContent = "";root.appendChild(create(n));}M._apply = apply;function create(n) {var el = d.createElement(n.tag);if (n.key) { el.dataset.key = n.key; keys[n.key] = el; }setAttrs(el, n);if (n.events) for (var v in n.events) el.dataset["a_" + v] = n.events[v];if (n.text != null) el.textContent = n.text;if (n.children) for (var i = 0; i < n.children.length; i++) el.appendChild(create(n.children[i]));return el;}function patch(el, n) {setAttrs(el, n);if (n.events) {for (var v in n.events) el.dataset["a_" + v] = n.events[v];}var da = el.dataset;for (var dk in da) {if (dk.indexOf("a_") === 0) {var ev = dk.slice(2);if (!n.events || !(ev in n.events)) delete da[dk];}}if (n.children) {reconcile(el, n.children);} else if (n.text != null && n.tag != "input" && n.tag != "textarea") {el.textContent = n.text;} else if (!n.children && n.text == null && n.tag != "input" && n.tag != "textarea") {while (el.firstChild) {purgeKeys(el.firstChild);el.removeChild(el.firstChild);}}}function purgeKeys(el) {var k = el.dataset ? el.dataset.key : null;if (k) delete keys[k];var ch = el.firstChild;while (ch) { purgeKeys(ch); ch = ch.nextSibling; }}function reconcile(parent, descs) {var i, c, el, k;var newEls = [];var wantKeys = {};for (i = 0; i < descs.length; i++) {c = descs[i];if (c.key && keys[c.key]) {el = keys[c.key];patch(el, c);wantKeys[c.key] = true;} else {el = create(c);if (c.key) wantKeys[c.key] = true;}newEls.push(el);}var ch = parent.firstChild;while (ch) {var nx = ch.nextSibling;k = ch.dataset ? ch.dataset.key : null;if (!k || !wantKeys[k]) {purgeKeys(ch);parent.removeChild(ch);}ch = nx;}for (i = 0; i < newEls.length; i++) {if (parent.childNodes[i] !== newEls[i]) {parent.insertBefore(newEls[i], parent.childNodes[i] || null);}}}function setAttrs(el, n) {if (n.attrs) for (var k in n.attrs) el.setAttribute(k, n.attrs[k]);}function bind() {d.addEventListener("click", function(e) {var t = e.target.closest("[data-a_click]");if (t) { e.preventDefault(); send(t.dataset.a_click, {}); }});d.addEventListener("submit", function(e) {var t = e.target.closest("[data-a_submit]");if (t) {e.preventDefault();var p = {}, f = new FormData(t);f.forEach(function(v, k) { p[k] = v; });send(t.dataset.a_submit, p);t.querySelectorAll("input").forEach(function(i) { i.value = ""; });}});d.addEventListener("input", function(e) {var t = e.target.closest("[data-a_input]");if (t) {var a = t.dataset.a_input;clearTimeout(deb[a]);deb[a] = setTimeout(function() { send(a, { value: t.value }); }, 300);}});}M.send = send;function send(action, payload) {if (action.indexOf("navigate:") === 0) {var path = action.slice(9);history.pushState({}, "", path);action = "navigate";payload = { path: path };}var body = JSON.stringify({ action: action, payload: payload });fetch("/actions/" + encodeURIComponent(action), {method: "POST",headers: { "Content-Type": "application/json" },body: body}).then(function(r) { return r.text(); }).then(function(raw) {if (!raw || raw[0] !== "{") return;if (wasm && wasm.store) {var bytes = enc.encode(raw);if (bytes.length <= 16384) {new Uint8Array(wasm.memory.buffer).set(bytes, wasm.input_ptr());wasm.store(bytes.length);}} else {lastHash = fnv(raw);}try { apply(JSON.parse(raw)); } catch(e) {}}).catch(function() {});if (status != "connected") queue.push(body);}self.addEventListener("popstate", function() {send("navigate", { path: location.pathname + location.search });});M.loadWasm = function(url) {if (wasm !== null) return;wasm = 0;fetch(url).then(function(r) { return r.arrayBuffer(); }).then(function(b) { return WebAssembly.instantiate(b, {}); }).then(function(result) {wasm = result.instance.exports;while (queue.length) {var q = JSON.parse(queue.shift());send(q.action, q.payload);}}).catch(function() { wasm = null; });};function fnv(s) {var h = 0x811c9dc5;for (var i = 0; i < s.length; i++) {h ^= s.charCodeAt(i);h = Math.imul(h, 0x01000193);}return h;}})(document);