sandboxedjs 0.1.1 → 0.1.2

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/README.md CHANGED
@@ -591,9 +591,11 @@ a tested claim.
591
591
 
592
592
  Honest list of what does not work:
593
593
 
594
- - **Vite's dev server** starts but fails resolving its own package metadata, because Nodepod's
595
- ESM layer does not give bundled chunks a correct `import.meta.url`. Express, Koa, Fastify-style
596
- apps and plain `http` servers work. See `examples/react-app` for a React setup that runs.
594
+ - **Vite's dev server** loads and reads its config, then stops when esbuild starts: Nodepod
595
+ initialises esbuild by importing it from a CDN over `https:`, which the Node ESM loader
596
+ refuses. Anything that needs esbuild Vite, and tools built on it is therefore unavailable
597
+ under Node. Express, Koa, Fastify-style apps and plain `http` servers work. See
598
+ `examples/react-app` for a React setup that runs.
597
599
  - **Python is MicroPython**, so C extensions (`numpy`, `pandas`, `cryptography`) are unavailable.
598
600
  - **No real sockets.** HTTP servers work through the request proxy; raw TCP/UDP does not.
599
601
  - **No real processes.** Processes are cooperative async tasks: `kill -9` cannot interrupt a
package/dist/index.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var headless = require('@scelar/nodepod/headless');
6
+ var acorn = require('acorn');
6
7
 
7
8
  var __defProp = Object.defineProperty;
8
9
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -17951,6 +17952,226 @@ async function readZip(data) {
17951
17952
  function pythonCommands() {
17952
17953
  return [python, pip];
17953
17954
  }
17955
+ var DECLARES_EXPORTS = /(?:^|[\s;{)])(?:var|let|const|function|class)\s+exports\b/;
17956
+ var NAME = "exports";
17957
+ function renameShadowedExports(source) {
17958
+ if (!DECLARES_EXPORTS.test(source)) return null;
17959
+ let ast;
17960
+ try {
17961
+ ast = acorn.parse(source, {
17962
+ ecmaVersion: "latest",
17963
+ sourceType: "module",
17964
+ allowAwaitOutsideFunction: true,
17965
+ allowHashBang: true
17966
+ });
17967
+ } catch {
17968
+ return null;
17969
+ }
17970
+ const body = ast.body;
17971
+ const isModule = body.some(
17972
+ (node2) => node2.type.startsWith("Import") || node2.type.startsWith("Export")
17973
+ );
17974
+ if (!isModule) return null;
17975
+ const programScope = { bindsExports: hoistedNames(body).has(NAME), parent: null };
17976
+ if (!programScope.bindsExports) return null;
17977
+ const targets = [];
17978
+ let bail = false;
17979
+ visit(ast, programScope);
17980
+ if (bail || targets.length === 0) return null;
17981
+ const replacement = freshName(source);
17982
+ let out = source;
17983
+ for (const target of [...targets].sort((a, b) => b.start - a.start)) {
17984
+ const text = target.shorthand ? `${NAME}: ${replacement}` : replacement;
17985
+ out = out.slice(0, target.start) + text + out.slice(target.end);
17986
+ }
17987
+ return out;
17988
+ function visit(node2, scope) {
17989
+ if (bail) return;
17990
+ let childScope = scope;
17991
+ let skip = NOTHING;
17992
+ switch (node2.type) {
17993
+ case "FunctionDeclaration":
17994
+ case "FunctionExpression":
17995
+ case "ArrowFunctionExpression": {
17996
+ const names = /* @__PURE__ */ new Set();
17997
+ for (const param of node2.params ?? []) collectPattern(param, names);
17998
+ if (isExports(node2.id) && node2.type === "FunctionExpression") names.add(NAME);
17999
+ const fnBody = node2.body;
18000
+ if (fnBody?.type === "BlockStatement") {
18001
+ for (const name of hoistedNames(fnBody.body)) names.add(name);
18002
+ }
18003
+ childScope = { bindsExports: names.has(NAME), parent: scope };
18004
+ break;
18005
+ }
18006
+ case "CatchClause": {
18007
+ const names = /* @__PURE__ */ new Set();
18008
+ if (node2.param) collectPattern(node2.param, names);
18009
+ childScope = { bindsExports: names.has(NAME), parent: scope };
18010
+ break;
18011
+ }
18012
+ case "ClassDeclaration":
18013
+ case "ClassExpression":
18014
+ if (isExports(node2.id) && node2.type === "ClassExpression") {
18015
+ childScope = { bindsExports: true, parent: scope };
18016
+ }
18017
+ break;
18018
+ case "BlockStatement":
18019
+ case "StaticBlock":
18020
+ if (node2 !== ast.body) {
18021
+ childScope = {
18022
+ bindsExports: blockNames(node2.body).has(NAME),
18023
+ parent: scope
18024
+ };
18025
+ }
18026
+ break;
18027
+ case "ForStatement":
18028
+ case "ForInStatement":
18029
+ case "ForOfStatement": {
18030
+ const head2 = node2.init ?? node2.left;
18031
+ if (head2?.type === "VariableDeclaration" && head2.kind !== "var") {
18032
+ const names = /* @__PURE__ */ new Set();
18033
+ for (const declarator of head2.declarations) {
18034
+ collectPattern(declarator.id, names);
18035
+ }
18036
+ childScope = { bindsExports: names.has(NAME), parent: scope };
18037
+ }
18038
+ break;
18039
+ }
18040
+ /* `export { exports }` would have to become `exports as exports`, and
18041
+ * `import { exports as x }` names someone else's binding. Neither is
18042
+ * worth handling; refuse rather than rewrite them wrongly. */
18043
+ case "ExportSpecifier":
18044
+ case "ImportSpecifier":
18045
+ if (isExports(node2.local) || isExports(node2.exported) || isExports(node2.imported)) {
18046
+ bail = true;
18047
+ }
18048
+ return;
18049
+ case "Identifier":
18050
+ if (node2.name === NAME && resolvesToProgram(scope)) {
18051
+ targets.push({ start: node2.start, end: node2.end, shorthand: false });
18052
+ }
18053
+ return;
18054
+ // Property positions are names, not references to the binding.
18055
+ case "MemberExpression":
18056
+ case "MethodDefinition":
18057
+ case "PropertyDefinition":
18058
+ skip = node2.computed ? NOTHING : PROPERTY;
18059
+ break;
18060
+ case "Property":
18061
+ if (node2.computed) break;
18062
+ if (node2.shorthand) {
18063
+ const value = node2.value;
18064
+ if (isExports(value) && resolvesToProgram(scope)) {
18065
+ targets.push({ start: value.start, end: value.end, shorthand: true });
18066
+ }
18067
+ return;
18068
+ }
18069
+ skip = PROPERTY;
18070
+ break;
18071
+ case "LabeledStatement":
18072
+ case "BreakStatement":
18073
+ case "ContinueStatement":
18074
+ skip = LABEL;
18075
+ break;
18076
+ }
18077
+ for (const [key, value] of Object.entries(node2)) {
18078
+ if (key === "type" || key === "start" || key === "end" || skip.includes(key)) continue;
18079
+ if (Array.isArray(value)) {
18080
+ for (const item of value) if (isNode2(item)) visit(item, childScope);
18081
+ } else if (isNode2(value)) {
18082
+ visit(value, childScope);
18083
+ }
18084
+ }
18085
+ }
18086
+ }
18087
+ function resolvesToProgram(scope) {
18088
+ for (let current = scope; current; current = current.parent) {
18089
+ if (current.bindsExports) return current.parent === null;
18090
+ }
18091
+ return false;
18092
+ }
18093
+ function hoistedNames(body) {
18094
+ const names = blockNames(body);
18095
+ collectVars(body, names);
18096
+ return names;
18097
+ }
18098
+ function blockNames(body) {
18099
+ const names = /* @__PURE__ */ new Set();
18100
+ for (const node2 of body ?? []) {
18101
+ if (node2.type === "VariableDeclaration" && node2.kind !== "var") {
18102
+ for (const declarator of node2.declarations) {
18103
+ collectPattern(declarator.id, names);
18104
+ }
18105
+ } else if (node2.type === "ClassDeclaration" && isNode2(node2.id)) {
18106
+ names.add(node2.id.name);
18107
+ } else if (node2.type === "FunctionDeclaration" && isNode2(node2.id)) {
18108
+ names.add(node2.id.name);
18109
+ }
18110
+ }
18111
+ return names;
18112
+ }
18113
+ function collectVars(nodes, names) {
18114
+ if (Array.isArray(nodes)) {
18115
+ for (const item of nodes) collectVars(item, names);
18116
+ return;
18117
+ }
18118
+ if (!isNode2(nodes)) return;
18119
+ const node2 = nodes;
18120
+ if (node2.type === "FunctionDeclaration" || node2.type === "FunctionExpression" || node2.type === "ArrowFunctionExpression") {
18121
+ if (isNode2(node2.id)) names.add(node2.id.name);
18122
+ return;
18123
+ }
18124
+ if (node2.type === "VariableDeclaration" && node2.kind === "var") {
18125
+ for (const declarator of node2.declarations) {
18126
+ collectPattern(declarator.id, names);
18127
+ }
18128
+ }
18129
+ for (const [key, value] of Object.entries(node2)) {
18130
+ if (key === "type" || key === "start" || key === "end") continue;
18131
+ collectVars(value, names);
18132
+ }
18133
+ }
18134
+ function collectPattern(node2, names) {
18135
+ if (!isNode2(node2)) return;
18136
+ switch (node2.type) {
18137
+ case "Identifier":
18138
+ names.add(node2.name);
18139
+ return;
18140
+ case "ObjectPattern":
18141
+ for (const property of node2.properties) {
18142
+ collectPattern(property.value ?? property.argument, names);
18143
+ }
18144
+ return;
18145
+ case "ArrayPattern":
18146
+ for (const element of node2.elements) collectPattern(element, names);
18147
+ return;
18148
+ case "AssignmentPattern":
18149
+ collectPattern(node2.left, names);
18150
+ return;
18151
+ case "RestElement":
18152
+ collectPattern(node2.argument, names);
18153
+ return;
18154
+ default:
18155
+ return;
18156
+ }
18157
+ }
18158
+ function isExports(value) {
18159
+ return isNode2(value) && value.type === "Identifier" && value.name === NAME;
18160
+ }
18161
+ function freshName(source) {
18162
+ let name = "__sandboxedjs_exports";
18163
+ let suffix = 0;
18164
+ while (source.includes(name)) name = `__sandboxedjs_exports${++suffix}`;
18165
+ return name;
18166
+ }
18167
+ function isNode2(value) {
18168
+ return typeof value === "object" && value !== null && typeof value.type === "string";
18169
+ }
18170
+ var NOTHING = [];
18171
+ var PROPERTY = ["property", "key"];
18172
+ var LABEL = ["label"];
18173
+
18174
+ // src/pkg/index.ts
17954
18175
  init_path();
17955
18176
  var NPM_VERSION = "10.9.0";
17956
18177
  function readManifest(ctx, dir3) {
@@ -18012,6 +18233,7 @@ async function installPackages(ctx, specs, opts) {
18012
18233
  }
18013
18234
  }
18014
18235
  normalizeBinDirectories(ctx, opts.cwd);
18236
+ normalizeEsmExports(ctx, opts.cwd);
18015
18237
  return 0;
18016
18238
  } catch (e) {
18017
18239
  ctx.warn(`npm error ${e instanceof Error ? e.message : String(e)}`);
@@ -18235,6 +18457,20 @@ function normalizeBinDirectories(ctx, root) {
18235
18457
  if (basename(path) === ".bin" && path !== join(modules, ".bin")) visit(path);
18236
18458
  }
18237
18459
  }
18460
+ function normalizeEsmExports(ctx, root) {
18461
+ const modules = join(root, "node_modules");
18462
+ if (!ctx.vfs.lexists(modules)) return;
18463
+ for (const path of ctx.vfs.walk(modules, { cred: ctx.cred })) {
18464
+ if (!/\.(?:js|mjs)$/.test(path)) continue;
18465
+ try {
18466
+ if (!ctx.vfs.lstat(path).isFile()) continue;
18467
+ const source = ctx.vfs.readText(path, ctx.cred);
18468
+ const repaired = renameShadowedExports(source);
18469
+ if (repaired !== null) ctx.vfs.writeFile(path, repaired, { privileged: true });
18470
+ } catch {
18471
+ }
18472
+ }
18473
+ }
18238
18474
  function printNpmHelp(ctx) {
18239
18475
  ctx.line(`npm <command>`);
18240
18476
  ctx.line("");