@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/dist/popcorn.mjs
DELETED
|
@@ -1,381 +0,0 @@
|
|
|
1
|
-
import { IframeBridge } from './bridge.mjs';
|
|
2
|
-
import { HEARTBEAT_TIMEOUT_MS, INIT_VM_TIMEOUT_MS, MESSAGES, EVENT_NAMES, MAX_RELOAD_N, CALL_TIMEOUT_MS } from './types.mjs';
|
|
3
|
-
import { throwError, buildError, PopcornError } from './errors.mjs';
|
|
4
|
-
export { PopcornInternalError } from './errors.mjs';
|
|
5
|
-
|
|
6
|
-
const INIT_TOKEN = Symbol();
|
|
7
|
-
const IFRAME_URL = new URL("./iframe.mjs", import.meta.url).href;
|
|
8
|
-
/**
|
|
9
|
-
* Manages Elixir by setting up iframe, WASM module, and event listeners. Used to sent messages to Elixir processes.
|
|
10
|
-
*/
|
|
11
|
-
class Popcorn {
|
|
12
|
-
heartbeatTimeoutMs = null;
|
|
13
|
-
onReloadCallback;
|
|
14
|
-
bridge = null;
|
|
15
|
-
bridgeConfig;
|
|
16
|
-
debug = false;
|
|
17
|
-
bundleURLs;
|
|
18
|
-
state = { status: "uninitialized" };
|
|
19
|
-
defaultReceiver = null;
|
|
20
|
-
requestId = 0;
|
|
21
|
-
calls = new Map();
|
|
22
|
-
logListeners = {
|
|
23
|
-
stdout: new Set(),
|
|
24
|
-
stderr: new Set(),
|
|
25
|
-
};
|
|
26
|
-
messageHandlers = new Set();
|
|
27
|
-
mountResolve = null;
|
|
28
|
-
heartbeatTimeout = null;
|
|
29
|
-
reloadN = 0;
|
|
30
|
-
constructor(params, token) {
|
|
31
|
-
if (token !== INIT_TOKEN)
|
|
32
|
-
throwError({ t: "private_constructor" });
|
|
33
|
-
const bundlePaths = params.bundlePaths ?? ["/bundle.avm"];
|
|
34
|
-
this.bundleURLs = bundlePaths.map((p) => new URL(p, import.meta.url).href);
|
|
35
|
-
this.onReloadCallback = params.onReload ?? noop;
|
|
36
|
-
this.debug = params.debug ?? false;
|
|
37
|
-
this.bridgeConfig = {
|
|
38
|
-
container: params.container,
|
|
39
|
-
script: { url: IFRAME_URL, entrypoint: "initVm" },
|
|
40
|
-
config: Object.fromEntries(this.bundleURLs.map((url, i) => [`bundle-path-${i}`, url])),
|
|
41
|
-
debug: true,
|
|
42
|
-
onMessage: this.iframeHandler.bind(this),
|
|
43
|
-
};
|
|
44
|
-
this.logListeners.stdout.add(params.onStdout ?? console.log);
|
|
45
|
-
this.logListeners.stderr.add(params.onStderr ?? console.warn);
|
|
46
|
-
this.heartbeatTimeoutMs = params.heartbeatTimeoutMs ?? HEARTBEAT_TIMEOUT_MS;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Creates an iframe and sets up communication channels.
|
|
50
|
-
* Returns after the Elixir app calls `Popcorn.Wasm.ready/0,1`.
|
|
51
|
-
*
|
|
52
|
-
* @example
|
|
53
|
-
* import { Popcorn } from "@swmansion/popcorn";
|
|
54
|
-
* const popcorn = await Popcorn.init({
|
|
55
|
-
* onStdout: console.log,
|
|
56
|
-
* onStderr: console.error,
|
|
57
|
-
* debug: true,
|
|
58
|
-
* });
|
|
59
|
-
*/
|
|
60
|
-
static async init(options) {
|
|
61
|
-
const { container, ...constructorParams } = options;
|
|
62
|
-
const containerWithDefault = container ?? document.documentElement;
|
|
63
|
-
const bundlePaths = constructorParams.bundlePaths && constructorParams.bundlePaths.length > 0
|
|
64
|
-
? constructorParams.bundlePaths
|
|
65
|
-
: [await resolveBundleURL("/bundle.avm", "/assets/bundle.avm")];
|
|
66
|
-
const popcorn = new Popcorn({ ...constructorParams, bundlePaths, container: containerWithDefault }, INIT_TOKEN);
|
|
67
|
-
popcorn.trace("Main: init, params: ", { container, ...constructorParams });
|
|
68
|
-
await popcorn.mount();
|
|
69
|
-
return popcorn;
|
|
70
|
-
}
|
|
71
|
-
async mount() {
|
|
72
|
-
if (this.bridge !== null)
|
|
73
|
-
throwError({ t: "already_mounted" });
|
|
74
|
-
this.assertStatus(["uninitialized", "reload"]);
|
|
75
|
-
this.transition({ status: "mount" });
|
|
76
|
-
this.trace("Main: mount, container: ", this.bridgeConfig.container);
|
|
77
|
-
this.bridge = new IframeBridge(this.bridgeConfig);
|
|
78
|
-
try {
|
|
79
|
-
const mountPromise = new Promise((resolve) => {
|
|
80
|
-
this.mountResolve = resolve;
|
|
81
|
-
});
|
|
82
|
-
let initTimeout;
|
|
83
|
-
await Promise.race([
|
|
84
|
-
mountPromise,
|
|
85
|
-
new Promise((_, reject) => {
|
|
86
|
-
initTimeout = setTimeout(() => reject(buildError({ t: "app_ready_timeout" })), INIT_VM_TIMEOUT_MS);
|
|
87
|
-
}),
|
|
88
|
-
]);
|
|
89
|
-
clearTimeout(initTimeout);
|
|
90
|
-
this.transition({ status: "ready" });
|
|
91
|
-
this.trace("Main: mounted");
|
|
92
|
-
this.onHeartbeat();
|
|
93
|
-
}
|
|
94
|
-
catch (error) {
|
|
95
|
-
this.deinit();
|
|
96
|
-
throw error;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Sends a message to an Elixir process and awaits for the response.
|
|
101
|
-
*
|
|
102
|
-
* If Elixir doesn't respond in configured timeout, the returned promise will be rejected with "process timeout" error.
|
|
103
|
-
*
|
|
104
|
-
* Unless passed via options, the name passed in `Popcorn.Wasm.set_default_receiver/1` on the Elixir side is used.
|
|
105
|
-
* Throws "Unspecified target process" if default process is not set and no process is specified.
|
|
106
|
-
*
|
|
107
|
-
* @example
|
|
108
|
-
* const result = await popcorn.call(
|
|
109
|
-
* { action: "get_user", id: 123 },
|
|
110
|
-
* { process: "user_server", timeoutMs: 5_000 },
|
|
111
|
-
* );
|
|
112
|
-
* console.log(result.data); // Deserialized Elixir response
|
|
113
|
-
* console.log(result.durationMs); // Entire call duration
|
|
114
|
-
*/
|
|
115
|
-
async call(args, { process, timeoutMs } = {}) {
|
|
116
|
-
this.assertStatus(["ready"]);
|
|
117
|
-
const targetProcess = process ?? this.defaultReceiver;
|
|
118
|
-
if (targetProcess === null)
|
|
119
|
-
throwError({ t: "bad_target" });
|
|
120
|
-
if (this.bridge === null)
|
|
121
|
-
throwError({ t: "unmounted" });
|
|
122
|
-
const requestId = this.requestId++;
|
|
123
|
-
const startTimeMs = performance.now();
|
|
124
|
-
const callPromise = new Promise((resolve) => {
|
|
125
|
-
this.calls.set(requestId, { acknowledged: false, startTimeMs, resolve });
|
|
126
|
-
});
|
|
127
|
-
this.trace("Main: call: ", { requestId, process, args });
|
|
128
|
-
this.bridge.sendIframeRequest({
|
|
129
|
-
type: MESSAGES.CALL,
|
|
130
|
-
value: { requestId, process: targetProcess, args },
|
|
131
|
-
});
|
|
132
|
-
const result = await withTimeout(callPromise, timeoutMs ?? CALL_TIMEOUT_MS);
|
|
133
|
-
this.calls.delete(requestId);
|
|
134
|
-
return result;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Sends a message to an Elixir process (default or from options) and returns immediately.
|
|
138
|
-
*
|
|
139
|
-
* Unless passed via options, the name passed in `Popcorn.Wasm.set_default_receiver/1` on the Elixir side is used.
|
|
140
|
-
* Throws "Unspecified target process" if default process is not set and no process is specified.
|
|
141
|
-
*/
|
|
142
|
-
cast(args, { process } = {}) {
|
|
143
|
-
this.assertStatus(["ready"]);
|
|
144
|
-
const targetProcess = process ?? this.defaultReceiver;
|
|
145
|
-
if (targetProcess === null)
|
|
146
|
-
throwError({ t: "bad_target" });
|
|
147
|
-
if (this.bridge === null)
|
|
148
|
-
throwError({ t: "unmounted" });
|
|
149
|
-
const requestId = this.requestId++;
|
|
150
|
-
this.trace("Main: cast: ", { requestId, process, args });
|
|
151
|
-
this.bridge.sendIframeRequest({
|
|
152
|
-
type: MESSAGES.CAST,
|
|
153
|
-
value: { requestId, process: targetProcess, args },
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Destroys an iframe and resets the instance.
|
|
158
|
-
*/
|
|
159
|
-
deinit() {
|
|
160
|
-
if (this.bridge === null)
|
|
161
|
-
throwError({ t: "unmounted" });
|
|
162
|
-
this.trace("Main: deinit");
|
|
163
|
-
this.transition({ status: "deinit" });
|
|
164
|
-
this.teardownBridge("deinitialized");
|
|
165
|
-
this.logListeners.stdout.clear();
|
|
166
|
-
this.logListeners.stderr.clear();
|
|
167
|
-
this.messageHandlers.clear();
|
|
168
|
-
}
|
|
169
|
-
teardownBridge(errorCode) {
|
|
170
|
-
if (this.bridge) {
|
|
171
|
-
this.bridge.deinit();
|
|
172
|
-
this.bridge = null;
|
|
173
|
-
}
|
|
174
|
-
this.mountResolve = null;
|
|
175
|
-
this.defaultReceiver = null;
|
|
176
|
-
if (this.heartbeatTimeout) {
|
|
177
|
-
clearTimeout(this.heartbeatTimeout);
|
|
178
|
-
this.heartbeatTimeout = null;
|
|
179
|
-
}
|
|
180
|
-
for (const callData of this.calls.values()) {
|
|
181
|
-
const durationMs = performance.now() - callData.startTimeMs;
|
|
182
|
-
callData.resolve({
|
|
183
|
-
ok: false,
|
|
184
|
-
error: new PopcornError(errorCode),
|
|
185
|
-
durationMs,
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
this.calls.clear();
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Registers a log listener that will be called when output of the specified type is received.
|
|
192
|
-
*/
|
|
193
|
-
registerLogListener(listener, type) {
|
|
194
|
-
this.logListeners[type].add(listener);
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Unregisters a previously registered log listener.
|
|
198
|
-
*/
|
|
199
|
-
unregisterLogListener(listener, type) {
|
|
200
|
-
this.logListeners[type].delete(listener);
|
|
201
|
-
}
|
|
202
|
-
notifyLogListeners(type, message) {
|
|
203
|
-
this.logListeners[type].forEach((listener) => {
|
|
204
|
-
listener(message);
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Registers a catch-all event handler. Returns an unsubscribe function.
|
|
209
|
-
*/
|
|
210
|
-
onMessage(handler) {
|
|
211
|
-
this.messageHandlers.add(handler);
|
|
212
|
-
return () => {
|
|
213
|
-
this.messageHandlers.delete(handler);
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
onEvent({ eventName, payload }) {
|
|
217
|
-
if (eventName.startsWith("popcorn")) {
|
|
218
|
-
if (eventName === EVENT_NAMES.ELIXIR_READY) {
|
|
219
|
-
this.trace("Main: elixir VM ready");
|
|
220
|
-
}
|
|
221
|
-
else if (eventName === EVENT_NAMES.APP_READY) {
|
|
222
|
-
this.defaultReceiver = payload.name;
|
|
223
|
-
this.mountResolve?.();
|
|
224
|
-
this.mountResolve = null;
|
|
225
|
-
}
|
|
226
|
-
else if (eventName === EVENT_NAMES.SET_DEFAULT_RECEIVER) {
|
|
227
|
-
this.defaultReceiver = payload.name;
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
this.trace("Unknown internal event:", eventName);
|
|
231
|
-
}
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
this.messageHandlers.forEach((handler) => {
|
|
235
|
-
try {
|
|
236
|
-
handler(eventName, payload);
|
|
237
|
-
}
|
|
238
|
-
catch (error) {
|
|
239
|
-
console.error(`Error in onMessage handler for '${eventName}':`, error);
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
iframeHandler(data) {
|
|
244
|
-
if (data.type === MESSAGES.EVENT) {
|
|
245
|
-
this.onEvent(data.value);
|
|
246
|
-
}
|
|
247
|
-
else if (data.type === MESSAGES.STDOUT) {
|
|
248
|
-
this.notifyLogListeners("stdout", data.value);
|
|
249
|
-
}
|
|
250
|
-
else if (data.type === MESSAGES.STDERR) {
|
|
251
|
-
this.notifyLogListeners("stderr", data.value);
|
|
252
|
-
}
|
|
253
|
-
else if (data.type === MESSAGES.CALL) {
|
|
254
|
-
this.onCall(data.value);
|
|
255
|
-
}
|
|
256
|
-
else if (data.type === MESSAGES.CALL_ACK) {
|
|
257
|
-
this.onCallAck(data.value);
|
|
258
|
-
}
|
|
259
|
-
else if (data.type === MESSAGES.HEARTBEAT) {
|
|
260
|
-
this.onHeartbeat();
|
|
261
|
-
}
|
|
262
|
-
else if (data.type === MESSAGES.RELOAD) {
|
|
263
|
-
this.reloadIframe();
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
throwError({ t: "assert" });
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
onCallAck({ requestId }) {
|
|
270
|
-
this.assertStatus(["ready"]);
|
|
271
|
-
this.trace("Main: onCallAck: ", { requestId });
|
|
272
|
-
const callData = this.calls.get(requestId);
|
|
273
|
-
if (callData === undefined)
|
|
274
|
-
throwError({ t: "bad_ack" });
|
|
275
|
-
this.calls.set(requestId, { ...callData, acknowledged: true });
|
|
276
|
-
}
|
|
277
|
-
onCall({ requestId, error, data, }) {
|
|
278
|
-
this.assertStatus(["ready"]);
|
|
279
|
-
this.trace("Main: onCall: ", { requestId, error, data });
|
|
280
|
-
const callData = this.calls.get(requestId);
|
|
281
|
-
if (callData === undefined)
|
|
282
|
-
throwError({ t: "bad_call" });
|
|
283
|
-
if (!callData.acknowledged)
|
|
284
|
-
throwError({ t: "no_acked_call" });
|
|
285
|
-
this.calls.delete(requestId);
|
|
286
|
-
const durationMs = performance.now() - callData.startTimeMs;
|
|
287
|
-
if (error !== undefined) {
|
|
288
|
-
callData.resolve({ ok: false, error, durationMs });
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
callData.resolve({ ok: true, data, durationMs });
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
onHeartbeat() {
|
|
295
|
-
if (this.heartbeatTimeout) {
|
|
296
|
-
clearTimeout(this.heartbeatTimeout);
|
|
297
|
-
}
|
|
298
|
-
this.heartbeatTimeout = setTimeout(() => {
|
|
299
|
-
this.trace("Main: heartbeat lost");
|
|
300
|
-
this.reloadIframe("heartbeat_lost");
|
|
301
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
302
|
-
}, this.heartbeatTimeoutMs);
|
|
303
|
-
}
|
|
304
|
-
reloadIframe(reason = "other") {
|
|
305
|
-
if (this.bridge === null) {
|
|
306
|
-
throwError({ t: "unmounted" });
|
|
307
|
-
}
|
|
308
|
-
if (document.hidden) {
|
|
309
|
-
this.trace("Main: reloading iframe skipped, window not visible");
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
this.reloadN++;
|
|
313
|
-
if (this.reloadN > MAX_RELOAD_N) {
|
|
314
|
-
this.trace("Main: exceeded max reload number");
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
this.trace("Main: reloading iframe");
|
|
318
|
-
this.transition({ status: "reload" });
|
|
319
|
-
this.teardownBridge("reload");
|
|
320
|
-
this.onReloadCallback(reason);
|
|
321
|
-
this.mount();
|
|
322
|
-
}
|
|
323
|
-
trace(...messages) {
|
|
324
|
-
if (this.debug) {
|
|
325
|
-
console.debug(...messages);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
transition(to) {
|
|
329
|
-
this.trace(`State: ${this.state.status} -> ${to.status}`);
|
|
330
|
-
this.state = to;
|
|
331
|
-
}
|
|
332
|
-
assertStatus(validStatuses) {
|
|
333
|
-
const currentStatus = this.state.status;
|
|
334
|
-
if (!validStatuses.includes(currentStatus)) {
|
|
335
|
-
throwError({
|
|
336
|
-
t: "bad_status",
|
|
337
|
-
status: currentStatus,
|
|
338
|
-
expectedStatus: validStatuses.join(" | "),
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
async function withTimeout(promise, ms) {
|
|
344
|
-
let timeout = null;
|
|
345
|
-
const timeoutPromise = new Promise((resolve) => {
|
|
346
|
-
timeout = setTimeout(() => {
|
|
347
|
-
resolve({
|
|
348
|
-
ok: false,
|
|
349
|
-
error: new PopcornError("timeout"),
|
|
350
|
-
durationMs: ms,
|
|
351
|
-
});
|
|
352
|
-
}, ms);
|
|
353
|
-
});
|
|
354
|
-
const result = await Promise.race([promise, timeoutPromise]);
|
|
355
|
-
if (!timeout)
|
|
356
|
-
throwError({ t: "assert" });
|
|
357
|
-
clearTimeout(timeout);
|
|
358
|
-
return result;
|
|
359
|
-
}
|
|
360
|
-
function noop() {
|
|
361
|
-
/* noop */
|
|
362
|
-
}
|
|
363
|
-
async function resolveBundleURL(primary, fallback) {
|
|
364
|
-
const fetchBundle = async (path) => {
|
|
365
|
-
const url = new URL(path, import.meta.url).href;
|
|
366
|
-
const response = await fetch(url, { method: "HEAD" });
|
|
367
|
-
const contentType = response.headers.get("Content-Type") ?? "";
|
|
368
|
-
if (!response.ok || contentType.includes("text/html")) {
|
|
369
|
-
throw new Error(`Bundle not found at "${path}"`);
|
|
370
|
-
}
|
|
371
|
-
return path;
|
|
372
|
-
};
|
|
373
|
-
try {
|
|
374
|
-
return await Promise.any([fetchBundle(primary), fetchBundle(fallback)]);
|
|
375
|
-
}
|
|
376
|
-
catch {
|
|
377
|
-
throwError({ t: "bundle_not_found", primary, fallback });
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
export { Popcorn, PopcornError };
|
package/dist/types.mjs
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
const INIT_VM_TIMEOUT_MS = 30_000;
|
|
2
|
-
const CALL_TIMEOUT_MS = 60_000;
|
|
3
|
-
const HEARTBEAT_TIMEOUT_MS = 60_000;
|
|
4
|
-
const MAX_RELOAD_N = 3;
|
|
5
|
-
const MESSAGES = {
|
|
6
|
-
EVENT: "popcorn-event",
|
|
7
|
-
CALL: "popcorn-call",
|
|
8
|
-
CAST: "popcorn-cast",
|
|
9
|
-
CALL_ACK: "popcorn-callAck",
|
|
10
|
-
STDOUT: "popcorn-stdout",
|
|
11
|
-
STDERR: "popcorn-stderr",
|
|
12
|
-
HEARTBEAT: "popcorn-heartbeat",
|
|
13
|
-
RELOAD: "popcorn-reload",
|
|
14
|
-
};
|
|
15
|
-
const EVENT_NAMES = {
|
|
16
|
-
ELIXIR_READY: "popcorn_elixir_ready",
|
|
17
|
-
APP_READY: "popcorn_app_ready",
|
|
18
|
-
SET_DEFAULT_RECEIVER: "popcorn_set_default_receiver",
|
|
19
|
-
};
|
|
20
|
-
const MESSAGES_TYPES = new Set(Object.values(MESSAGES));
|
|
21
|
-
function isMessageType(type) {
|
|
22
|
-
return MESSAGES_TYPES.has(type);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export { CALL_TIMEOUT_MS, EVENT_NAMES, HEARTBEAT_TIMEOUT_MS, INIT_VM_TIMEOUT_MS, MAX_RELOAD_N, MESSAGES, isMessageType };
|