beam.com 0.1.0-rc.5 → 0.1.0-rc.7

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/NOTICE CHANGED
@@ -33,7 +33,7 @@ Cosmopolitan Libc 4.0.2 (the C runtime of the APE file)
33
33
  shows them.
34
34
  https://github.com/jart/cosmopolitan
35
35
 
36
- OpenSSL 4.0.2 (libcrypto, for the crypto NIF)
36
+ OpenSSL 4.0.3 (libcrypto, for the crypto NIF)
37
37
  Copyright The OpenSSL Project Authors.
38
38
  Apache License 2.0 (LICENSE).
39
39
  https://github.com/openssl/openssl
@@ -76,9 +76,20 @@ of lazy_html in lib/beam_com/priv/nifs; only a program with lazy_html
76
76
  lexbor: Copyright 2018-2020 Alexander Borisov. Apache License 2.0
77
77
  (LICENSE). https://github.com/lexbor/lexbor
78
78
 
79
+ sha1collisiondetection (SHA-1 with collision detection, the library of
80
+ git; in the helper program git_sha1dc of the git client)
81
+ Copyright 2017 Marc Stevens and Dan Shumow.
82
+ MIT License (licenses/sha1collisiondetection.txt).
83
+ https://github.com/cr-marcstevens/sha1collisiondetection
84
+
85
+ The Public Suffix List (lib/beam_com/priv/public_suffix_list.dat, for
86
+ the warning of --allow-net=*.DOMAIN; only beam.com contains it)
87
+ Mozilla Public License 2.0 (licenses/mpl-2.0.txt).
88
+ https://publicsuffix.org
89
+
79
90
  The WebAssembly runtime (beam.wasm and beam.mjs in the zip, for
80
91
  --target wasm32)
81
- Erlang/OTP 29.1.1 and OpenSSL 4.0.2, as above, compiled with
92
+ Erlang/OTP 29.1.1 and OpenSSL 4.0.3, as above, compiled with
82
93
  Emscripten. The runtime contains the system libraries of Emscripten:
83
94
  - Emscripten: MIT License and University of Illinois/NCSA Open Source
84
95
  License (licenses/emscripten.txt). https://github.com/emscripten-core/emscripten
@@ -94,4 +105,25 @@ Custom builds (build.sh) can also contain:
94
105
  - picosat_elixir (EXTRA_NIFS): Apache License 2.0, with PicoSAT (MIT
95
106
  License, Copyright 2006-2015 Armin Biere, Johannes Kepler University;
96
107
  licenses/picosat.txt).
108
+ - vix (EXTRA_NIFS): MIT License, with libvips and glib (GNU LGPL 2.1 or
109
+ later), libheif (GNU LGPL 3.0), aom (BSD 2-Clause License, with the
110
+ Alliance for Open Media Patent License 1.0), Little CMS (MIT
111
+ License), cgif (MIT License), libimagequant (the BSD 2-Clause License
112
+ of its fork by sharp-libvips) and the other libraries of
113
+ scripts/libvips.sh, each with its own license (see their sources).
114
+ - rusty_csv (EXTRA_NIFS): MIT License. The Rust crates of its NIF
115
+ (rustler and others) have the MIT License or the Apache License 2.0;
116
+ see its Cargo.lock.
117
+ - ex_pdfium (EXTRA_NIFS): MIT License. Its NIF has PDFium (BSD 3-Clause
118
+ License, Copyright 2014 The PDFium Authors,
119
+ https://pdfium.googlesource.com/pdfium) and the third-party code of
120
+ PDFium: FreeType (the FreeType License: portions of this software are
121
+ copyright The FreeType Project, https://freetype.org, all rights
122
+ reserved), Abseil (Apache License 2.0), ICU (Unicode License v3),
123
+ zlib (zlib License), libjpeg-turbo (IJG License and BSD 3-Clause
124
+ License), OpenJPEG (BSD 2-Clause License), Little CMS (MIT License),
125
+ Anti-Grain Geometry 2.3, fast_float and BigInteger. Their license
126
+ texts are in licenses/pdfium of such a build. The Rust crates of the
127
+ NIF (pdfium-render, rustler and others) have the MIT License or the
128
+ Apache License 2.0; see the Cargo.lock of ex_pdfium.
97
129
  - More OTP applications (OTP_APPS): as Erlang/OTP above.
package/README.md CHANGED
@@ -74,12 +74,18 @@ On NetBSD and OpenBSD, start the file with the APE loader
74
74
 
75
75
  ### A custom build
76
76
 
77
- Do you need more OTP applications (for example `ssh` or `mnesia`), Hex and
77
+ Do you need more OTP applications (for example `mnesia` or `eldap`), Hex and
78
78
  rebar3 in the file, or a file without Elixir, SQLite or WebAssembly? Open
79
79
  an issue with the form
80
80
  [A custom build of beam.com](https://github.com/sntran/BEAM.com/issues/new?template=custom_build.yml).
81
81
  CI builds and tests the file with your choices, and puts it on the issue.
82
- You need no toolchain. See "A custom build" in
82
+ Without a GitHub account, use the form of
83
+ [mkfifo.com/beam.com](https://mkfifo.com/beam.com?form): it gives the
84
+ file in the browser, or sends its link to your email address. A script
85
+ gets the same file with one request, as
86
+ `curl -fo beam.com 'https://mkfifo.com/beam.com?apps=mnesia&nifs+=vix'`,
87
+ and `curl -fo beam.com https://mkfifo.com/beam.com` gives the default
88
+ build. You need no toolchain. See "A custom build" and "A build URL" in
83
89
  [`docs/BUILDING.md`](docs/BUILDING.md).
84
90
 
85
91
  ## Quick start
@@ -219,7 +225,7 @@ jobs:
219
225
 
220
226
  Then turn on Pages one time: Settings, Pages, Source "GitHub Actions". The
221
227
  `GITHUB_TOKEN` cannot do this step. Until the release `v0.1.0`, use the
222
- release candidate `@v0.1.0-rc.5` in place of `@v0.1.0`. With `@main`, the
228
+ release candidate `@v0.1.0-rc.7` in place of `@v0.1.0`. With `@main`, the
223
229
  workflow uses the `edge` build of `beam.com`.
224
230
 
225
231
  The workflow ([`pages-app.yml`](.github/workflows/pages-app.yml)) builds
@@ -240,9 +246,9 @@ run shows a warning. A pin to a commit SHA uses the release of that
240
246
  commit, and stops with an error when the commit has no release tag.
241
247
  Before the build, the workflow checks the SHA-256 of `beam.com` and its
242
248
  provenance (`gh attestation verify`). The check stops the build when
243
- `build.yml` of BEAM.com did not make the file, or when the check cannot
244
- reach GitHub or Sigstore. It needs no other
245
- permission.
249
+ `build.yml` of BEAM.com did not make the file from the commit of the
250
+ release tag (or from `main`, for `edge`), or when the check cannot reach
251
+ GitHub or Sigstore. It needs no other permission.
246
252
 
247
253
  The app needs an HTTP listener on `PORT` (4000 by default), and only the
248
254
  NIFs of the WebAssembly runtime. The limits:
@@ -286,7 +292,7 @@ Each GitHub release from 0.1.0-rc.2 also holds the package, the same
286
292
  file as on npm. npm installs it with no registry:
287
293
 
288
294
  ```sh
289
- npm install https://github.com/sntran/BEAM.com/releases/download/v0.1.0-rc.5/beam.com-0.1.0-rc.5.tgz
295
+ npm install https://github.com/sntran/BEAM.com/releases/download/v0.1.0-rc.7/beam.com-0.1.0-rc.7.tgz
290
296
  ```
291
297
 
292
298
  - `BEAM_COM`: the path of a `beam.com` to use. Then nothing is
@@ -346,7 +352,8 @@ the Erlang shell in your browser is at
346
352
  | File | What |
347
353
  |---|---|
348
354
  | [`docs/PROGRAMS.md`](docs/PROGRAMS.md) | Run and build programs: the inputs, Hex packages, command line programs, native files (`--target`), distributed Erlang, a release in the zip, debugging. |
349
- | [`docs/ELIXIR.md`](docs/ELIXIR.md) | Elixir programs, the tools (`mix`, `iex`, `elixir`), Phoenix from source, the file watcher. |
355
+ | [`docs/ELIXIR.md`](docs/ELIXIR.md) | Elixir programs, the tools (`mix`, `iex`, `elixir`), Phoenix from source, Phoenix in one file, the file watcher. |
356
+ | [`docs/DEPLOY.md`](docs/DEPLOY.md) | Deploy a Phoenix app to a Linux server: a build on the server or in CI, systemd, migrations, new code with no restart, and what needs a restart. |
350
357
  | [`docs/WORKERS.md`](docs/WORKERS.md) | Cloudflare Workers, Deno Deploy and web pages: `app.com` with the npm package, Durable Objects, Ecto SQLite, HTTP through `fetch()`, snapshots, tenants, static sites (`--page`), and limits. |
351
358
  | [`docs/NOTEBOOKS.md`](docs/NOTEBOOKS.md) | The documentation as notebooks that run in your browser: a tour of beam.com, the WebAssembly VM, the anatomy of the file, WebAssembly programs, hosts and storage, networking, and building programs. |
352
359
  | [`docs/LIBRARIES.md`](docs/LIBRARIES.md) | Crypto and TLS, SQLite, and WebAssembly in Erlang code. |
@@ -378,7 +385,7 @@ the Erlang shell in your browser is at
378
385
  |---|---|
379
386
  | Linux x86_64 and aarch64, WSL2 | ✅ |
380
387
  | macOS arm64 and x86_64 | ✅ |
381
- | Windows x86_64 | ✅ (no port programs) |
388
+ | Windows x86_64 | ✅ |
382
389
  | FreeBSD, NetBSD, OpenBSD 7.3 | ✅ |
383
390
  | OpenBSD 7.4 and later | ❌ (not supported by Cosmopolitan) |
384
391
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beam.com",
3
- "version": "0.1.0-rc.5",
3
+ "version": "0.1.0-rc.7",
4
4
  "description": "Erlang/OTP and Elixir in one executable file: npx beam.com, and the WebAssembly runtime for a native app.com",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -31,6 +31,12 @@
31
31
  // the host serves before the VM (worker.js). They are not in files,
32
32
  // except cache_manifest.json, which Phoenix reads at its start.
33
33
  //
34
+ // appStatics(read, size, { runtime }) gives only that Map, with no read and
35
+ // no check of the other files: the front of a Worker (cloudflare/index.js)
36
+ // serves the static files with no VM. The check of the CRC-32 of each file
37
+ // of the release cost 80 to 180 ms of CPU time for a release of 16 MB, and
38
+ // the front pays it in each new isolate.
39
+ //
34
40
  // The zip of beam.com stores each .beam file as a gzip file (the loader of
35
41
  // ERTS reads it so, beam_com_zip), so it is a view too. A deflated .beam
36
42
  // entry (an older app.com) becomes a gzip file with the same data, with
@@ -139,32 +145,13 @@ export async function appRelease(read, size, options = {}) {
139
145
  // default uses DecompressionStream, which the global scope of a Worker
140
146
  // cannot use (cloudflare/release.js gives inflateRawSync of node:zlib).
141
147
  export async function appFiles(read, size, { runtime, statics: split = true, inflate: unzip = inflate } = {}) {
142
- const { entries, cdAt } = await zipEntries(read, size);
143
- const want = entries.filter((e) => inRelease(e.name) && !e.name.endsWith('/'));
144
- const json = want.find((e) => e.name === `${EDGE}.release.json`);
145
- if (!json) fail('no edge part (.wasm/.release.json): build it with a beam.com that has the WebAssembly runtime, and without --no-edge');
146
- // One read: the entries of the release, up to the central directory.
147
- const lo = Math.min(...want.map((e) => e.offset));
148
- const span = await read(lo, cdAt - lo);
149
- if (span.length !== cdAt - lo) fail('a short read of the release');
150
- const meta = await entryData(span, lo, json, unzip);
151
- const release = JSON.parse(text.decode(meta));
152
- if (runtime && release.runtime !== runtime) {
153
- fail(`it was built for the runtime ${String(release.runtime).slice(0, 12)}, and this runtime is ${runtime.slice(0, 12)}: `
154
- + 'use the runtime of the beam.com that built it');
155
- }
156
- // The static files of a Phoenix app (PHX_SERVER in the environment of
157
- // the release): lib/NAME-VSN/priv/static/, for the application with the
158
- // name of the release. Plug.Static serves them at the root of the site.
159
- const app = split && release.env?.PHX_SERVER === 'true' && typeof release.name === 'string'
160
- && `lib/${release.name}-`;
161
- const prefix = app && want.map((e) => /^lib\/[^/]+\/priv\/static\//.exec(e.name)?.[0])
162
- .find((p) => p?.startsWith(app) && /^[0-9]/.test(p.slice(app.length)));
148
+ const { want, json, lo, span, meta, release } = await releaseHead(read, size, runtime, unzip);
149
+ const prefix = split && staticPrefix(release, want);
163
150
  const statics = new Map();
164
151
  const files = new Map();
165
152
  for (const e of want) {
166
153
  if (e.name.startsWith(EDGE)) continue;
167
- if (prefix && e.name.startsWith(prefix) && e.name !== `${prefix}cache_manifest.json`) {
154
+ if (prefix && isStatic(e, prefix)) {
168
155
  statics.set(e.name.slice(prefix.length - 1), staticEntry(span, lo, e));
169
156
  continue;
170
157
  }
@@ -179,6 +166,46 @@ export async function appFiles(read, size, { runtime, statics: split = true, inf
179
166
  };
180
167
  }
181
168
 
169
+ export async function appStatics(read, size, { runtime, inflate: unzip = inflate } = {}) {
170
+ const { want, lo, span, release } = await releaseHead(read, size, runtime, unzip);
171
+ const prefix = staticPrefix(release, want);
172
+ const statics = new Map();
173
+ if (prefix) for (const e of want) if (isStatic(e, prefix)) statics.set(e.name.slice(prefix.length - 1), staticEntry(span, lo, e));
174
+ return statics;
175
+ }
176
+
177
+ // The entries of the release, the span of their bytes (one read, up to the
178
+ // central directory), and .release.json, which must be of the runtime.
179
+ async function releaseHead(read, size, runtime, unzip) {
180
+ const { entries, cdAt } = await zipEntries(read, size);
181
+ const want = entries.filter((e) => inRelease(e.name) && !e.name.endsWith('/'));
182
+ const json = want.find((e) => e.name === `${EDGE}.release.json`);
183
+ if (!json) fail('no edge part (.wasm/.release.json): build it with a beam.com that has the WebAssembly runtime, and without --no-edge');
184
+ const lo = Math.min(...want.map((e) => e.offset));
185
+ const span = await read(lo, cdAt - lo);
186
+ if (span.length !== cdAt - lo) fail('a short read of the release');
187
+ const meta = await entryData(span, lo, json, unzip);
188
+ const release = JSON.parse(text.decode(meta));
189
+ if (runtime && release.runtime !== runtime) {
190
+ fail(`it was built for the runtime ${String(release.runtime).slice(0, 12)}, and this runtime is ${runtime.slice(0, 12)}: `
191
+ + 'use the runtime of the beam.com that built it');
192
+ }
193
+ return { want, json, lo, span, meta, release };
194
+ }
195
+
196
+ // The static files of a Phoenix app (PHX_SERVER in the environment of the
197
+ // release): lib/NAME-VSN/priv/static/, for the application with the name
198
+ // of the release, or null. Plug.Static serves them at the root of the site.
199
+ function staticPrefix(release, want) {
200
+ const app = release.env?.PHX_SERVER === 'true' && typeof release.name === 'string' && `lib/${release.name}-`;
201
+ return (app && want.map((e) => /^lib\/[^/]+\/priv\/static\//.exec(e.name)?.[0])
202
+ .find((p) => p?.startsWith(app) && /^[0-9]/.test(p.slice(app.length)))) || null;
203
+ }
204
+
205
+ // A static file of the prefix. cache_manifest.json stays in the release:
206
+ // Phoenix reads it at its start.
207
+ const isStatic = (e, prefix) => e.name.startsWith(prefix) && e.name !== `${prefix}cache_manifest.json`;
208
+
182
209
  // A static file: its bytes in the zip (stored or deflated), not checked
183
210
  // here (the host checks the CRC-32 when it inflates them).
184
211
  function staticEntry(span, base, e) {