@swmansion/popcorn 0.4.0-next.3 → 0.4.0-next.4

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
@@ -4,7 +4,7 @@ Run Elixir in a browser with OTP/BEAM compiled to WebAssembly.
4
4
  This prerelease replaces the AtomVM runtime used by Popcorn 0.3.x.
5
5
 
6
6
  Start with the
7
- [versioned Popcorn guide](https://popcorn.hexdocs.pm/0.4.0-next.3/introduction.html).
7
+ [versioned Popcorn guide](https://popcorn.hexdocs.pm/0.4.0-next.4/introduction.html).
8
8
  It includes a complete Mix and Vite tutorial.
9
9
 
10
10
  ## Install
@@ -13,12 +13,12 @@ It includes a complete Mix and Vite tutorial.
13
13
  npm install @swmansion/popcorn@next
14
14
  ```
15
15
 
16
- Add `{:popcorn, "0.4.0-next.3"}` to your Elixir application's dependencies.
16
+ Add `{:popcorn, "0.4.0-next.4"}` to your Elixir application's dependencies.
17
17
  Run `mix deps.get` before building the JavaScript application. The bundler
18
18
  plugins invoke Mix locally to compile and package application and
19
19
  standard-library code.
20
20
 
21
- Use the toolchain pinned in [mise.toml](https://github.com/software-mansion/popcorn/blob/v0.4.0-next.3/mise.toml).
21
+ Use the toolchain pinned in [mise.toml](https://github.com/software-mansion/popcorn/blob/v0.4.0-next.4/mise.toml).
22
22
  The packager checks host OTP compatibility against the selected runtime's manifest.
23
23
 
24
24
  ## Configure Vite
@@ -71,6 +71,15 @@ vm.deinit();
71
71
  `popcorn.genserver.call()` to call a supervised GenServer from JavaScript.
72
72
  Use `popcorn.onEvent()` to receive events from BEAM processes.
73
73
 
74
+ Each VM starts with 64 MiB of Wasm linear memory and can grow to 256 MiB on
75
+ phones/tablets or 2 GiB on desktop. Device detection uses the browser's user
76
+ agent and touch support. Configure the limits with
77
+ `Popcorn.init({ beam: { maxMemory: { mobile: "256M", desktop: "2G" } } })`.
78
+ Omit `maxMemory` to keep these defaults, or override either key separately.
79
+ Only `M` and `G` suffixes are supported; they mean MiB and GiB.
80
+ The limit must be a multiple of 64 KiB between 64 MiB and 2 GiB; JavaScript and
81
+ filesystem memory are additional.
82
+
74
83
  ## Serve in production
75
84
 
76
85
  Serve over HTTPS or localhost. Set these headers on the application and runtime responses:
@@ -85,4 +94,4 @@ Serve `.wasm` as `application/wasm`. Serve compressed `.tar.gz` files with `Cont
85
94
  The package also emits uncompressed tar files and Brotli variants. Brotli uses standard effort by default. Set `brotliEffort: "max"` for maximum compression in release builds.
86
95
 
87
96
  The JavaScript bridge currently requires a Content Security Policy that permits `unsafe-eval`.
88
- See the [versioned Elixir API](https://popcorn.hexdocs.pm/0.4.0-next.3/) for interoperability details.
97
+ See the [versioned Elixir API](https://popcorn.hexdocs.pm/0.4.0-next.4/) for interoperability details.
package/dist/events.d.ts CHANGED
@@ -3,7 +3,7 @@ import { type Mapper } from "./etf";
3
3
  import type { AnyValue, BeamBootOptions, BeamEvent, BeamSendPayload, BeamTarget } from "./types";
4
4
  type BootEvent = {
5
5
  type: "popcorn:boot";
6
- payload: Pick<BeamBootOptions, "emulatorArgs" | "extraArgs" | "env" | "ttySize" | "noshell"> & {
6
+ payload: Pick<BeamBootOptions, "emulatorArgs" | "extraArgs" | "env" | "maxMemoryBytes" | "ttySize" | "noshell"> & {
7
7
  otpAssetsRoot?: string;
8
8
  };
9
9
  };
package/dist/index.mjs CHANGED
@@ -547,6 +547,7 @@ class Popcorn {
547
547
  vmWorker;
548
548
  state = { status: "created" };
549
549
  opts;
550
+ maxMemoryBytes;
550
551
  ttySize;
551
552
  output;
552
553
  requestSeq = 0;
@@ -624,12 +625,13 @@ class Popcorn {
624
625
  check(isValidTtySize(ttySize));
625
626
  check(opts.beam?.otpAssetsRoot === undefined ||
626
627
  opts.beam.otpAssetsRoot.endsWith("/"), "otpAssetsRoot must end with a slash");
628
+ const { maxMemory, ...beam } = opts.beam ?? {};
629
+ this.maxMemoryBytes = resolveMaxMemory(maxMemory);
627
630
  this.opts = {
628
631
  ...opts,
629
632
  beam: {
630
- ...opts.beam,
631
- emulatorArgs: opts.beam?.emulatorArgs ??
632
- schedulers({ base: 1, dirtyCpu: 1, dirtyIo: 1 }),
633
+ ...beam,
634
+ emulatorArgs: beam.emulatorArgs ?? schedulers({ base: 1, dirtyCpu: 1, dirtyIo: 1 }),
633
635
  },
634
636
  };
635
637
  this.ttySize = { ...ttySize };
@@ -749,6 +751,7 @@ class Popcorn {
749
751
  type: "popcorn:boot",
750
752
  payload: {
751
753
  ...this.opts.beam,
754
+ maxMemoryBytes: this.maxMemoryBytes,
752
755
  ttySize: this.ttySize,
753
756
  noshell: this.opts.tty === undefined,
754
757
  },
@@ -1191,6 +1194,23 @@ class Popcorn {
1191
1194
  this.deinit(exitReason(payload));
1192
1195
  }
1193
1196
  }
1197
+ function resolveMaxMemory(limits) {
1198
+ const mobileLimit = parseMemorySize(limits?.mobile ?? "256M");
1199
+ const desktopLimit = parseMemorySize(limits?.desktop ?? "2G");
1200
+ const mobile = /Android|Mobile|iPad|iPhone|iPod/.test(navigator.userAgent);
1201
+ const ipad = /Macintosh/.test(navigator.userAgent) && navigator.maxTouchPoints > 1;
1202
+ return mobile || ipad ? mobileLimit : desktopLimit;
1203
+ }
1204
+ function parseMemorySize(size) {
1205
+ const match = /^(\d+(?:\.\d+)?)([MG])$/.exec(size);
1206
+ check(match !== null, 'maxMemory must use M or G, such as "256M" or "2G"');
1207
+ const bytes = Number(match[1]) * (match[2] === "M" ? 1024 ** 2 : 1024 ** 3);
1208
+ check(Number.isSafeInteger(bytes) &&
1209
+ bytes >= 64 * 1024 * 1024 &&
1210
+ bytes <= 2 * 1024 * 1024 * 1024 &&
1211
+ bytes % 65536 === 0, "maxMemory must be a multiple of 64 KiB between 64 MiB and 2 GiB");
1212
+ return bytes;
1213
+ }
1194
1214
  /**
1195
1215
  * Builds `beam.emulatorArgs` for scheduler counts.
1196
1216
  *
package/dist/popcorn.d.ts CHANGED
@@ -1,12 +1,22 @@
1
1
  import { type Result } from "./errors";
2
2
  import { type PopcornEvent } from "./events";
3
3
  import type { AnyValue, BeamBootOptions, OtpErrorPayload, Pid, TtySize } from "./types";
4
+ type MemoryLimits = {
5
+ mobile?: `${number}${"M" | "G"}`;
6
+ desktop?: `${number}${"M" | "G"}`;
7
+ };
4
8
  /** Output type for a terminal. */
5
9
  type TtyOutput = "text" | "bytes";
6
10
  type OutputChunk<Output extends TtyOutput> = Output extends "bytes" ? Uint8Array : string;
7
11
  /** Browser VM configuration. */
8
12
  export type PopcornOpts<Output extends TtyOutput = "text"> = {
9
13
  beam?: Pick<BeamBootOptions, "emulatorArgs" | "extraArgs" | "env"> & {
14
+ /**
15
+ * Maximum Wasm linear memory by platform. Omitted keys default to
16
+ * mobile: "256M", desktop: "2G". M and G mean MiB and GiB.
17
+ * Limits must be in 64 KiB increments between 64 MiB and 2 GiB.
18
+ */
19
+ maxMemory?: MemoryLimits;
10
20
  /**
11
21
  * Asset directory URL.
12
22
  *
@@ -116,6 +126,7 @@ export declare class Popcorn<Output extends TtyOutput = "text"> {
116
126
  private vmWorker;
117
127
  private state;
118
128
  private readonly opts;
129
+ private readonly maxMemoryBytes;
119
130
  private readonly ttySize;
120
131
  private output;
121
132
  private requestSeq;