@trops/dash-core 0.1.483 → 0.1.485

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.
@@ -11,10 +11,11 @@ var require$$3$1 = require('xml2js');
11
11
  var require$$4 = require('JSONStream');
12
12
  var require$$5 = require('stream');
13
13
  var require$$6 = require('csv-parser');
14
+ var require$$0$3 = require('quickjs-emscripten');
14
15
  var require$$7 = require('https');
15
- var require$$0$4 = require('@modelcontextprotocol/sdk/client/index.js');
16
+ var require$$0$5 = require('@modelcontextprotocol/sdk/client/index.js');
16
17
  var require$$1$4 = require('@modelcontextprotocol/sdk/client/stdio.js');
17
- var require$$0$3 = require('pkce-challenge');
18
+ var require$$0$4 = require('pkce-challenge');
18
19
  var require$$2$1 = require('os');
19
20
  var require$$7$1 = require('child_process');
20
21
  var require$$3$2 = require('adm-zip');
@@ -23,17 +24,17 @@ var require$$2$2 = require('vm');
23
24
  var require$$1$5 = require('croner');
24
25
  var require$$2$3 = require('algoliasearch');
25
26
  var require$$3$3 = require('node:path');
26
- var require$$0$5 = require('openai');
27
+ var require$$0$6 = require('openai');
27
28
  require('live-plugin-manager');
28
- var require$$0$8 = require('@anthropic-ai/sdk');
29
+ var require$$0$9 = require('@anthropic-ai/sdk');
29
30
  var require$$3$4 = require('crypto');
30
31
  var require$$8$1 = require('zod');
31
- var require$$0$6 = require('http');
32
+ var require$$0$7 = require('http');
32
33
  var require$$1$6 = require('http2');
33
34
  var require$$2$4 = require('node-forge');
34
- var require$$0$7 = require('css');
35
+ var require$$0$8 = require('css');
35
36
  var require$$1$7 = require('node-vibrant/node');
36
- var require$$0$9 = require('ws');
37
+ var require$$0$a = require('ws');
37
38
 
38
39
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
39
40
 
@@ -1649,6 +1650,235 @@ const themeController$5 = {
1649
1650
 
1650
1651
  var themeController_1 = themeController$5;
1651
1652
 
1653
+ /**
1654
+ * safeJsExecutor.js
1655
+ *
1656
+ * Run renderer-supplied JavaScript bodies in a QuickJS WASM sandbox.
1657
+ *
1658
+ * Why: dash-core's transformFile API (and a handful of widget eval sites)
1659
+ * historically used the dynamic-function constructor to compile user-
1660
+ * authored JS in the main process. That gives any caller full Node.js
1661
+ * privileges — filesystem, network, child_process — turning a single
1662
+ * dataApi call into an RCE primitive. See
1663
+ * docs/security/ipc-filesystem-audit.md.
1664
+ *
1665
+ * QuickJS-emscripten runs JS in a separate WASM sandbox with no host
1666
+ * globals (no `require`, no `process`, no `fetch`). Code executes
1667
+ * against a tiny built-in surface: `Math`, `JSON`, `Date`, `Array`,
1668
+ * primitives. The WASM boundary is the OS's responsibility, so escapes
1669
+ * are not a keep-up-with-attackers problem the way `vm.runInNewContext`
1670
+ * is.
1671
+ *
1672
+ * Public API:
1673
+ *
1674
+ * await runOnce({ body, args, inputs, timeoutMs, memoryBytes }) — one
1675
+ * compile + run, returns `{ value }` or `{ error }`. Cheap to call
1676
+ * repeatedly but creates a fresh context each time.
1677
+ *
1678
+ * await createCompiled({ body, args, memoryBytes }) — compile once
1679
+ * and return `{ run(inputs, timeoutMs), dispose() }` for streaming
1680
+ * workloads (e.g., transformFile maps over many records).
1681
+ *
1682
+ * Implementation notes:
1683
+ *
1684
+ * - The QuickJS WASM module loads once per process (cached).
1685
+ * - Each context gets its own runtime → independent memory limit and
1686
+ * interrupt handler, no cross-talk between concurrent transforms.
1687
+ * - Inputs cross the boundary as JSON strings (round-trip serialized);
1688
+ * output crosses back the same way. Functions/Symbols/Maps don't
1689
+ * survive — but transform mappings produce plain JSON objects by
1690
+ * contract.
1691
+ * - The timeout uses a deadline check inside QuickJS's interrupt
1692
+ * handler, NOT setTimeout — JS event-loop callbacks can't fire
1693
+ * while QuickJS is executing synchronously.
1694
+ */
1695
+
1696
+ // quickjs-emscripten is loaded lazily inside getModule() rather than at
1697
+ // module top. Reason: when transform.js (which lives in the same dir)
1698
+ // does require("./safeJsExecutor") and rollup-plugin-commonjs sees a
1699
+ // statically-required external (`quickjs-emscripten`) inside that file,
1700
+ // it can mark the relative import as transitive-external and then fail
1701
+ // to resolve back. Deferring the require breaks the static-analysis
1702
+ // chain so the rollup build resolves cleanly.
1703
+ const DEFAULT_TIMEOUT_MS = 1000;
1704
+ const DEFAULT_MEMORY_BYTES = 32 * 1024 * 1024; // 32 MB
1705
+
1706
+ let _modulePromise = null;
1707
+ function getModule() {
1708
+ if (!_modulePromise) {
1709
+ const { getQuickJS } = require$$0$3;
1710
+ _modulePromise = getQuickJS();
1711
+ }
1712
+ return _modulePromise;
1713
+ }
1714
+
1715
+ function injectInputs(vm, args, inputs) {
1716
+ if (!Array.isArray(args) || !Array.isArray(inputs)) {
1717
+ throw new Error(
1718
+ "safeJsExecutor: args and inputs must be arrays of equal length",
1719
+ );
1720
+ }
1721
+ if (args.length !== inputs.length) {
1722
+ throw new Error("safeJsExecutor: args.length must equal inputs.length");
1723
+ }
1724
+ for (let i = 0; i < args.length; i++) {
1725
+ const name = args[i];
1726
+ if (typeof name !== "string" || !/^[A-Za-z_$][\w$]*$/.test(name)) {
1727
+ throw new Error("safeJsExecutor: arg names must be valid JS identifiers");
1728
+ }
1729
+ const json = JSON.stringify(inputs[i]);
1730
+ // Use evalCode to materialize the JSON-typed value inside the
1731
+ // VM. For undefined inputs, JSON.stringify returns undefined
1732
+ // (not a string) — fall through to evaluating the literal
1733
+ // `undefined`.
1734
+ const literal = json === undefined ? "undefined" : json;
1735
+ const result = vm.evalCode(`(${literal})`);
1736
+ if (result.error) {
1737
+ const err = vm.dump(result.error);
1738
+ result.error.dispose();
1739
+ throw new Error(
1740
+ `safeJsExecutor: failed to inject "${name}": ${
1741
+ err && err.message ? err.message : err
1742
+ }`,
1743
+ );
1744
+ }
1745
+ vm.setProp(vm.global, name, result.value);
1746
+ result.value.dispose();
1747
+ }
1748
+ }
1749
+
1750
+ function setDeadline(vm, timeoutMs) {
1751
+ const deadline = Date.now() + Math.max(1, timeoutMs);
1752
+ vm.runtime.setInterruptHandler(() => Date.now() > deadline);
1753
+ }
1754
+
1755
+ function buildWrappedBody(args, body) {
1756
+ // Wrap the user body in an IIFE so `return` works at the body level
1757
+ // (matching the dynamic-function-constructor semantics this is
1758
+ // replacing).
1759
+ return `(function(${args.join(",")}){${body}})(${args.join(",")})`;
1760
+ }
1761
+
1762
+ async function runOnce({
1763
+ body,
1764
+ args = [],
1765
+ inputs = [],
1766
+ timeoutMs = DEFAULT_TIMEOUT_MS,
1767
+ memoryBytes = DEFAULT_MEMORY_BYTES,
1768
+ }) {
1769
+ if (typeof body !== "string" || !body.trim()) {
1770
+ return { error: "body must be a non-empty string" };
1771
+ }
1772
+ const QuickJS = await getModule();
1773
+ const vm = QuickJS.newContext();
1774
+ try {
1775
+ vm.runtime.setMemoryLimit(memoryBytes);
1776
+ vm.runtime.setMaxStackSize(1024 * 1024);
1777
+ setDeadline(vm, timeoutMs);
1778
+
1779
+ injectInputs(vm, args, inputs);
1780
+ const wrapped = buildWrappedBody(args, body);
1781
+ const result = vm.evalCode(wrapped);
1782
+
1783
+ if (result.error) {
1784
+ const err = vm.dump(result.error);
1785
+ result.error.dispose();
1786
+ return {
1787
+ error:
1788
+ err && err.message
1789
+ ? String(err.message)
1790
+ : typeof err === "string"
1791
+ ? err
1792
+ : JSON.stringify(err),
1793
+ };
1794
+ }
1795
+ const value = vm.dump(result.value);
1796
+ result.value.dispose();
1797
+ return { value };
1798
+ } catch (e) {
1799
+ return { error: e && e.message ? e.message : String(e) };
1800
+ } finally {
1801
+ vm.dispose();
1802
+ }
1803
+ }
1804
+
1805
+ async function createCompiled({
1806
+ body,
1807
+ args = [],
1808
+ memoryBytes = DEFAULT_MEMORY_BYTES,
1809
+ }) {
1810
+ if (typeof body !== "string" || !body.trim()) {
1811
+ throw new Error("body must be a non-empty string");
1812
+ }
1813
+ const QuickJS = await getModule();
1814
+ const vm = QuickJS.newContext();
1815
+ vm.runtime.setMemoryLimit(memoryBytes);
1816
+ vm.runtime.setMaxStackSize(1024 * 1024);
1817
+
1818
+ // Define the user function once on the VM globals so subsequent
1819
+ // run() calls can invoke it with fresh args without re-parsing.
1820
+ const define = vm.evalCode(
1821
+ `globalThis.__userFn = function(${args.join(",")}){${body}};`,
1822
+ );
1823
+ if (define.error) {
1824
+ const err = vm.dump(define.error);
1825
+ define.error.dispose();
1826
+ vm.dispose();
1827
+ throw new Error(
1828
+ "safeJsExecutor: compile failed: " +
1829
+ (err && err.message ? err.message : JSON.stringify(err)),
1830
+ );
1831
+ }
1832
+ define.value.dispose();
1833
+
1834
+ let disposed = false;
1835
+
1836
+ return {
1837
+ run(inputs, timeoutMs = DEFAULT_TIMEOUT_MS) {
1838
+ if (disposed) {
1839
+ return { error: "executor already disposed" };
1840
+ }
1841
+ try {
1842
+ setDeadline(vm, timeoutMs);
1843
+ const argList = inputs
1844
+ .map((v) => (v === undefined ? "undefined" : JSON.stringify(v)))
1845
+ .join(",");
1846
+ const result = vm.evalCode(`__userFn(${argList})`);
1847
+ if (result.error) {
1848
+ const err = vm.dump(result.error);
1849
+ result.error.dispose();
1850
+ return {
1851
+ error:
1852
+ err && err.message
1853
+ ? String(err.message)
1854
+ : typeof err === "string"
1855
+ ? err
1856
+ : JSON.stringify(err),
1857
+ };
1858
+ }
1859
+ const value = vm.dump(result.value);
1860
+ result.value.dispose();
1861
+ return { value };
1862
+ } catch (e) {
1863
+ return { error: e && e.message ? e.message : String(e) };
1864
+ }
1865
+ },
1866
+ dispose() {
1867
+ if (!disposed) {
1868
+ disposed = true;
1869
+ vm.dispose();
1870
+ }
1871
+ },
1872
+ };
1873
+ }
1874
+
1875
+ var safeJsExecutor$1 = {
1876
+ runOnce,
1877
+ createCompiled,
1878
+ DEFAULT_TIMEOUT_MS,
1879
+ DEFAULT_MEMORY_BYTES,
1880
+ };
1881
+
1652
1882
  /**
1653
1883
  * Utils/tranaform
1654
1884
  * Main gial is to transform a file of data into another form (CSV -> Json for example)
@@ -1667,9 +1897,15 @@ var csv = require$$6;
1667
1897
  const path$g = require$$1$2;
1668
1898
  const { app: app$9 } = require$$0$1;
1669
1899
  const { ensureDirectoryExistence: ensureDirectoryExistence$1 } = file;
1900
+ const safeJsExecutor = safeJsExecutor$1;
1670
1901
 
1671
1902
  const TRANSFORM_APP_NAME = "Dashboard";
1672
1903
  const MAX_MAPPING_BODY_SIZE = 10240; // 10KB limit for mapping function body
1904
+ // Per-record execution limit. Each mapping function call inside the
1905
+ // streaming transform must complete within this budget; a runaway loop
1906
+ // in the user's mapping triggers an "interrupted" error and the record
1907
+ // is skipped rather than blocking the whole stream.
1908
+ const PER_RECORD_TIMEOUT_MS = 1000;
1673
1909
 
1674
1910
  /**
1675
1911
  * XtreamerClientTransform
@@ -1975,45 +2211,78 @@ let Transform$1 = class Transform {
1975
2211
  // JSON parser
1976
2212
  var parser = JSONStream$1.parse("*");
1977
2213
 
1978
- if (fs$b.existsSync(resolvedFilepath)) {
1979
- console.log("file exists ", resolvedFilepath);
1980
- // create the readStream to parse the large file (json)
1981
- var readStream = fs$b.createReadStream(resolvedFilepath).pipe(parser);
1982
-
1983
- ensureDirectoryExistence$1(resolvedOutFilepath);
1984
-
1985
- var writeStream = fs$b.createWriteStream(resolvedOutFilepath);
1986
-
1987
- let sep = "";
1988
- let count = 0;
1989
-
1990
- // create our mapping function
1991
- const fn = new Function(args, mappingFunctionBody);
2214
+ if (!fs$b.existsSync(resolvedFilepath)) {
2215
+ return reject(new Error("File doesnt exist"));
2216
+ }
2217
+ console.log("file exists ", resolvedFilepath);
2218
+ // create the readStream to parse the large file (json)
2219
+ var readStream = fs$b.createReadStream(resolvedFilepath).pipe(parser);
2220
+
2221
+ ensureDirectoryExistence$1(resolvedOutFilepath);
2222
+
2223
+ var writeStream = fs$b.createWriteStream(resolvedOutFilepath);
2224
+
2225
+ let sep = "";
2226
+ let count = 0;
2227
+
2228
+ // Compile the user-supplied mapping function inside a QuickJS
2229
+ // sandbox. The previous implementation compiled the body with
2230
+ // full Node.js privileges (filesystem, network, process). The
2231
+ // sandbox gives the body a tiny pure-JS surface (Math, JSON,
2232
+ // Date, primitives) and no host globals — see
2233
+ // electron/utils/safeJsExecutor.js for full rationale.
2234
+ //
2235
+ // createCompiled is async (the WASM module must be loaded). Use
2236
+ // .then/.catch instead of await because we're inside a sync
2237
+ // Promise executor.
2238
+ safeJsExecutor
2239
+ .createCompiled({
2240
+ body: mappingFunctionBody,
2241
+ args,
2242
+ })
2243
+ .then((executor) => {
2244
+ startStreamingWithExecutor(executor);
2245
+ })
2246
+ .catch((e) => {
2247
+ reject(
2248
+ new Error(
2249
+ "mappingFunctionBody failed to compile: " +
2250
+ (e && e.message ? e.message : String(e)),
2251
+ ),
2252
+ );
2253
+ });
1992
2254
 
2255
+ function startStreamingWithExecutor(executor) {
1993
2256
  // begin the write stream
1994
2257
  writeStream.write("[\n");
1995
2258
 
1996
2259
  readStream.on("data", (data) => {
1997
2260
  try {
1998
- //console.log("in stream", count, data);
1999
- // data in this case is the JSON object...
2000
2261
  if (count > 0) {
2001
2262
  sep = ",\n";
2002
2263
  }
2003
2264
 
2004
2265
  if (data) {
2005
- // transform the data here...
2006
- const newValue = fn(data, count);
2266
+ const result = executor.run([data, count], PER_RECORD_TIMEOUT_MS);
2267
+ if (result.error) {
2268
+ // Don't block the stream on a single bad record — log,
2269
+ // skip, continue. Matches the previous try/catch in the
2270
+ // unsandboxed implementation.
2271
+ console.log(
2272
+ "[transform] mapping error on record " +
2273
+ count +
2274
+ ": " +
2275
+ result.error,
2276
+ );
2277
+ return;
2278
+ }
2007
2279
 
2008
- writeStream.write(sep + JSON.stringify(newValue));
2280
+ writeStream.write(sep + JSON.stringify(result.value));
2009
2281
 
2010
2282
  if (callbackEvent !== null && win !== null) {
2011
- win.webContents.send(callbackEvent, {
2012
- count,
2013
- });
2283
+ win.webContents.send(callbackEvent, { count });
2014
2284
  }
2015
2285
 
2016
- // increment the counter
2017
2286
  count++;
2018
2287
  }
2019
2288
  } catch (e) {
@@ -2021,19 +2290,19 @@ let Transform$1 = class Transform {
2021
2290
  }
2022
2291
  });
2023
2292
 
2024
- readStream.on("end", (data) => {
2293
+ readStream.on("end", () => {
2025
2294
  writeStream.write("\n]");
2026
2295
  writeStream.close();
2296
+ executor.dispose();
2027
2297
  resolve("Complete: wrote " + count + " objects");
2028
2298
  });
2029
2299
 
2030
2300
  readStream.on("error", (err) => {
2031
2301
  console.log("read stream error transform ", err.message);
2302
+ executor.dispose();
2032
2303
  reject(err);
2033
2304
  });
2034
- } else {
2035
- reject(new Error("File doesnt exist"));
2036
- }
2305
+ } // end startStreamingWithExecutor
2037
2306
  });
2038
2307
  };
2039
2308
 
@@ -19134,7 +19403,7 @@ auth$2.exchangeAuthorization = exchangeAuthorization;
19134
19403
  auth$2.refreshAuthorization = refreshAuthorization;
19135
19404
  auth$2.fetchToken = fetchToken;
19136
19405
  auth$2.registerClient = registerClient;
19137
- const pkce_challenge_1 = __importDefault$2(require$$0$3);
19406
+ const pkce_challenge_1 = __importDefault$2(require$$0$4);
19138
19407
  const types_js_1$5 = types$2;
19139
19408
  const auth_js_1$1 = auth$1;
19140
19409
  const auth_js_2 = auth$1;
@@ -20663,7 +20932,7 @@ streamableHttp$1.StreamableHTTPClientTransport = StreamableHTTPClientTransport$1
20663
20932
  * Uses @modelcontextprotocol/sdk for protocol handling.
20664
20933
  */
20665
20934
 
20666
- const { Client } = require$$0$4;
20935
+ const { Client } = require$$0$5;
20667
20936
  const {
20668
20937
  StdioClientTransport,
20669
20938
  } = require$$1$4;
@@ -25941,7 +26210,7 @@ const algoliaController$1 = {
25941
26210
 
25942
26211
  var algoliaController_1 = algoliaController$1;
25943
26212
 
25944
- const OpenAI = require$$0$5;
26213
+ const OpenAI = require$$0$6;
25945
26214
  const events$2 = events$8;
25946
26215
 
25947
26216
  const openaiController$1 = {
@@ -44320,7 +44589,7 @@ __export(src_exports, {
44320
44589
  var dist = __toCommonJS(src_exports);
44321
44590
 
44322
44591
  // src/server.ts
44323
- var import_node_http = require$$0$6;
44592
+ var import_node_http = require$$0$7;
44324
44593
 
44325
44594
  // src/listener.ts
44326
44595
  var import_node_http22 = require$$1$6;
@@ -47905,10 +48174,10 @@ var themeFromUrlErrors$1 = {
47905
48174
  * computed styles, and favicon/logo images (via node-vibrant).
47906
48175
  */
47907
48176
 
47908
- const css = require$$0$7;
48177
+ const css = require$$0$8;
47909
48178
  const { Vibrant } = require$$1$7;
47910
48179
  const https = require$$7;
47911
- const http = require$$0$6;
48180
+ const http = require$$0$7;
47912
48181
  const { URL: URL$1 } = require$$4$1;
47913
48182
  const {
47914
48183
  UrlUnreachableError,
@@ -52468,7 +52737,7 @@ var toolHandlers$1 = {
52468
52737
  * per-request, receiving the full messages array each time.
52469
52738
  */
52470
52739
 
52471
- const Anthropic = require$$0$8;
52740
+ const Anthropic = require$$0$9;
52472
52741
  const mcpController$2 = mcpControllerExports;
52473
52742
  const cliController$1 = cliController_1;
52474
52743
  const toolDefinitions = toolDefinitions$1;
@@ -57886,7 +58155,7 @@ var notificationController_1 = notificationController$2;
57886
58155
  * Multiple widgets referencing the same provider share a single socket.
57887
58156
  */
57888
58157
 
57889
- const WebSocket = require$$0$9;
58158
+ const WebSocket = require$$0$a;
57890
58159
 
57891
58160
  /**
57892
58161
  * Active WebSocket connections
@@ -58710,7 +58979,7 @@ clientCache$1.registerFactory("algolia", (credentials) => {
58710
58979
 
58711
58980
  // --- OpenAI ---
58712
58981
  clientCache$1.registerFactory("openai", (credentials) => {
58713
- const OpenAI = require$$0$5;
58982
+ const OpenAI = require$$0$6;
58714
58983
  return new OpenAI({ apiKey: credentials.apiKey });
58715
58984
  });
58716
58985