@remix_labs/machine-starter 1.1929.0-dev → 2.1838.0-dev

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/common.js ADDED
@@ -0,0 +1,292 @@
1
+ import { nanoid } from "nanoid";
2
+ import { GROOVEBOX_BUILD } from "./groovebox_build.js";
3
+
4
+ let terminate = function(f) {};
5
+ if (globalThis.ThisIsNode) {
6
+ terminate = function(f) {
7
+ Process.on("exit", () => {
8
+ console.log("terminating machine worker");
9
+ f()
10
+ })
11
+ }
12
+ };
13
+
14
+ function CreateVMID() {
15
+ return nanoid()
16
+ }
17
+
18
+ let initialMask = 64 + 1024; // default for web
19
+ globalThis.MixSetMask = (m => {
20
+ initialMask = m;
21
+ });
22
+
23
+ function StartWASM(hub, baseURL, org, workspace, vmID, user, token, noOutputViaMQTT) {
24
+ return StartWASM2(hub, {
25
+ baseURL: baseURL,
26
+ org: org,
27
+ workspace: workspace,
28
+ token: token,
29
+ noOutputViaMQTT: noOutputViaMQTT,
30
+ machType: "WASM"
31
+ })
32
+ }
33
+
34
+ function StartWASM2(hub, config) {
35
+ console.log("GROOVEBOX_BUILD (machine-starter)", GROOVEBOX_BUILD);
36
+ return hub.newChannel().then(channel => {
37
+ // injected by either index.js or node.js
38
+ let worker = globalThis.GetMachineWASMWorker();
39
+ let localFFIs = {};
40
+ if (config.localFFIs) {
41
+ for (const [k, v] of Object.entries(config.localFFIs)) {
42
+ if (v.isRaw && (v.canFail || v.useJsonDecoder)) {
43
+ console.error("A raw, local FFI must neither be failing nor use the JSON decoder: " + k);
44
+ } else {
45
+ localFFIs[k] = {...v};
46
+ delete localFFIs[k].run;
47
+ }
48
+ }
49
+ }
50
+ let config_msg =
51
+ { "_rmx_type": "msg_vm_configure",
52
+ "baseURL": config.baseURL,
53
+ "org": config.org,
54
+ "workspace": config.workspace,
55
+ "vmID": config.vmID,
56
+ "user": config.user,
57
+ "token": config.token,
58
+ "hub": channel.port,
59
+ "outputViaMQTT": !(config.noOutputViaMQTT),
60
+ "machType": config.machType,
61
+ "debugMask": initialMask,
62
+ "localFFIs": localFFIs,
63
+ "grooveboxUrlPrefix": globalThis.GROOVEBOX_URL_PREFIX,
64
+ "allowInsecureHttp": globalThis.GROOVEBOX_ALLOW_INSECURE_HTTP,
65
+ "interceptFFI": config.interceptFFI,
66
+ "interceptDynloadFFI": config.interceptDynloadFFI,
67
+ };
68
+ worker.postMessage(config_msg, [ channel.port ]);
69
+ terminate(() => worker.terminate());
70
+ globalThis.MixSetMask = (m => {
71
+ worker.postMessage({ "_rmx_type": "msg_vm_logMask",
72
+ "mask": m
73
+ })
74
+ });
75
+ if (config.localFFIs)
76
+ setupLocalFFIs(hub, config); // don't await!
77
+ return worker;
78
+ })
79
+ }
80
+
81
+ class Token {
82
+ constructor(data, enabled, extras) {
83
+ // data: must be an Uint8Array
84
+ // enabled: a number with the bitset of enabled operations
85
+ // extras: a map
86
+ if (!data instanceof Uint8Array)
87
+ throw new Error("Token: data must be an Uint8Array");
88
+ if (typeof(enabled) != "number")
89
+ throw new Error("Token: enabled_ops must be a number");
90
+ this.data = data;
91
+ this.enabled = enabled;
92
+ this.extras = extras;
93
+ }
94
+ encode() {
95
+ return new Map([
96
+ [ "_rmx_type", "token" ],
97
+ [ "data", this.data ],
98
+ [ "enabled", this.enabled ],
99
+ [ "extras", this.extras ]
100
+ ]);
101
+ }
102
+ }
103
+
104
+ function decode_Token(m) {
105
+ return new Token(m.get("data"), m.get("enabled"), m.get("extras"))
106
+ }
107
+
108
+ class Case {
109
+ constructor(name, arg) {
110
+ if (typeof(name) != "string")
111
+ throw new Error("Case: name must be a string");
112
+ this.name = name;
113
+ this.arg = arg;
114
+ }
115
+ encode() {
116
+ return new Map([
117
+ [ "_rmx_type", "case" ],
118
+ [ "name", this.name ],
119
+ [ "arg", this.arg ],
120
+ ]);
121
+ }
122
+ }
123
+
124
+ function decode_Case(m) {
125
+ return new Case(m.get("name"), m.get("arg"))
126
+ }
127
+
128
+ class Opaque {
129
+ constructor(buf) {
130
+ if (!(buf instanceof Uint32Array))
131
+ throw new Error("Opaque: must be a Uint32Array");
132
+ this.data = buf;
133
+ }
134
+ encode() {
135
+ return new Map([
136
+ [ "_rmx_type", "opaque" ],
137
+ [ "data", this.data ],
138
+ ]);
139
+ }
140
+ }
141
+
142
+ function decode_Opaque(m) {
143
+ return new Opaque(m.get("data"))
144
+ }
145
+
146
+ function encode(val) {
147
+ switch (typeof(val)) {
148
+ case "object":
149
+ if (val === null) return val;
150
+ if (Array.isArray(val)) {
151
+ // array: map the array elements
152
+ val = val.map(encode)
153
+ } else if (Object.getPrototypeOf(val) === Object.prototype) {
154
+ // simple object: map the object elements
155
+ val = Object.fromEntries(Object.entries(val).map(([ key, elem ]) => [ key, encode(elem) ]))
156
+ } else if (val.encode instanceof Function) {
157
+ // map after encode(): map the map elements
158
+ val = new Map(Array.from(val.encode().entries()).map(([ key, elem ]) => [ key, encode(elem) ]))
159
+ };
160
+ return val;
161
+ default:
162
+ return val;
163
+ }
164
+ }
165
+
166
+ function decode(val) {
167
+ switch (typeof(val)) {
168
+ case "object":
169
+ if (val === null) return val;
170
+ if (Array.isArray(val)) {
171
+ // array: map the array elements
172
+ val = val.map(decode)
173
+ } else if (Object.getPrototypeOf(val) === Object.prototype) {
174
+ // simple object: map the object elements
175
+ val = Object.fromEntries(Object.entries(val).map(([ key, elem ]) => [ key, decode(elem) ]))
176
+ } else if (val instanceof Map) {
177
+ val = new Map(Array.from(val.entries()).map(([ key, elem ]) => [ key, decode(elem) ]))
178
+ let ty = val.get("_rmx_type");
179
+ switch (ty) {
180
+ case "token":
181
+ return decode_Token(val);
182
+ case "case":
183
+ return decode_Case(val);
184
+ case "opaque":
185
+ return decode_Opaque(val);
186
+ default:
187
+ throw new Error("decode: bad _rmx_type: " + ty);
188
+ }
189
+ };
190
+ return val;
191
+ default:
192
+ return val;
193
+ }
194
+ }
195
+
196
+ async function setupLocalFFIs(hub, config) {
197
+ let channel = await hub.newChannel();
198
+ let comm = new FFIComm(hub, channel);
199
+ let localFFIs = config.localFFIs;
200
+ await channel.setLocalSubTopic("/local/ffi/call");
201
+ await channel.setLocalPubTopic("/local/ffi/return");
202
+ let sub = await channel.subscribe("/local/ffi/call");
203
+ while (true) {
204
+ let msg = await sub.next();
205
+ let name = msg.payload.name;
206
+ let call_id = msg.payload.call_id;
207
+ let args = decode(msg.payload.args);
208
+ let fun = localFFIs[name];
209
+ try {
210
+ if (!fun)
211
+ throw new Error("no such local FFI: " + name);
212
+ let connector =
213
+ { call_id: call_id,
214
+ hub: hub,
215
+ channel: channel,
216
+ state: config.state,
217
+ };
218
+ let r = fun instanceof Function ? fun(connector, args) : fun.run(connector, args);
219
+ if (r instanceof Promise) {
220
+ await comm.later(call_id);
221
+ r.then(
222
+ (value) => comm.returnOrFail(fun, call_id, value),
223
+ (reason) => comm.error(call_id, reason.message, reason.stack));
224
+ } else {
225
+ await comm.returnOrFail(fun, call_id, r);
226
+ }
227
+ } catch (reason) {
228
+ await comm.error(call_id, reason.message, reason.stack);
229
+ }
230
+ }
231
+ }
232
+
233
+ class FFIComm {
234
+ constructor(hub, channel) {
235
+ this.hub = hub;
236
+ this.channel = channel;
237
+ }
238
+ later(call_id) {
239
+ let r_payload =
240
+ { call_id: call_id,
241
+ };
242
+ let response = this.hub.newLocalMessage("msg_ffi_later", "starter", r_payload);
243
+ return this.channel.publish("/local/ffi/return", response, false);
244
+ }
245
+ return_(fun, call_id, result) {
246
+ let r_payload =
247
+ { call_id: call_id,
248
+ value: fun.useJsonDecoder ? result : encode(result)
249
+ };
250
+ let response = this.hub.newLocalMessage("msg_ffi_return", "starter", r_payload);
251
+ return this.channel.publish("/local/ffi/return", response, false);
252
+ }
253
+ error(call_id, message, stack) {
254
+ let r_payload =
255
+ { call_id: call_id,
256
+ message: message,
257
+ stack: stack === undefined ? [] : stack,
258
+ };
259
+ let response = this.hub.newLocalMessage("msg_ffi_error", "starter", r_payload);
260
+ this.channel.publish("/local/ffi/return", response, false);
261
+ }
262
+ returnOrFail(fun, call_id, result) {
263
+ if (fun.canFail) {
264
+ var name, arg;
265
+ if (result instanceof Case) {
266
+ name = result.name;
267
+ arg = result.arg;
268
+ } else if (fun.useJsonDecoder && result._rmx_type && result._rmx_type.startsWith("{tag}case:")) {
269
+ name = result._rmx_type.substring(10);
270
+ arg = result._rmx_value;
271
+ } else {
272
+ return this.error(call_id, "expected result from local FFI that can fail");
273
+ }
274
+ switch (name) {
275
+ case "ok":
276
+ return this.return_(fun, call_id, arg);
277
+ case "error":
278
+ return this.error(call_id, arg);
279
+ default:
280
+ return this.error(call_id, "expected result from local FFI that can fail, found case value " + name);
281
+ }
282
+ } else {
283
+ return this.return_(fun, call_id, result);
284
+ }
285
+ }
286
+ }
287
+
288
+ // use worker.terminate() to shut a worker down!
289
+
290
+ export { CreateVMID, StartWASM, StartWASM2,
291
+ Token, Case, Opaque
292
+ }
@@ -1,2 +1,2 @@
1
- var GROOVEBOX_BUILD = "1929";
1
+ var GROOVEBOX_BUILD = "1931";
2
2
  export { GROOVEBOX_BUILD }
package/index.js CHANGED
@@ -1,308 +1,15 @@
1
- import { nanoid } from "nanoid";
2
- import { GROOVEBOX_BUILD } from "./groovebox_build.js";
3
-
4
- let terminate = function(f) {};
5
- if (globalThis.ThisIsNode) {
6
- terminate = function(f) {
7
- Process.on("exit", () => {
8
- console.log("terminating machine worker");
9
- f()
10
- })
11
- }
12
- };
13
-
14
- function CreateVMID() {
15
- return nanoid()
16
- }
17
-
18
- let initialMask = 64 + 1024; // default for web
19
- globalThis.MixSetMask = (m => {
20
- initialMask = m;
21
- });
22
-
23
- function StartWASM(hub, baseURL, org, workspace, vmID, user, token, noOutputViaMQTT) {
24
- return StartWASM2(hub, {
25
- baseURL: baseURL,
26
- org: org,
27
- workspace: workspace,
28
- vmID: vmID,
29
- user: user,
30
- token: token,
31
- noOutputViaMQTT: noOutputViaMQTT,
32
- machType: "WASM"
33
- })
34
- }
35
-
36
- function StartWASM2(hub, config) {
37
- console.log("GROOVEBOX_BUILD (machine-starter)", GROOVEBOX_BUILD);
38
- return hub.newChannel().then(channel => {
39
- let bundle;
40
- if (globalThis.ThisIsNode) {
41
- bundle = new URL("../../machine-wasm/dist/node-bundle.js", import.meta.url);
42
- } else if (globalThis.GROOVEBOX_URL_PREFIX) {
43
- // FIXME: Stopgap solution until we figure out a way to bundle groovebox correctly
44
- // already fire off the download of the wasm code:
45
- let code_url = new URL(`${globalThis.GROOVEBOX_URL_PREFIX}/machine-wasm-code.wasm`, window.location.href);
46
- fetch(code_url, {cache:"default"}).then(resp => { return resp.arrayBuffer() });
47
- bundle = new URL(`${globalThis.GROOVEBOX_URL_PREFIX}/machine-wasm-core.js`, window.location.href);
48
- } else {
49
- // already fire off the download of the wasm code:
50
- let code_url = new URL("/g/machine-wasm-code.wasm", window.location.href);
51
- fetch(code_url, {cache:"default"}).then(resp => { return resp.arrayBuffer() });
52
- bundle = new URL("/g/machine-wasm-core.js", window.location.href);
53
- }
54
- let localFFIs = {};
55
- if (config.localFFIs) {
56
- for (const [k, v] of Object.entries(config.localFFIs)) {
57
- if (v.isRaw && (v.canFail || v.useJsonDecoder)) {
58
- console.error("A raw, local FFI must neither be failing nor use the JSON decoder: " + k);
59
- } else {
60
- localFFIs[k] = {...v};
61
- delete localFFIs[k].run;
62
- }
63
- }
64
- }
65
- let worker = new Worker(bundle);
66
- let config_msg =
67
- { "_rmx_type": "msg_vm_configure",
68
- "baseURL": config.baseURL,
69
- "org": config.org,
70
- "workspace": config.workspace,
71
- "vmID": config.vmID,
72
- "user": config.user,
73
- "token": config.token,
74
- "hub": channel.port,
75
- "outputViaMQTT": !(config.noOutputViaMQTT),
76
- "machType": config.machType,
77
- "debugMask": initialMask,
78
- "localFFIs": localFFIs,
79
- "grooveboxUrlPrefix": globalThis.GROOVEBOX_URL_PREFIX,
80
- "allowInsecureHttp": globalThis.GROOVEBOX_ALLOW_INSECURE_HTTP,
81
- "interceptFFI": config.interceptFFI,
82
- "interceptDynloadFFI": config.interceptDynloadFFI,
83
- };
84
- worker.postMessage(config_msg, [ channel.port ]);
85
- terminate(() => worker.terminate());
86
- globalThis.MixSetMask = (m => {
87
- worker.postMessage({ "_rmx_type": "msg_vm_logMask",
88
- "mask": m
89
- })
90
- });
91
- if (config.localFFIs)
92
- setupLocalFFIs(hub, config); // don't await!
93
- return worker;
94
- })
95
- }
96
-
97
- class Token {
98
- constructor(data, enabled, extras) {
99
- // data: must be an Uint8Array
100
- // enabled: a number with the bitset of enabled operations
101
- // extras: a map
102
- if (!data instanceof Uint8Array)
103
- throw new Error("Token: data must be an Uint8Array");
104
- if (typeof(enabled) != "number")
105
- throw new Error("Token: enabled_ops must be a number");
106
- this.data = data;
107
- this.enabled = enabled;
108
- this.extras = extras;
109
- }
110
- encode() {
111
- return new Map([
112
- [ "_rmx_type", "token" ],
113
- [ "data", this.data ],
114
- [ "enabled", this.enabled ],
115
- [ "extras", this.extras ]
116
- ]);
117
- }
1
+ function workerify(code) {
2
+ let blob = new Blob([code], {type: 'application/javascript'});
3
+ return new Worker(URL.createObjectURL(blob));
118
4
  }
119
5
 
120
- function decode_Token(m) {
121
- return new Token(m.get("data"), m.get("enabled"), m.get("extras"))
122
- }
123
-
124
- class Case {
125
- constructor(name, arg) {
126
- if (typeof(name) != "string")
127
- throw new Error("Case: name must be a string");
128
- this.name = name;
129
- this.arg = arg;
130
- }
131
- encode() {
132
- return new Map([
133
- [ "_rmx_type", "case" ],
134
- [ "name", this.name ],
135
- [ "arg", this.arg ],
136
- ]);
137
- }
138
- }
6
+ import MachineWASMWorker from './machine-wasm.worker.js';
139
7
 
140
- function decode_Case(m) {
141
- return new Case(m.get("name"), m.get("arg"))
8
+ function GetMachineWASMWorker() {
9
+ return workerify(MachineWASMWorker);
142
10
  }
143
11
 
144
- class Opaque {
145
- constructor(buf) {
146
- if (!(buf instanceof Uint32Array))
147
- throw new Error("Opaque: must be a Uint32Array");
148
- this.data = buf;
149
- }
150
- encode() {
151
- return new Map([
152
- [ "_rmx_type", "opaque" ],
153
- [ "data", this.data ],
154
- ]);
155
- }
156
- }
157
-
158
- function decode_Opaque(m) {
159
- return new Opaque(m.get("data"))
160
- }
161
-
162
- function encode(val) {
163
- switch (typeof(val)) {
164
- case "object":
165
- if (val === null) return val;
166
- if (Array.isArray(val)) {
167
- // array: map the array elements
168
- val = val.map(encode)
169
- } else if (Object.getPrototypeOf(val) === Object.prototype) {
170
- // simple object: map the object elements
171
- val = Object.fromEntries(Object.entries(val).map(([ key, elem ]) => [ key, encode(elem) ]))
172
- } else if (val.encode instanceof Function) {
173
- // map after encode(): map the map elements
174
- val = new Map(Array.from(val.encode().entries()).map(([ key, elem ]) => [ key, encode(elem) ]))
175
- };
176
- return val;
177
- default:
178
- return val;
179
- }
180
- }
181
-
182
- function decode(val) {
183
- switch (typeof(val)) {
184
- case "object":
185
- if (val === null) return val;
186
- if (Array.isArray(val)) {
187
- // array: map the array elements
188
- val = val.map(decode)
189
- } else if (Object.getPrototypeOf(val) === Object.prototype) {
190
- // simple object: map the object elements
191
- val = Object.fromEntries(Object.entries(val).map(([ key, elem ]) => [ key, decode(elem) ]))
192
- } else if (val instanceof Map) {
193
- val = new Map(Array.from(val.entries()).map(([ key, elem ]) => [ key, decode(elem) ]))
194
- let ty = val.get("_rmx_type");
195
- switch (ty) {
196
- case "token":
197
- return decode_Token(val);
198
- case "case":
199
- return decode_Case(val);
200
- case "opaque":
201
- return decode_Opaque(val);
202
- default:
203
- throw new Error("decode: bad _rmx_type: " + ty);
204
- }
205
- };
206
- return val;
207
- default:
208
- return val;
209
- }
210
- }
211
-
212
- async function setupLocalFFIs(hub, config) {
213
- let channel = await hub.newChannel();
214
- let comm = new FFIComm(hub, channel);
215
- let localFFIs = config.localFFIs;
216
- await channel.setLocalSubTopic("/local/ffi/call");
217
- await channel.setLocalPubTopic("/local/ffi/return");
218
- let sub = await channel.subscribe("/local/ffi/call");
219
- while (true) {
220
- let msg = await sub.next();
221
- let name = msg.payload.name;
222
- let call_id = msg.payload.call_id;
223
- let args = decode(msg.payload.args);
224
- let fun = localFFIs[name];
225
- try {
226
- if (!fun)
227
- throw new Error("no such local FFI: " + name);
228
- let connector =
229
- { call_id: call_id,
230
- hub: hub,
231
- channel: channel,
232
- state: config.state,
233
- };
234
- let r = fun instanceof Function ? fun(connector, args) : fun.run(connector, args);
235
- if (r instanceof Promise) {
236
- await comm.later(call_id);
237
- r.then(
238
- (value) => comm.returnOrFail(fun, call_id, value),
239
- (reason) => comm.error(call_id, reason.message, reason.stack));
240
- } else {
241
- await comm.returnOrFail(fun, call_id, r);
242
- }
243
- } catch (reason) {
244
- await comm.error(call_id, reason.message, reason.stack);
245
- }
246
- }
247
- }
248
-
249
- class FFIComm {
250
- constructor(hub, channel) {
251
- this.hub = hub;
252
- this.channel = channel;
253
- }
254
- later(call_id) {
255
- let r_payload =
256
- { call_id: call_id,
257
- };
258
- let response = this.hub.newLocalMessage("msg_ffi_later", "starter", r_payload);
259
- return this.channel.publish("/local/ffi/return", response, false);
260
- }
261
- return_(fun, call_id, result) {
262
- let r_payload =
263
- { call_id: call_id,
264
- value: fun.useJsonDecoder ? result : encode(result)
265
- };
266
- let response = this.hub.newLocalMessage("msg_ffi_return", "starter", r_payload);
267
- return this.channel.publish("/local/ffi/return", response, false);
268
- }
269
- error(call_id, message, stack) {
270
- let r_payload =
271
- { call_id: call_id,
272
- message: message,
273
- stack: stack === undefined ? [] : stack,
274
- };
275
- let response = this.hub.newLocalMessage("msg_ffi_error", "starter", r_payload);
276
- this.channel.publish("/local/ffi/return", response, false);
277
- }
278
- returnOrFail(fun, call_id, result) {
279
- if (fun.canFail) {
280
- var name, arg;
281
- if (result instanceof Case) {
282
- name = result.name;
283
- arg = result.arg;
284
- } else if (fun.useJsonDecoder && result._rmx_type && result._rmx_type.startsWith("{tag}case:")) {
285
- name = result._rmx_type.substring(10);
286
- arg = result._rmx_value;
287
- } else {
288
- return this.error(call_id, "expected result from local FFI that can fail");
289
- }
290
- switch (name) {
291
- case "ok":
292
- return this.return_(fun, call_id, arg);
293
- case "error":
294
- return this.error(call_id, arg);
295
- default:
296
- return this.error(call_id, "expected result from local FFI that can fail, found case value " + name);
297
- }
298
- } else {
299
- return this.return_(fun, call_id, result);
300
- }
301
- }
302
- }
12
+ globalThis.GetMachineWASMWorker = GetMachineWASMWorker;
303
13
 
304
- // use worker.terminate() to shut a worker down!
14
+ export * from './common.js';
305
15
 
306
- export { CreateVMID, StartWASM, StartWASM2,
307
- Token, Case, Opaque
308
- }