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

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/README.md CHANGED
@@ -74,7 +74,7 @@ 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).
@@ -219,7 +219,7 @@ jobs:
219
219
 
220
220
  Then turn on Pages one time: Settings, Pages, Source "GitHub Actions". The
221
221
  `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
222
+ release candidate `@v0.1.0-rc.6` in place of `@v0.1.0`. With `@main`, the
223
223
  workflow uses the `edge` build of `beam.com`.
224
224
 
225
225
  The workflow ([`pages-app.yml`](.github/workflows/pages-app.yml)) builds
@@ -240,9 +240,9 @@ run shows a warning. A pin to a commit SHA uses the release of that
240
240
  commit, and stops with an error when the commit has no release tag.
241
241
  Before the build, the workflow checks the SHA-256 of `beam.com` and its
242
242
  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.
243
+ `build.yml` of BEAM.com did not make the file from the commit of the
244
+ release tag (or from `main`, for `edge`), or when the check cannot reach
245
+ GitHub or Sigstore. It needs no other permission.
246
246
 
247
247
  The app needs an HTTP listener on `PORT` (4000 by default), and only the
248
248
  NIFs of the WebAssembly runtime. The limits:
@@ -286,7 +286,7 @@ Each GitHub release from 0.1.0-rc.2 also holds the package, the same
286
286
  file as on npm. npm installs it with no registry:
287
287
 
288
288
  ```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
289
+ npm install https://github.com/sntran/BEAM.com/releases/download/v0.1.0-rc.6/beam.com-0.1.0-rc.6.tgz
290
290
  ```
291
291
 
292
292
  - `BEAM_COM`: the path of a `beam.com` to use. Then nothing is
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.6",
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) {