@swmansion/popcorn 0.3.2 → 0.4.0-next.0
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/LICENSE +1 -1
- package/NOTICE +12 -0
- package/README.md +78 -2
- package/dist/beam.d.ts +10 -0
- package/dist/errors.d.ts +96 -39
- package/dist/etf.d.ts +38 -0
- package/dist/events.d.ts +82 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.mjs +1287 -2
- package/dist/plugins/beam_tools/lib/popcorn/beam_tools/beam_patcher.ex +184 -0
- package/dist/plugins/beam_tools/lib/popcorn/beam_tools/cli.ex +74 -0
- package/dist/plugins/beam_tools/lib/popcorn/beam_tools/packager.ex +540 -0
- package/dist/plugins/beam_tools/mix.exs +16 -0
- package/dist/plugins/beam_tools/patches/kernel/prim_tty.erl +13 -0
- package/dist/plugins/beam_tools/patches/stdlib/beam_lib.erl +27 -0
- package/dist/plugins/esbuild.d.ts +10 -2
- package/dist/plugins/esbuild.mjs +39 -34
- package/dist/plugins/rollup.d.ts +10 -2
- package/dist/plugins/rollup.mjs +46 -25
- package/dist/plugins/shared.d.ts +54 -4
- package/dist/plugins/shared.mjs +207 -0
- package/dist/plugins/vite.d.ts +17 -2
- package/dist/plugins/vite.mjs +201 -75
- package/dist/popcorn.d.ts +237 -110
- package/dist/runtimes/core/beam.emu.mjs +141 -0
- package/dist/runtimes/core/beam.mjs +141 -0
- package/dist/runtimes/core/beam.wasm +0 -0
- package/dist/runtimes/core/manifest.json +1 -0
- package/dist/runtimes/crypto/beam.emu.mjs +520 -0
- package/dist/runtimes/crypto/beam.mjs +520 -0
- package/dist/runtimes/crypto/beam.wasm +0 -0
- package/dist/runtimes/crypto/manifest.json +1 -0
- package/dist/tar.d.ts +4 -0
- package/dist/types.d.ts +108 -63
- package/dist/utils.d.ts +7 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.mjs +765 -0
- package/package.json +20 -28
- package/dist/AtomVM.mjs +0 -7992
- package/dist/AtomVM.wasm +0 -0
- package/dist/bridge.d.ts +0 -22
- package/dist/bridge.mjs +0 -66
- package/dist/errors.mjs +0 -55
- package/dist/iframe.d.ts +0 -1
- package/dist/iframe.mjs +0 -215
- package/dist/popcorn.mjs +0 -381
- package/dist/types.mjs +0 -25
package/LICENSE
CHANGED
|
@@ -198,4 +198,4 @@
|
|
|
198
198
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
199
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
200
|
See the License for the specific language governing permissions and
|
|
201
|
-
limitations under the License.
|
|
201
|
+
limitations under the License.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Popcorn includes Erlang/OTP compiled to WebAssembly with Emscripten.
|
|
2
|
+
|
|
3
|
+
Erlang/OTP
|
|
4
|
+
Copyright Ericsson AB and other contributors.
|
|
5
|
+
Licensed under the Apache License, Version 2.0.
|
|
6
|
+
Source: https://github.com/erlang/otp
|
|
7
|
+
Popcorn's runtime includes modifications for WebAssembly and JavaScript interoperability.
|
|
8
|
+
|
|
9
|
+
Emscripten
|
|
10
|
+
Copyright the Emscripten authors.
|
|
11
|
+
Available under the MIT license and the University of Illinois/NCSA Open Source License.
|
|
12
|
+
Source: https://github.com/emscripten-core/emscripten
|
package/README.md
CHANGED
|
@@ -1,3 +1,79 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Popcorn OTP
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run Elixir in a browser with OTP/BEAM compiled to WebAssembly.
|
|
4
|
+
This prerelease replaces the AtomVM runtime used by Popcorn 0.3.x.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm install @swmansion/popcorn@next
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Add `{:popcorn, "0.4.0-next.0"}` to your Elixir application's dependencies.
|
|
13
|
+
Run `mix deps.get` and `mix compile` before building the JavaScript application.
|
|
14
|
+
The bundler plugins invoke Mix locally to package application and standard-library code.
|
|
15
|
+
|
|
16
|
+
Use the toolchain pinned in [popcorn/mise.toml](https://github.com/software-mansion/popcorn/blob/v0.4.0-next.0/popcorn/mise.toml).
|
|
17
|
+
The packager checks host OTP compatibility against the selected runtime's manifest.
|
|
18
|
+
|
|
19
|
+
## Configure Vite
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { defineConfig } from "vite";
|
|
23
|
+
import { popcorn } from "@swmansion/popcorn/vite";
|
|
24
|
+
|
|
25
|
+
export default defineConfig({
|
|
26
|
+
plugins: [
|
|
27
|
+
popcorn({
|
|
28
|
+
rootDir: "../",
|
|
29
|
+
app: "my_app",
|
|
30
|
+
}),
|
|
31
|
+
],
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Set `rootDir` to the compiled Mix project's directory and `app` to its OTP application name.
|
|
36
|
+
Use `app: null` to package the base runtime without starting an application.
|
|
37
|
+
|
|
38
|
+
The npm package contains two variants:
|
|
39
|
+
|
|
40
|
+
- `core`: without native crypto support.
|
|
41
|
+
- `crypto`: includes native crypto and ASN.1 support for applications that depend on crypto, public_key, or ssl.
|
|
42
|
+
|
|
43
|
+
The plugin emits only the selected variant. The browser does not download both.
|
|
44
|
+
Both variants share one JavaScript API and one Hex package.
|
|
45
|
+
The plugin selects `crypto` when the application's dependencies or `extraApps` require it; otherwise it selects `core`.
|
|
46
|
+
Set `runtimeVariant: "core"` or `runtimeVariant: "crypto"` to override this choice.
|
|
47
|
+
An explicit `"core"` selection produces a build error if the application requires crypto.
|
|
48
|
+
|
|
49
|
+
Rollup and esbuild plugins accept the same options through `@swmansion/popcorn/rollup` and `@swmansion/popcorn/esbuild`.
|
|
50
|
+
Use ESM output with those bundlers.
|
|
51
|
+
|
|
52
|
+
## Start the runtime
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { Popcorn } from "@swmansion/popcorn";
|
|
56
|
+
|
|
57
|
+
const result = await Popcorn.init({});
|
|
58
|
+
if (!result.ok) throw result.error;
|
|
59
|
+
|
|
60
|
+
const vm = result.data;
|
|
61
|
+
// Stop the runtime when the application no longer needs it.
|
|
62
|
+
vm.deinit();
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Serve in production
|
|
66
|
+
|
|
67
|
+
Serve over HTTPS or localhost. Set these headers on the application and runtime responses:
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
Cross-Origin-Opener-Policy: same-origin
|
|
71
|
+
Cross-Origin-Embedder-Policy: require-corp
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Vite sets these headers for development and preview. Configure your production server separately.
|
|
75
|
+
Serve `.wasm` as `application/wasm`. Serve compressed `.tar.gz` files with `Content-Encoding: gzip` for `.tar` requests.
|
|
76
|
+
The package also emits uncompressed tar files. Brotli variants require `brotli: true` and the corresponding server configuration.
|
|
77
|
+
|
|
78
|
+
The JavaScript bridge currently requires a Content Security Policy that permits `unsafe-eval`.
|
|
79
|
+
See the [versioned Elixir API](https://popcorn.hexdocs.pm/0.4.0-next.0/) for interoperability details.
|
package/dist/beam.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Result } from "./errors";
|
|
2
|
+
import type { BeamBootOptions, BeamSendPayload } from "./types";
|
|
3
|
+
export type Beam = {
|
|
4
|
+
boot: Promise<Result<null>>;
|
|
5
|
+
vmReady: Promise<void>;
|
|
6
|
+
send: (message: BeamSendPayload) => Result<null>;
|
|
7
|
+
writeStdin: (chunk: Uint8Array) => void;
|
|
8
|
+
resizeTty: (columns: number, rows: number) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function start(options: BeamBootOptions): Beam;
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,45 +1,102 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/** Error codes for internal errors that indicate bugs or misuse */
|
|
9
|
-
export type PopcornInternalErrorCode = "assert" | "private_constructor" | "bad_call" | "no_acked_call" | "bad_ack" | "already_mounted" | "unmounted" | "bad_target" | "bad_status" | "app_ready_timeout" | "bundle_not_found";
|
|
10
|
-
/** Non-recoverable error indicating a bug or library misuse (always thrown) */
|
|
11
|
-
export declare class PopcornInternalError extends Error {
|
|
12
|
-
readonly code: PopcornInternalErrorCode;
|
|
13
|
-
constructor(code: PopcornInternalErrorCode, message?: string);
|
|
14
|
-
}
|
|
15
|
-
/** Internal errors - indicate bugs, protocol violations, or library misuse */
|
|
16
|
-
type ErrorData = {
|
|
17
|
-
t: "assert";
|
|
18
|
-
} | {
|
|
19
|
-
t: "private_constructor";
|
|
20
|
-
} | {
|
|
21
|
-
t: "bad_call";
|
|
22
|
-
} | {
|
|
23
|
-
t: "no_acked_call";
|
|
24
|
-
} | {
|
|
25
|
-
t: "bad_ack";
|
|
1
|
+
/**
|
|
2
|
+
* An operation result. Check `ok` before accessing `data` or `error`.
|
|
3
|
+
* Expected failures use the error branch. Invalid usage can still throw.
|
|
4
|
+
*/
|
|
5
|
+
export type Result<T, E extends Tag = Tag> = {
|
|
6
|
+
ok: true;
|
|
7
|
+
data: T;
|
|
26
8
|
} | {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
9
|
+
ok: false;
|
|
10
|
+
error: PopcornError<E>;
|
|
11
|
+
};
|
|
12
|
+
type Tag = keyof PopcornErrors;
|
|
13
|
+
/** Error tags and their structured details. Match tags instead of message text. */
|
|
14
|
+
export type PopcornErrors = {
|
|
15
|
+
"timeout:init": {
|
|
16
|
+
timeoutMs: number;
|
|
17
|
+
};
|
|
18
|
+
"timeout:send": {
|
|
19
|
+
timeoutMs: number;
|
|
20
|
+
};
|
|
21
|
+
"timeout:call": {
|
|
22
|
+
timeoutMs: number;
|
|
23
|
+
};
|
|
24
|
+
"worker:load": {
|
|
25
|
+
message: string;
|
|
26
|
+
};
|
|
27
|
+
"vm:exited": VmExitedData;
|
|
28
|
+
"bridge:not-started": EmptyData;
|
|
29
|
+
"bridge:invalid-target": EmptyData;
|
|
30
|
+
"bridge:unserializable": UnserializableData;
|
|
31
|
+
"bridge:listener-not-found": {
|
|
32
|
+
targetName: string;
|
|
33
|
+
};
|
|
34
|
+
"genserver:noproc": {
|
|
35
|
+
target: string;
|
|
36
|
+
};
|
|
37
|
+
"genserver:exit": {
|
|
38
|
+
reason: string;
|
|
39
|
+
};
|
|
40
|
+
"genserver:unserializable": EmptyData;
|
|
41
|
+
"stdio:overflow": {
|
|
42
|
+
capacityBytes: number;
|
|
43
|
+
attemptedBytes: number;
|
|
44
|
+
};
|
|
45
|
+
"beam:missing-boot-script": {
|
|
46
|
+
url: string;
|
|
47
|
+
};
|
|
48
|
+
"beam:missing-manifest": {
|
|
49
|
+
url: string;
|
|
50
|
+
};
|
|
51
|
+
"beam:missing-tarball": {
|
|
52
|
+
name: string;
|
|
53
|
+
all: string[];
|
|
54
|
+
};
|
|
55
|
+
"internal:check": {
|
|
56
|
+
detail?: string;
|
|
57
|
+
};
|
|
58
|
+
"internal:unreachable": EmptyData;
|
|
59
|
+
"runtime:eval-unavailable": EmptyData;
|
|
60
|
+
};
|
|
61
|
+
/** Error tag and details without the Error instance or stack. */
|
|
62
|
+
export type SerializedError<T extends Tag = Tag> = {
|
|
63
|
+
[K in T]: {
|
|
64
|
+
t: K;
|
|
65
|
+
data: PopcornErrors[K];
|
|
66
|
+
};
|
|
67
|
+
}[T];
|
|
68
|
+
type EmptyData = Record<never, never>;
|
|
69
|
+
export type UnserializableReason = "cyclic-object" | "non-plain-object" | "lossy-int" | "non-finite-float" | "unsupported";
|
|
70
|
+
type UnserializableData = {
|
|
71
|
+
data: unknown;
|
|
72
|
+
part: unknown;
|
|
73
|
+
reason: UnserializableReason;
|
|
74
|
+
};
|
|
75
|
+
type VmExitedData = {
|
|
76
|
+
reason: "deinit";
|
|
32
77
|
} | {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
expectedStatus: string;
|
|
78
|
+
reason: "abort";
|
|
79
|
+
data: string;
|
|
36
80
|
} | {
|
|
37
|
-
|
|
81
|
+
reason: "error";
|
|
82
|
+
data: string;
|
|
38
83
|
} | {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
fallback: string;
|
|
84
|
+
reason: "exit";
|
|
85
|
+
data: number;
|
|
42
86
|
};
|
|
43
|
-
export declare function
|
|
44
|
-
export declare function
|
|
87
|
+
export declare function err<T extends Tag>(t: T, data: PopcornErrors[T]): PopcornError<T>;
|
|
88
|
+
export declare function isErr<T extends Tag = Tag>(error: unknown, t?: T): error is PopcornError<T>;
|
|
89
|
+
/** @hidden */
|
|
90
|
+
export declare class PopcornError<T extends Tag = Tag> extends Error {
|
|
91
|
+
readonly cause: SerializedError<T>;
|
|
92
|
+
private readonly serialized;
|
|
93
|
+
constructor(cause: SerializedError<T>);
|
|
94
|
+
get t(): T;
|
|
95
|
+
get data(): PopcornErrors[T];
|
|
96
|
+
/** Returns the tag and a shallow copy of its details. */
|
|
97
|
+
serialize(): SerializedError<T>;
|
|
98
|
+
/** Restores a serialized error. Throws if validation fails. */
|
|
99
|
+
static deserialize(value: unknown): PopcornError;
|
|
100
|
+
}
|
|
101
|
+
export declare function isUnserializableReason(value: unknown): value is UnserializableReason;
|
|
45
102
|
export {};
|
package/dist/etf.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type Result } from "./errors";
|
|
2
|
+
declare class AtomTerm {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
constructor(name: string);
|
|
5
|
+
}
|
|
6
|
+
declare class TupleTerm {
|
|
7
|
+
readonly entries: unknown[];
|
|
8
|
+
constructor(entries: unknown[]);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Creates a BEAM atom value.
|
|
12
|
+
*
|
|
13
|
+
* The atom must already exist in the receiving VM. Plain strings encode as binaries.
|
|
14
|
+
*/
|
|
15
|
+
export declare function atom(name: string): AtomTerm;
|
|
16
|
+
/** Alias for {@link atom}. */
|
|
17
|
+
export declare const a: typeof atom;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a BEAM tuple.
|
|
20
|
+
*
|
|
21
|
+
* Plain arrays encode as lists.
|
|
22
|
+
*/
|
|
23
|
+
export declare function tuple(first: unknown, second: unknown, ...rest: unknown[]): TupleTerm;
|
|
24
|
+
/** Alias for {@link tuple}. */
|
|
25
|
+
export declare const t: typeof tuple;
|
|
26
|
+
/** Pre-encoded ETF sub-term bytes (no version prefix), spliced verbatim. */
|
|
27
|
+
export declare class RawTerm {
|
|
28
|
+
readonly bytes: Uint8Array;
|
|
29
|
+
constructor(bytes: Uint8Array);
|
|
30
|
+
/**
|
|
31
|
+
* Wraps a full external term (`term_to_binary` output) as a spliceable
|
|
32
|
+
* sub-term by dropping its leading version byte.
|
|
33
|
+
*/
|
|
34
|
+
static fromExternal(external: Uint8Array): RawTerm;
|
|
35
|
+
}
|
|
36
|
+
export type Mapper = (value: object) => object;
|
|
37
|
+
export declare function encode(data: unknown, mapper?: Mapper): Result<Uint8Array<ArrayBuffer>, "bridge:unserializable">;
|
|
38
|
+
export {};
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type Result, type SerializedError } from "./errors";
|
|
2
|
+
import { type Mapper } from "./etf";
|
|
3
|
+
import type { AnyValue, BeamBootOptions, BeamEvent, BeamSendPayload, BeamTarget } from "./types";
|
|
4
|
+
type BootEvent = {
|
|
5
|
+
type: "popcorn:boot";
|
|
6
|
+
payload: Pick<BeamBootOptions, "emulatorArgs" | "extraArgs" | "env" | "ttySize"> & {
|
|
7
|
+
otpAssetsRoot?: string;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
type StdinEvent = {
|
|
11
|
+
type: "popcorn:stdin";
|
|
12
|
+
payload: {
|
|
13
|
+
chunk: Uint8Array;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
type TtyResizeEvent = {
|
|
17
|
+
type: "popcorn:tty-resize";
|
|
18
|
+
payload: {
|
|
19
|
+
columns: number;
|
|
20
|
+
rows: number;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
type SendEvent = {
|
|
24
|
+
type: "popcorn:send";
|
|
25
|
+
payload: SendRequestPayload;
|
|
26
|
+
};
|
|
27
|
+
export type RunJsReplyPayload = {
|
|
28
|
+
message: BeamSendPayload;
|
|
29
|
+
};
|
|
30
|
+
type RunJsReplyEvent = {
|
|
31
|
+
type: "popcorn:run-js-reply";
|
|
32
|
+
payload: RunJsReplyPayload;
|
|
33
|
+
};
|
|
34
|
+
export type SendRequestPayload = {
|
|
35
|
+
id: string;
|
|
36
|
+
message: BeamSendPayload;
|
|
37
|
+
};
|
|
38
|
+
export type SerializedSendResult = {
|
|
39
|
+
ok: true;
|
|
40
|
+
data: null;
|
|
41
|
+
} | {
|
|
42
|
+
ok: false;
|
|
43
|
+
error: SerializedError;
|
|
44
|
+
};
|
|
45
|
+
export type SendCompletionPayload = {
|
|
46
|
+
id: string;
|
|
47
|
+
result: SerializedSendResult;
|
|
48
|
+
};
|
|
49
|
+
type SendEndEvent = {
|
|
50
|
+
type: "popcorn:send-end";
|
|
51
|
+
payload: SendCompletionPayload;
|
|
52
|
+
};
|
|
53
|
+
type BootEndEvent = {
|
|
54
|
+
type: "popcorn:boot-vm-ready";
|
|
55
|
+
payload: {};
|
|
56
|
+
} | {
|
|
57
|
+
type: "popcorn:boot-end";
|
|
58
|
+
payload: {};
|
|
59
|
+
} | {
|
|
60
|
+
type: "popcorn:boot-fail";
|
|
61
|
+
payload: SerializedError;
|
|
62
|
+
};
|
|
63
|
+
export type MainToVmEvent = BootEvent | SendEvent | RunJsReplyEvent | StdinEvent | TtyResizeEvent;
|
|
64
|
+
/**
|
|
65
|
+
* A decoded BEAM message payload.
|
|
66
|
+
*
|
|
67
|
+
* Includes restored PID handles and tracked JavaScript values.
|
|
68
|
+
*/
|
|
69
|
+
export type PopcornEvent = AnyValue;
|
|
70
|
+
type RuntimeEvent = BeamEvent | SendEndEvent;
|
|
71
|
+
export type VmToMainEvent = RuntimeEvent | BootEndEvent;
|
|
72
|
+
export declare function readMainEvent(value: unknown): MainToVmEvent;
|
|
73
|
+
export declare function readWorkerEvent(value: unknown): VmToMainEvent;
|
|
74
|
+
export declare function serializeSendPayload(target: BeamTarget, payload: AnyValue, mapper?: Mapper): Result<BeamSendPayload, "bridge:unserializable">;
|
|
75
|
+
export declare function deserializeBridgeMessage(text: string): Extract<BeamEvent, {
|
|
76
|
+
type: "otp:message" | "otp:error" | "otp:run_js";
|
|
77
|
+
}> | null;
|
|
78
|
+
/** Usable only from main context. */
|
|
79
|
+
export declare function toVm(worker: Worker, event: MainToVmEvent, transfer?: Transferable[]): void;
|
|
80
|
+
/** Usable only from webworkers. */
|
|
81
|
+
export declare function toMain(event: VmToMainEvent): void;
|
|
82
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export
|
|
3
|
-
export
|
|
1
|
+
export { PopcornError } from "./errors";
|
|
2
|
+
export { a, atom, t, tuple } from "./etf";
|
|
3
|
+
export { Popcorn, schedulers } from "./popcorn";
|
|
4
|
+
export type { PopcornOpts, SchedulerOptions, GenServer } from "./popcorn";
|
|
5
|
+
export type { PopcornEvent } from "./events";
|
|
6
|
+
export type { AnyValue, Pid, OtpErrorPayload, TtySize } from "./types";
|
|
7
|
+
export type { PopcornErrors, Result, SerializedError } from "./errors";
|