beam.com 0.1.0-rc.1

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.
Files changed (102) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +97 -0
  3. package/README.md +369 -0
  4. package/js/beam.com.mjs +33 -0
  5. package/js/download.mjs +78 -0
  6. package/js/node.mjs +75 -0
  7. package/package.json +36 -0
  8. package/runtime/app-com.js +203 -0
  9. package/runtime/beam.mjs +2 -0
  10. package/runtime/beam.wasm +0 -0
  11. package/runtime/browser/beam-wasm.js +2 -0
  12. package/runtime/browser/net.js +7 -0
  13. package/runtime/browser/none.js +4 -0
  14. package/runtime/browser.js +161 -0
  15. package/runtime/cloudflare/durable.js +361 -0
  16. package/runtime/cloudflare/index.js +76 -0
  17. package/runtime/cloudflare/release.js +42 -0
  18. package/runtime/cloudflare/snapshot.js +3 -0
  19. package/runtime/cloudflare/worker.js +1891 -0
  20. package/runtime/deno/app.js +8 -0
  21. package/runtime/deno/beam-wasm.js +3 -0
  22. package/runtime/deno/none.js +3 -0
  23. package/runtime/deno/release-bin.js +46 -0
  24. package/runtime/deno/snapshot-bin.js +11 -0
  25. package/runtime/deno/worker.js +1891 -0
  26. package/runtime/deno.js +346 -0
  27. package/runtime/deno.json +5 -0
  28. package/runtime/durable-global.js +40 -0
  29. package/runtime/durable.js +361 -0
  30. package/runtime/global.js +26 -0
  31. package/runtime/licenses/LICENSE +202 -0
  32. package/runtime/licenses/NOTICE +97 -0
  33. package/runtime/licenses/bcrypt_elixir.txt +87 -0
  34. package/runtime/licenses/cosmopolitan.txt +16 -0
  35. package/runtime/licenses/elixir/Apache-2.0.txt +73 -0
  36. package/runtime/licenses/elixir/LicenseRef-elixir-trademark-policy.txt +98 -0
  37. package/runtime/licenses/elixir/LicenseRef-scancode-unicode.txt +58 -0
  38. package/runtime/licenses/emscripten.txt +102 -0
  39. package/runtime/licenses/exqlite.txt +21 -0
  40. package/runtime/licenses/llvm.txt +311 -0
  41. package/runtime/licenses/musl.txt +193 -0
  42. package/runtime/licenses/otp/BSD-2-Clause.txt +22 -0
  43. package/runtime/licenses/otp/BSD-3-Clause.txt +25 -0
  44. package/runtime/licenses/otp/LicenseRef-IETF-MIB.txt +9 -0
  45. package/runtime/licenses/otp/LicenseRef-RSA-PKCS3.txt +23 -0
  46. package/runtime/licenses/otp/LicenseRef-RSA-PKCS5v2-0.txt +17 -0
  47. package/runtime/licenses/otp/LicenseRef-scancode-ietf-trust.txt +296 -0
  48. package/runtime/licenses/otp/LicenseRef-scancode-ietf.txt +23 -0
  49. package/runtime/licenses/otp/LicenseRef-scancode-w3c-docs-19990405.txt +26 -0
  50. package/runtime/licenses/otp/LicenseRef-scancode-wxwindows-free-doc-3.txt +57 -0
  51. package/runtime/licenses/otp/MIT.txt +17 -0
  52. package/runtime/licenses/otp/SGI-B-2.0.txt +25 -0
  53. package/runtime/licenses/otp/W3C-19980720.txt +55 -0
  54. package/runtime/licenses/picosat.txt +19 -0
  55. package/runtime/licenses/wamr.txt +219 -0
  56. package/runtime/nifs.js +2 -0
  57. package/runtime/package.json +1 -0
  58. package/runtime/page/404.html +49 -0
  59. package/runtime/page/app-com.js +203 -0
  60. package/runtime/page/beam.mjs +2 -0
  61. package/runtime/page/browser/beam-wasm.js +2 -0
  62. package/runtime/page/browser/net.js +7 -0
  63. package/runtime/page/browser/none.js +4 -0
  64. package/runtime/page/browser.js +161 -0
  65. package/runtime/page/index.html +50 -0
  66. package/runtime/page/licenses/LICENSE +202 -0
  67. package/runtime/page/licenses/NOTICE +97 -0
  68. package/runtime/page/licenses/bcrypt_elixir.txt +87 -0
  69. package/runtime/page/licenses/cosmopolitan.txt +16 -0
  70. package/runtime/page/licenses/elixir/Apache-2.0.txt +73 -0
  71. package/runtime/page/licenses/elixir/LicenseRef-elixir-trademark-policy.txt +98 -0
  72. package/runtime/page/licenses/elixir/LicenseRef-scancode-unicode.txt +58 -0
  73. package/runtime/page/licenses/emscripten.txt +102 -0
  74. package/runtime/page/licenses/exqlite.txt +21 -0
  75. package/runtime/page/licenses/llvm.txt +311 -0
  76. package/runtime/page/licenses/musl.txt +193 -0
  77. package/runtime/page/licenses/otp/BSD-2-Clause.txt +22 -0
  78. package/runtime/page/licenses/otp/BSD-3-Clause.txt +25 -0
  79. package/runtime/page/licenses/otp/LicenseRef-IETF-MIB.txt +9 -0
  80. package/runtime/page/licenses/otp/LicenseRef-RSA-PKCS3.txt +23 -0
  81. package/runtime/page/licenses/otp/LicenseRef-RSA-PKCS5v2-0.txt +17 -0
  82. package/runtime/page/licenses/otp/LicenseRef-scancode-ietf-trust.txt +296 -0
  83. package/runtime/page/licenses/otp/LicenseRef-scancode-ietf.txt +23 -0
  84. package/runtime/page/licenses/otp/LicenseRef-scancode-w3c-docs-19990405.txt +26 -0
  85. package/runtime/page/licenses/otp/LicenseRef-scancode-wxwindows-free-doc-3.txt +57 -0
  86. package/runtime/page/licenses/otp/MIT.txt +17 -0
  87. package/runtime/page/licenses/otp/SGI-B-2.0.txt +25 -0
  88. package/runtime/page/licenses/otp/W3C-19980720.txt +55 -0
  89. package/runtime/page/licenses/picosat.txt +19 -0
  90. package/runtime/page/licenses/wamr.txt +219 -0
  91. package/runtime/page/main.js +176 -0
  92. package/runtime/page/runtime-id.js +3 -0
  93. package/runtime/page/scope.js +17 -0
  94. package/runtime/page/sw.js +111 -0
  95. package/runtime/page/vm.js +148 -0
  96. package/runtime/page/worker.js +1891 -0
  97. package/runtime/page/ws-shim.js +143 -0
  98. package/runtime/release/app.js +17 -0
  99. package/runtime/release.json +1 -0
  100. package/runtime/runtime-id.js +3 -0
  101. package/runtime/tcp-proxy.mjs +30 -0
  102. package/runtime/worker.js +1891 -0
@@ -0,0 +1,143 @@
1
+ // The service worker of the page (sw.js) adds this script to each HTML page
2
+ // of the VM. It does four things:
3
+ // - A service worker cannot take a WebSocket. So a socket to this site goes
4
+ // to the VM through the tab of index.html (a message port), and the VM
5
+ // runs its upgrade. The message goes to the top window, the tab of
6
+ // index.html. Another socket is a real one.
7
+ // - A service worker cannot open the SharedWorker of the VM. So it gives a
8
+ // request of this frame to this frame, and this frame gives it to its
9
+ // tab, which gives it to the VM.
10
+ // - A link or a form of the app with a path outside the frame (for
11
+ // example href="/", which an app writes without its base path) goes to
12
+ // the same path in the frame. Without this, the browser leaves the scope
13
+ // of the service worker, and the site gives a 404.
14
+ // - The frame of the app gives its path to its tab at each load and after
15
+ // each change of its history (a navigation of LiveView). The tab keeps it
16
+ // in the fragment of its URL, so a reload and a link keep the page.
17
+ (() => {
18
+ // The same rule as scope.js, which a classic script cannot import.
19
+ // The scope of the frame: data-app of sw.js (this script can come from
20
+ // a CDN), else app/ next to this script (the sw.js of an older site).
21
+ const script = document.currentScript;
22
+ const APP = script?.dataset.app ? new URL(script.dataset.app, location.href)
23
+ : new URL('./app/', script?.src ?? location.href);
24
+ const outside = (u) => u.origin === location.origin && !`${u.pathname}/`.startsWith(APP.pathname);
25
+ const inApp = (u) => new URL(APP.pathname.replace(/\/$/, '') + u.pathname + u.search + u.hash, u).href;
26
+
27
+ addEventListener('click', (e) => {
28
+ const a = e.target instanceof Element ? e.target.closest('a[href]') : null;
29
+ if (!a || a.hasAttribute('download') || (a.target && a.target !== '_self')) return;
30
+ const u = new URL(a.href);
31
+ if (outside(u)) a.href = inApp(u);
32
+ }, true);
33
+ addEventListener('submit', (e) => {
34
+ const form = e.target;
35
+ if (!(form instanceof HTMLFormElement)) return;
36
+ const u = new URL(form.action);
37
+ if (outside(u)) form.action = inApp(u);
38
+ }, true);
39
+
40
+ const Native = window.WebSocket;
41
+ const top = window.top;
42
+ if (!top || top === window) return;
43
+
44
+ // Only the frame of the app (not a frame in it) gives its path.
45
+ if (window.parent === top && location.pathname.startsWith(APP.pathname)) {
46
+ const report = () => top.postMessage({
47
+ type: 'path',
48
+ path: location.pathname.slice(APP.pathname.length - 1) + location.search + location.hash,
49
+ }, location.origin);
50
+ for (const name of ['pushState', 'replaceState']) {
51
+ const native = history[name];
52
+ history[name] = function (...args) {
53
+ const result = native.apply(this, args);
54
+ report();
55
+ return result;
56
+ };
57
+ }
58
+ addEventListener('popstate', report);
59
+ addEventListener('hashchange', report);
60
+ report();
61
+ }
62
+
63
+ const sw = navigator.serviceWorker;
64
+ if (sw) {
65
+ sw.addEventListener('message', (e) => {
66
+ if (e.data?.type === 'fetch') top.postMessage(e.data, location.origin, [...e.ports]);
67
+ });
68
+ sw.startMessages();
69
+ sw.controller?.postMessage({ type: 'frame' });
70
+ }
71
+
72
+ class VmSocket extends EventTarget {
73
+ static CONNECTING = 0;
74
+ static OPEN = 1;
75
+ static CLOSING = 2;
76
+ static CLOSED = 3;
77
+
78
+ constructor(url, protocols) {
79
+ super();
80
+ this.url = url.href;
81
+ this.readyState = 0;
82
+ this.protocol = '';
83
+ this.extensions = '';
84
+ this.bufferedAmount = 0;
85
+ this.binaryType = 'blob';
86
+ this.onopen = this.onmessage = this.onclose = this.onerror = null;
87
+ const { port1, port2 } = new MessageChannel();
88
+ this.port = port1;
89
+ port1.onmessage = (e) => this.receive(e.data);
90
+ const list = protocols === undefined ? [] : [].concat(protocols);
91
+ top.postMessage({ type: 'ws', path: url.pathname + url.search, protocols: list }, location.origin, [port2]);
92
+ }
93
+
94
+ receive(m) {
95
+ if (m.open) {
96
+ this.readyState = 1;
97
+ this.protocol = m.protocol ?? '';
98
+ this.emit(new Event('open'));
99
+ } else if (m.message !== undefined) {
100
+ let data = m.message;
101
+ if (data instanceof ArrayBuffer && this.binaryType === 'blob') data = new Blob([data]);
102
+ this.emit(new MessageEvent('message', { data, origin: location.origin }));
103
+ } else if (m.close) {
104
+ if (this.readyState === 3) return;
105
+ const wasOpen = this.readyState === 1;
106
+ this.readyState = 3;
107
+ this.port.close();
108
+ if (!wasOpen || m.error) this.emit(new Event('error'));
109
+ this.emit(new CloseEvent('close', { code: m.code ?? 1006, reason: m.reason ?? '', wasClean: !m.error }));
110
+ }
111
+ }
112
+
113
+ send(data) {
114
+ if (this.readyState === 0) throw new DOMException('Still in CONNECTING state.', 'InvalidStateError');
115
+ if (this.readyState !== 1) return;
116
+ if (data instanceof Blob) {
117
+ data.arrayBuffer().then((b) => this.port.postMessage({ message: b }, [b]));
118
+ } else if (ArrayBuffer.isView(data)) {
119
+ this.port.postMessage({ message: data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) });
120
+ } else {
121
+ this.port.postMessage({ message: data });
122
+ }
123
+ }
124
+
125
+ close(code = 1000, reason = '') {
126
+ if (this.readyState >= 2) return;
127
+ this.readyState = 2;
128
+ this.port.postMessage({ close: true, code, reason });
129
+ }
130
+
131
+ emit(event) {
132
+ this.dispatchEvent(event);
133
+ this[`on${event.type}`]?.(event);
134
+ }
135
+ }
136
+
137
+ window.WebSocket = function WebSocket(url, protocols) {
138
+ const u = new URL(url, location.href);
139
+ if (u.host === location.host) return new VmSocket(u, protocols);
140
+ return protocols === undefined ? new Native(url) : new Native(url, protocols);
141
+ };
142
+ Object.assign(window.WebSocket, { CONNECTING: 0, OPEN: 1, CLOSING: 2, CLOSED: 3, prototype: Native.prototype });
143
+ })();
@@ -0,0 +1,17 @@
1
+ // A Worker that holds only a release (release.bin of beam_com_wasm), for a runtime
2
+ // Worker (worker.js with a service binding APP) to boot; and, if there is
3
+ // one, a snapshot of the booted VM (snapshot.bin).
4
+ import release from './release.bin';
5
+
6
+ async function snapshot() {
7
+ try { return (await import('./snapshot.bin')).default; } catch { return null; }
8
+ }
9
+
10
+ export default {
11
+ async fetch(request) {
12
+ const path = new URL(request.url).pathname;
13
+ const data = path === '/release.bin' ? release : path === '/snapshot.bin' ? await snapshot() : null;
14
+ if (!data) return new Response('not found', { status: 404 });
15
+ return new Response(data, { headers: { 'content-type': 'application/octet-stream' } });
16
+ },
17
+ };
@@ -0,0 +1 @@
1
+ {"version": "0.1.0-rc.1", "sha256": "910a07163cdfd74ab8534af7613104f3b84ea4ffda7b495b9be86d9359d8519d"}
@@ -0,0 +1,3 @@
1
+ // The identity of this runtime (beam_com_wasm:runtime_id/1): the SHA-256 of
2
+ // the output of "sha256sum app-com.js beam.mjs beam.wasm worker.js".
3
+ export default '8f7af69ef48fc70da056a5684afc43055a33cfd9efb3099d858b0f75b33e351b';
@@ -0,0 +1,30 @@
1
+ // A client proxy for incoming TCP on Workers: each connection to a local
2
+ // port becomes a WebSocket to /.tcp/PORT of the Worker (worker.js), which
3
+ // gives it to the listener of PORT in Erlang (gen_tcp:listen).
4
+ //
5
+ // node tcp-proxy.mjs LOCAL_PORT wss://app.example.com/.tcp/PORT
6
+ // ssh -p LOCAL_PORT localhost
7
+ import net from 'node:net';
8
+ import { WebSocket } from 'ws';
9
+
10
+ const [localPort, url] = process.argv.slice(2);
11
+ if (!localPort || !url) {
12
+ console.error('usage: node tcp-proxy.mjs LOCAL_PORT WS_URL');
13
+ process.exit(2);
14
+ }
15
+
16
+ net.createServer((sock) => {
17
+ sock.pause();
18
+ sock.setNoDelay(true);
19
+ const ws = new WebSocket(url);
20
+ ws.binaryType = 'nodebuffer';
21
+ ws.on('open', () => {
22
+ sock.on('data', (d) => ws.send(d, { binary: true }));
23
+ sock.resume();
24
+ });
25
+ ws.on('message', (d) => sock.write(d));
26
+ ws.on('close', () => sock.end());
27
+ ws.on('error', (e) => { console.error(`tcp-proxy: ${e.message}`); sock.destroy(); });
28
+ sock.on('close', () => ws.close());
29
+ sock.on('error', () => ws.close());
30
+ }).listen(Number(localPort), '127.0.0.1', () => console.error(`tcp-proxy: 127.0.0.1:${localPort} -> ${url}`));