dcp-client 4.3.5 → 4.3.7

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.
@@ -1 +1 @@
1
- {"browser":["deny-node","kvin/kvin.js","script-load-wrapper","wrap-event-listeners","timer-classes","event-loop-virtualization","unique-timing","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap"],"node":["kvin/kvin.js","sa-ww-simulation","script-load-wrapper","wrap-event-listeners","timer-classes","event-loop-virtualization","unique-timing","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap"],"native":["deny-node","kvin/kvin.js","sa-ww-simulation","script-load-wrapper","native-event-loop","wrap-event-listeners","timer-classes","event-loop-virtualization","unique-timing","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap"],"nodeTesting":["kvin/kvin.js","sa-ww-simulation","script-load-wrapper","wrap-event-listeners","timer-classes","event-loop-virtualization","unique-timing","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap","testing.js"],"testing":["deny-node","kvin/kvin.js","sa-ww-simulation","script-load-wrapper","native-event-loop","wrap-event-listeners","timer-classes","event-loop-virtualization","unique-timing","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap","testing.js"]}
1
+ {"browser":["deny-node","kvin/kvin.js","script-load-wrapper","wrap-event-listeners","timer-classes","event-loop-virtualization","unique-timing","worktimes","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","pyodide-core","calculate-capabilities","bootstrap"],"node":["kvin/kvin.js","sa-ww-simulation","script-load-wrapper","wrap-event-listeners","timer-classes","event-loop-virtualization","unique-timing","worktimes","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","pyodide-core","calculate-capabilities","bootstrap"],"native":["deny-node","kvin/kvin.js","sa-ww-simulation","script-load-wrapper","native-event-loop","wrap-event-listeners","timer-classes","event-loop-virtualization","unique-timing","worktimes","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","pyodide-core","calculate-capabilities","bootstrap"],"nodeTesting":["kvin/kvin.js","sa-ww-simulation","script-load-wrapper","wrap-event-listeners","timer-classes","event-loop-virtualization","unique-timing","worktimes","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","pyodide-core","calculate-capabilities","bootstrap","testing.js"],"testing":["deny-node","kvin/kvin.js","sa-ww-simulation","script-load-wrapper","native-event-loop","wrap-event-listeners","timer-classes","event-loop-virtualization","unique-timing","worktimes","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","pyodide-core","calculate-capabilities","bootstrap","testing.js"]}
package/index.js CHANGED
@@ -373,11 +373,17 @@ function magicView(node, seen)
373
373
  *
374
374
  * Returns false is the file simply does not exist.
375
375
  *
376
+ * Setting `DCP_CLIENT_ALLOW_INSECURE_CONFIGURATION` to a non-empty value disables
377
+ * the security check.
378
+ *
376
379
  * @param {string} fullPath the full path to the file to check
377
380
  * @param {object} statBuf [optional] existing stat buf for the file
378
381
  */
379
382
  function checkConfigFileSafePerms(fullPath, statBuf)
380
383
  {
384
+ if (process.env.DCP_CLIENT_ALLOW_INSECURE_CONFIGURATION)
385
+ return true;
386
+
381
387
  const fun = checkConfigFileSafePerms;
382
388
 
383
389
  if (!fs.existsSync(fullPath))
@@ -12,24 +12,17 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
12
12
  const ring1PostMessage = self.postMessage;
13
13
  const global = typeof globalThis === 'undefined' ? self : globalThis;
14
14
 
15
- // aggregated from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#Reflection
16
15
  const allowList = new Set([
17
- '__proto__',
18
- 'addEventListener',
19
- 'applyAccesslist',
16
+ // global objects, aggregated from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#Reflection
17
+ 'AggregateError',
20
18
  'Array',
21
19
  'ArrayBuffer',
22
- 'AsyncFunction',
23
20
  'Atomics',
24
21
  'BigInt',
25
22
  'BigInt64Array',
26
23
  'BigUint64Array',
27
24
  'Boolean',
28
- 'Blob',
29
- 'bravojs',
30
- 'clearInterval',
31
- 'clearTimeout',
32
- 'console',
25
+
33
26
  'constructor',
34
27
  'DataView',
35
28
  'Date',
@@ -41,12 +34,10 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
41
34
  'escape',
42
35
  'eval',
43
36
  'EvalError',
44
- 'File',
45
- 'FileReader',
46
37
  'Float32Array',
47
38
  'Float64Array',
48
39
  'Function',
49
- 'Headers',
40
+ 'globalThis',
50
41
  'Infinity',
51
42
  'Int16Array',
52
43
  'Int32Array',
@@ -58,7 +49,6 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
58
49
  'Math',
59
50
  'module',
60
51
  'NaN',
61
- 'navigator',
62
52
  'null',
63
53
  'Number',
64
54
  'Object',
@@ -67,26 +57,18 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
67
57
  'onmessage',
68
58
  'parseFloat',
69
59
  'parseInt',
70
- 'performance',
71
60
  'postMessage',
72
61
  'Promise',
73
62
  'propertyIsEnumerable',
74
63
  'Proxy',
75
- 'pt0',
76
64
  'RangeError',
77
65
  'ReferenceError',
78
66
  'Reflect',
79
67
  'RegExp',
80
- 'removeEventListener',
81
- 'requestAnimationFrame',
82
68
  'require',
83
69
  'Response',
84
- 'self',
85
70
  'Set',
86
- 'setInterval',
87
- 'setTimeout',
88
- 'setImmediate',
89
- 'sleep',
71
+ 'SharedArrayBuffer',
90
72
  'String',
91
73
  'Symbol',
92
74
  'SyntaxError',
@@ -96,7 +78,6 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
96
78
  'toString',
97
79
  'TypeError',
98
80
  'URIError',
99
- 'URL',
100
81
  'Uint16Array',
101
82
  'Uint32Array',
102
83
  'Uint8Array',
@@ -106,11 +87,44 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
106
87
  'valueOf',
107
88
  'WeakMap',
108
89
  'WeakSet',
90
+ 'WeakRef',
91
+ '__proto__',
92
+
93
+ // WorkerGlobalScope symbols, aggregated from https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope
94
+ // 'caches',
95
+ //'crossOriginIsolated',
96
+ 'crypto',
97
+ //'fonts',
98
+ // 'indexedDB',
99
+ 'isSecureContext',
100
+ 'location',
101
+ 'navigator',
102
+ //'origin',
103
+ 'performance',
104
+ // 'scheduler',
105
+ 'self',
106
+ 'console',
107
+ 'atob',
108
+ 'btoa',
109
+ 'clearInterval',
110
+ 'clearTimeout',
111
+ // 'fetch',
112
+ // 'importScripts',
113
+ 'queueMicrotask',
114
+ 'setInterval',
115
+ 'setTimeout',
116
+ 'structuredClone',
117
+ 'reportError',
118
+ 'WorkerGlobalScope',
119
+
120
+ // WebAssembly symbols
109
121
  'WebAssembly',
122
+
123
+ // WebGL symbols
110
124
  'WebGL2RenderingContext',
111
125
  'WebGLTexture',
112
- 'WorkerGlobalScope',
113
- // All webGPU symbols are allowed
126
+
127
+ // WebGPU symbols
114
128
  'WebGPUWindow',
115
129
  'GPU',
116
130
  'GPUAdapter',
@@ -152,9 +166,15 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
152
166
  'GPUTextureView',
153
167
  'GPUUncapturedErrorEvent',
154
168
  'GPUValidationError',
155
- // Our own symbols
169
+
170
+ // DCP symbols / chosen web API additions
156
171
  'progress',
157
172
  'work',
173
+ 'bravojs',
174
+ 'setImmediate',
175
+ 'Blob',
176
+ 'addEventListener',
177
+ 'removeEventListener',
158
178
  ]);
159
179
 
160
180
  // Origin time for performance polyfill
@@ -169,15 +189,15 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
169
189
  // Assumption that if performance exists, performance.now must exist
170
190
  performance: typeof performance !== 'undefined' ? performance : {
171
191
  now: ()=>{
172
- res = new Date().getTime() - pt0;
192
+ const res = new Date().getTime() - pt0;
173
193
  return res;
174
194
  }
175
195
  },
176
196
  importScripts: function () {
177
197
  throw new Error('importScripts is not supported on DCP');
178
198
  },
179
- WorkerGlobalScope: typeof globalThis === 'undefined' ? self : globalThis,
180
199
  globalThis: typeof globalThis === 'undefined' ? self : globalThis,
200
+ WorkerGlobalScope: typeof globalThis === 'undefined' ? self : globalThis,
181
201
  // For browsers/SA-workers that don't support btoa/atob, modified from https://github.com/MaxArt2501/base64-js/blob/master/base64.js
182
202
  btoa: function (string) {
183
203
  var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
@@ -659,10 +679,9 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
659
679
  *
660
680
  * @param {object} obj - The object, which will have the allow list applied to its properties.
661
681
  * @param {Set} allowList - A set of properties to allow people to access.
662
- * @param {Set} blockList - An object of property names mapping to booleans to indicate whether access is allowed or not.
663
- * @param {Set} polyfills - An object of property names that have been polyfilled.
682
+ * @param {Object} blockList - An object of property names mapping to booleans to indicate whether access is allowed or not.
664
683
  */
665
- function applyAccessLists(obj, allowList, blockList = {}, polyfills = {}) {
684
+ function applyAccessLists(obj, allowList, blockList = {}) {
666
685
  if (!obj) { return; }
667
686
  Object.getOwnPropertyNames(obj).forEach(function (prop) {
668
687
  if (Object.getOwnPropertyDescriptor(obj, prop).configurable) {
@@ -670,17 +689,13 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
670
689
  let isSet = false;
671
690
  let propValue;
672
691
  Object.defineProperty(obj, prop, {
673
- get: function () {
674
- if (isSet) {
692
+ get: function getProtectedProperty() {
693
+ if (isSet)
675
694
  return propValue;
676
- } else {
677
- if (prop in polyfills) {
678
- return polyfills[prop];
679
- }
695
+ else
680
696
  return undefined;
681
- }
682
697
  },
683
- set: function (value) {
698
+ set: function setProtectedProperty(value) {
684
699
  propValue = value;
685
700
  isSet = true;
686
701
  },
@@ -711,30 +726,20 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
711
726
  }
712
727
 
713
728
  /**
714
- * Applies a list of polyfills to symbols not present in the global object. Will apply
715
- * this list through the objects entire prototype chain
729
+ * Assigns each property from some polyfill object to another object, inserting or replacing for each property
716
730
  *
717
- * @param {Object} obj - The global object to add properties on
718
- * @param {Set} polyfills - An object of property names to create/polyfill
731
+ * @param {Object} obj - The object to add properties on
732
+ * @param {Object} polyfills - An object of properties to create/polyfill
719
733
  */
720
- function applyPolyfills(obj, polyfills = {}) {
734
+ function applyPolyfills(obj, polyfills) {
721
735
  // Apply symbols from polyfill object
722
736
  for (let prop in polyfills) {
723
- let found = false;
724
- for (let o = obj; o.__proto__ && (o.__proto__ !== Object); o = o.__proto__) {
725
- if (o.hasOwnProperty(prop)) {
726
- found = true;
727
- break;
728
- }
729
- }
730
- if (found) { continue; }
731
737
  let propValue = polyfills[prop];
732
738
  Object.defineProperty(obj, prop, {
733
- get: function () {
739
+ get: function getPolyfill() {
734
740
  return propValue;
735
-
736
741
  },
737
- set: function (value) {
742
+ set: function setPolyfill(value) {
738
743
  propValue = value;
739
744
  },
740
745
  configurable: false
@@ -748,43 +753,23 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
748
753
  * so that the blockList is accessible to modify w/o adding it to the allowList.
749
754
  */
750
755
  function applyAllAccessLists() {
751
- // We need to apply the access lists to global, global.__proto__, and global.__proto__.__proto__,
752
- // because there's networking-accessing functions inside global.__proto__.__proto__, like fetch.
753
- //
754
- // If we're in a robust environment (node, browser, WebWorker, basically anything but v8),
755
- // then we have to climb the prototype chain and apply the allowList there, but we have to stop
756
- // before we allow Object's properties
756
+ // We need to apply the access lists to global, and the entirety of global's prototype chain
757
+ // because there's networking-accessing functions inside the chain, like fetch.
757
758
 
758
759
  var global = typeof globalThis === 'undefined' ? self : globalThis;
759
-
760
- // Ternary expression to avoid a ReferenceError on navigator
761
- let _GPU = ((typeof navigator !== 'undefined') && (typeof navigator.gpu !== 'undefined')) ? navigator.gpu :
762
- (typeof GPU !== 'undefined'? GPU : undefined);
763
-
764
- for (let g = global; g.__proto__ && (g.__proto__ !== Object); g = g.__proto__) {
765
- applyAccessLists(g, allowList, blockList, polyfills);
766
- }
760
+ for (let g = global; Object.getPrototypeOf(g); g = Object.getPrototypeOf(g))
761
+ applyAccessLists(g, allowList, blockList);
767
762
 
768
763
  if (typeof navigator === 'undefined')
764
+ navigator = { userAgent: 'not a browser' };
765
+ else
769
766
  {
770
- navigator = {
771
- userAgent: 'not a browser',
772
- gpu: _GPU,
767
+ const navPolyFill = {
768
+ userAgent: navigator.userAgent || 'not a browser',
773
769
  };
774
- }
775
- else if (!protectedStorage.createdNewNavigator)
776
- {
777
- // We also want to allowList certain parts of navigator, but not others.
778
- const navAllowlist = new Set([
779
- 'userAgent',
780
- 'gpu',
781
- ]);
782
- let navPolyfill = {
783
- userAgent: typeof navigator.userAgent !== 'undefined'? navigator.userAgent : 'not a browser',
784
- gpu: _GPU,
785
- };
786
- applyAccessLists(navigator.__proto__, navAllowlist, {}, {}, navPolyfill);
787
- applyPolyfills(navigator.__proto__, navPolyfill);
770
+ if (navigator.gpu)
771
+ navPolyFill.gpu = navigator.gpu;
772
+ navigator = navPolyFill;
788
773
  }
789
774
  }
790
775
 
@@ -835,6 +820,12 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
835
820
  };
836
821
  }
837
822
 
823
+ function allowWorktimeSymbols(symbols)
824
+ {
825
+ for (let symbol of symbols)
826
+ allowList.add(symbol);
827
+ }
828
+
838
829
  addEventListener('message', async (event) => {
839
830
  try {
840
831
  if (event.request === 'applyRequirements') {
@@ -845,6 +836,10 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
845
836
  blockList.OffscreenCanvas = !requirements.environment.offscreenCanvas;
846
837
  blockList.WebGPUWindow = !requirements.environment.webgpu;
847
838
  blockList.GPU = !requirements.environment.webgpu;
839
+
840
+ if (event.worktime && protectedStorage.worktimeGlobals[event.worktime])
841
+ allowWorktimeSymbols(protectedStorage.worktimeGlobals[event.worktime]);
842
+
848
843
  applyAllAccessLists();
849
844
 
850
845
  ring1PostMessage({ request: 'applyRequirementsDone' });
@@ -74,7 +74,7 @@ self.wrapScriptLoading({ scriptName: 'bravojs-env', ringTransition: true }, func
74
74
  protectedStorage.sandboxConfig = message.sandboxConfig;
75
75
  Object.assign(self.work.job.public, message.job.public); /* override locale-specific defaults if specified */
76
76
  // Load bravojs' module.main with the work function
77
- module.declare(message.job.dependencies || (message.job.requireModules /* deprecated */), function mainModule(require, exports, module) {
77
+ module.declare(message.job.dependencies || (message.job.requireModules /* deprecated */), async function mainModule(require, exports, module) {
78
78
  try {
79
79
  if (exports.hasOwnProperty('job'))
80
80
  throw new Error("Tried to assign sandbox when it was already assigned"); /* Should be impossible - might happen if throw during assign? */
@@ -83,11 +83,22 @@ self.wrapScriptLoading({ scriptName: 'bravojs-env', ringTransition: true }, func
83
83
  message.job.requirePath.map(p => require.paths.push(p));
84
84
  message.job.modulePath.map(p => module.paths.push(p));
85
85
  exports.arguments = message.job.arguments;
86
-
87
- if (message.job.useStrict)
88
- exports.job = indirectEval(`"use strict"; (${message.job.workFunction})`);
89
- else
90
- exports.job = indirectEval(`(${message.job.workFunction})`);
86
+ exports.worktime = message.job.worktime;
87
+
88
+ switch (message.job.worktime.name)
89
+ {
90
+ case 'map-basic':
91
+ if (message.job.useStrict)
92
+ exports.job = indirectEval(`"use strict"; (${message.job.workFunction})`);
93
+ else
94
+ exports.job = indirectEval(`(${message.job.workFunction})`);
95
+ break;
96
+ case 'pyodide':
97
+ exports.job = await generatePyodideFunction(message.job);
98
+ break;
99
+ default:
100
+ throw new Error(`Unsupported worktime: ${message.job.worktime.name}`);
101
+ }
91
102
  } catch(e) {
92
103
  reportError(e);
93
104
  return;
@@ -125,6 +136,148 @@ self.wrapScriptLoading({ scriptName: 'bravojs-env', ringTransition: true }, func
125
136
  }
126
137
  })
127
138
 
139
+ /**
140
+ * Factory function which generates a "map-basic"-like workFunction
141
+ * out of a Pyodide Worktime job (Python code, files, env variables).
142
+ *
143
+ * It takes any "images" passed in the workFunction "arguments" and
144
+ * writes them to the in memory filesystem provided by Emscripten.
145
+ * It adds any environment variables specified in the workFunction
146
+ * "arguments" to the pseudo-"process" for use.
147
+ * It globally imports a dcp module with function "set_slice_handler"
148
+ * which takes a python function as input. The python function passed
149
+ * to that slice handler is invoked by the function which this
150
+ * factory function returns.
151
+ *
152
+ * @param {Object} job The job data associated with the message
153
+ * @returns {Function} function pyodideWorkFn(slice) -> result
154
+ */
155
+ async function generatePyodideFunction(job)
156
+ {
157
+ var pythonSliceHandler;
158
+
159
+ const pyodide = await pyodideInit();
160
+ const sys = pyodide.pyimport('sys');
161
+
162
+ const findImports = pyodide.runPython('import pyodide; pyodide.code.find_imports');
163
+ const findPythonModuleLoader = pyodide.runPython('import importlib; importlib.find_loader');
164
+
165
+ const parsedArguments = parsePyodideArguments(job.arguments);
166
+
167
+ // write images to file and set environment variables
168
+ const prepPyodide = pyodide.runPython(`
169
+ import tarfile, io
170
+ import os, sys
171
+
172
+ def prepPyodide(args):
173
+ for image in args['images']:
174
+ image = bytes(image)
175
+ imageFile = io.BytesIO(image)
176
+ tar = tarfile.open(mode='r', fileobj=imageFile)
177
+ tar.extractall()
178
+
179
+ for item, value in args['environmentVariables'].items():
180
+ os.environ[item] = value
181
+
182
+ sys.argv.extend(args['sysArgv'])
183
+
184
+ return
185
+
186
+ prepPyodide`);
187
+
188
+ prepPyodide(pyodide.toPy(parsedArguments));
189
+
190
+ // register the dcp Python module
191
+ if (!sys.modules.get('dcp'))
192
+ {
193
+ const create_proxy = pyodide.runPython('import pyodide;pyodide.ffi.create_proxy');
194
+
195
+ pyodide.registerJsModule('dcp', {
196
+ set_slice_handler: function pyodide$$dcp$$setSliceHandler(func) {
197
+ pythonSliceHandler = create_proxy(func);
198
+ },
199
+ progress,
200
+ });
201
+ }
202
+ pyodide.runPython( 'import dcp' );
203
+
204
+ // attempt to import packages from the package manager (if they're not already loaded)
205
+ const workFunctionPythonImports = findImports(job.workFunction).toJs();
206
+ const packageManagerImports = workFunctionPythonImports.filter(x=>!findPythonModuleLoader(x));
207
+ if (packageManagerImports.length > 0)
208
+ {
209
+ await fetchAndInitPyodidePackages(packageManagerImports);
210
+ await pyodide.loadPackage(packageManagerImports);
211
+ }
212
+
213
+ return workFunctionWrapper;
214
+
215
+ /**
216
+ * Evaluates the Python WorkFunction string and then executes the slice
217
+ * handler Python function. If no call to `dcp.set_slice_handler` is passed
218
+ * or a non function is passed to it.
219
+ * This function specifically only takes one parameter since Pyodide Slice
220
+ * Handlers only accept one parameter.
221
+ */
222
+ async function workFunctionWrapper(datum)
223
+ {
224
+ const pyodide = await pyodideInit(); // returns the same promise when called multiple times
225
+
226
+ // load and execute the Python Workfunction, this populates the pythonSliceHandler variable
227
+ await pyodide.runPython(job.workFunction);
228
+
229
+ // failure to specify a slice handler is considered an error
230
+ if (!pythonSliceHandler)
231
+ throw new Error('ENOSLICEHANDLER: Must specify the slice handler using `dcp.set_slice_handler(fn)`');
232
+
233
+ // setting the slice handler to a non function or lambda is not supported
234
+ else if (typeof pythonSliceHandler !== 'function')
235
+ throw new Error('ENOSLICEHANDLER: Slice Handler must be a function');
236
+
237
+ const sliceHandlerResult = await pythonSliceHandler(datum);
238
+
239
+ // if it is a PyProxy, convert its value to JavaScript
240
+ if (sliceHandlerResult.toJs)
241
+ return sliceHandlerResult.toJs();
242
+
243
+ return sliceHandlerResult;
244
+ }
245
+
246
+ /*
247
+ * Refer to the "The Pyodide Worktime"."Work Function (JS)"."Arguments"."Commands"
248
+ * part of the DCP Worktimes spec.
249
+ */
250
+ function parsePyodideArguments(args)
251
+ {
252
+ var index = 1;
253
+ const numArgs = args[0];
254
+ const images = [];
255
+ const environmentVariables = {};
256
+ const sysArgv = args.slice(numArgs);
257
+
258
+ while (index < numArgs)
259
+ {
260
+ switch (args[index])
261
+ {
262
+ case 'gzImage':
263
+ const image = args[index+1];
264
+ images.push(image);
265
+ index+=2;
266
+ break;
267
+ case 'env':
268
+ const env = args[index+1].split(/=(.*)/s);
269
+ index+=2;
270
+ environmentVariables[env[0]] = env[1];
271
+ break;
272
+ default:
273
+ throw new Error(`Invalid argument ${args[index]}`);
274
+ }
275
+ }
276
+
277
+ return { sysArgv, images, environmentVariables };
278
+ }
279
+ }
280
+
128
281
  /** A module.declare suitable for running when processing modules arriving as part
129
282
  * of a module group or other in-memory cache.
130
283
  */
@@ -48,9 +48,7 @@ self.wrapScriptLoading({ scriptName: 'calculate-capabilities' }, function calcul
48
48
  return arguments[0];
49
49
  });`
50
50
 
51
- webgpu =
52
- (typeof navigator !== 'undefined' &&
53
- typeof navigator.gpu !== 'undefined');
51
+ webgpu = Boolean(globalThis.navigator?.gpu);
54
52
 
55
53
  if (webgpu) {
56
54
  try {
@@ -135,9 +133,11 @@ self.wrapScriptLoading({ scriptName: 'calculate-capabilities' }, function calcul
135
133
  addEventListener('message', async (event) => {
136
134
  try {
137
135
  if (event.request === 'describe') {
136
+ const worktimes = globalThis.worktimes;
138
137
  const capabilities = await getCapabilities();
139
138
  ring2PostMessage({
140
139
  capabilities,
140
+ worktimes,
141
141
  request: 'describe',
142
142
  });
143
143
  }