@poe-platform/safe-js 0.1.799 → 0.1.801

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.
Files changed (46) hide show
  1. package/README.md +4 -2
  2. package/dist/safe-js/chunks/{chunk-KULEMOAE.js → chunk-4KMUWNZD.js} +5 -156
  3. package/dist/safe-js/chunks/{chunk-ULPZIHB5.js → chunk-5RLD4TVI.js} +174 -127
  4. package/dist/safe-js/chunks/{chunk-MCFRB5X3.js → chunk-6H5VC5W5.js} +84 -14
  5. package/dist/safe-js/chunks/{chunk-TBZILU4U.js → chunk-7THYP46K.js} +1 -1
  6. package/dist/safe-js/chunks/{chunk-UU4T2KIT.js → chunk-CK3YK2EC.js} +1 -1
  7. package/dist/safe-js/chunks/{chunk-3O7HA6WT.js → chunk-F6Y734YA.js} +2 -2
  8. package/dist/safe-js/chunks/{chunk-M6S3F3WM.js → chunk-HFRBLE6Y.js} +42 -36
  9. package/dist/safe-js/chunks/{chunk-OPYN6N5M.js → chunk-HPHCQNFV.js} +154 -1
  10. package/dist/safe-js/chunks/{chunk-W6C3Q6U4.js → chunk-IB6WAQAE.js} +2 -2
  11. package/dist/safe-js/chunks/{chunk-ZKLJW27N.js → chunk-PMOXSLM5.js} +1 -1
  12. package/dist/safe-js/chunks/chunk-QRWRR6IA.js +78 -0
  13. package/dist/safe-js/chunks/{chunk-MDJ7MJYI.js → chunk-XOMGWE37.js} +2 -2
  14. package/dist/safe-js/chunks/{chunk-QTCPJ7FN.js → chunk-XV5OYKND.js} +1 -1
  15. package/dist/safe-js/chunks/{chunk-OTVZWRYE.js → chunk-YCFOANSU.js} +23 -18
  16. package/dist/safe-js/chunks/{cli-runtime-DYXJZCGV.js → cli-runtime-L4IABN2W.js} +15 -15
  17. package/dist/safe-js/chunks/{dump-XDJEF2LG.js → dump-KUPBSXSL.js} +2 -2
  18. package/dist/safe-js/chunks/{harness-32OOFC5A.js → harness-3DDUS3RY.js} +2 -2
  19. package/dist/safe-js/chunks/{mcp-PTCA53MN.js → mcp-5TMFK3UE.js} +3 -3
  20. package/dist/safe-js/chunks/{migration-file-EEGIOHQB.js → migration-file-4CYSJ6LO.js} +3 -3
  21. package/dist/safe-js/chunks/{restore-U3CCH36B.js → restore-GDHBT5ZD.js} +3 -3
  22. package/dist/safe-js/chunks/{run-Q74MU3QQ.js → run-M6N5KCPF.js} +5 -5
  23. package/dist/safe-js/chunks/{runtime-modules-SL5D6LAJ.js → runtime-modules-MGSXY4BN.js} +2 -2
  24. package/dist/safe-js/cli.js +2 -2
  25. package/dist/safe-js/core.d.ts +6 -0
  26. package/dist/safe-js/core.js +33 -7
  27. package/dist/safe-js/index.js +24 -93
  28. package/dist/safe-js/interp/data-copy.d.ts +3 -2
  29. package/dist/safe-js/interp/host-bridge.d.ts +5 -4
  30. package/dist/safe-js/modules/fs-config.d.ts +1 -0
  31. package/dist/safe-js/modules/fs.d.ts +2 -0
  32. package/dist/safe-js/modules/fs.js +2 -2
  33. package/dist/safe-js/node-unavailable.d.ts +1 -0
  34. package/dist/safe-js/portable/chunks/chunk-2FNMGWDK.js +58703 -0
  35. package/dist/safe-js/portable/chunks/chunk-H5X3T5VP.js +909 -0
  36. package/dist/safe-js/portable/chunks/chunk-XCTU25SU.js +111 -0
  37. package/dist/safe-js/portable/chunks/source-files-CVU252FB.js +7 -0
  38. package/dist/safe-js/portable/core.js +14763 -0
  39. package/dist/safe-js/portable/modules/fs.js +9 -0
  40. package/dist/safe-js/snapshot/arguments.d.ts +2 -0
  41. package/dist/safe-js/snapshot/property-descriptors.d.ts +2 -0
  42. package/dist/safe-js/snapshot/regexp-properties.d.ts +2 -0
  43. package/dist/safe-js/snapshot/symbols.d.ts +2 -0
  44. package/dist/safe-js/workerd.d.ts +3 -0
  45. package/dist/safe-js/workerd.js +74436 -0
  46. package/package.json +30 -6
package/README.md CHANGED
@@ -8,6 +8,8 @@ built-ins. It is suitable for capability-controlled scripts and agent workflows.
8
8
  Complete ECMAScript conformance has not been established; see
9
9
  [development status](#development-status) and [meaningful limitations](#meaningful-limitations).
10
10
 
11
+ Under `workerd` and `browser` conditions, the standalone root and `/core` exports select the portable interpreter with `makeFsModule`, `makeEnvModule`, `makeTimeModule`, `makeFailModule`, `makeMetricModule`, and `makeHarnessModule`. The `/modules/fs` entry is also portable; supply a filesystem adapter for host file access.
12
+
11
13
  ## Quickstart
12
14
 
13
15
  Install the public ESM package. Its declared Node.js minimum is 18.18+, but
@@ -36,7 +38,7 @@ console.log(result.returnValue);
36
38
 
37
39
  `run()` takes source text, not a file path. Success returns `ok`, `returnValue`, `snapshot`, and `stats`. Handle both an `ok: false` result and a rejected promise: parsing, budget exhaustion, cancellation, and some execution failures can reject. Top-level `await` in this example lets rejections reach Node.
38
40
 
39
- `@poe-platform/safe-js/core` exposes `run`, `createRealm`, `defineExtension`, `lint`, `Budget`, and replayable-random helpers. The `/core`, `/workerd`, and `/modules/fs` workspace entries bundle with the `workerd` or `browser` condition without Node built-ins. Supply a `@poe-code/safe-fs` adapter for filesystem capabilities; `createRootedSourceResolver(root, adapter)` grants portable source imports. File snapshots, migrations, harnesses, and file-running helpers also accept an `adapter`. The shared filesystem lives in `@poe-platform/safe-fs`, with a portable `/core` entry. Existing `@poe-platform/safe-js/fs`, `/fs/core`, and `/fs/node` imports re-export it. Legacy `poe-code/safe-js` imports remain available through the CLI package but use a separate runtime; keep factories and errors within one import family.
41
+ `@poe-platform/safe-js/core` exposes `run`, `createRealm`, `defineExtension`, `lint`, `Budget`, and replayable-random helpers for Node.js. In Workers, the `@poe-platform/safe-js` root selects `/workerd`: it exports the portable core API plus `declareHostOperation` without requiring `nodejs_compat`, including the runtime hooks needed by Safe Bash's `nodeCommands()`. The `/core` and `/modules/fs` workspace entries also bundle with the `workerd` or `browser` condition without Node built-ins. Supply a `@poe-platform/safe-fs/core` adapter for filesystem capabilities; `createRootedSourceResolver(root, adapter)` grants portable source imports. File snapshots, migrations, harnesses, and file-running helpers also accept an `adapter`. The shared filesystem lives in `@poe-platform/safe-fs`, with a portable `/core` entry. Existing `@poe-platform/safe-js/fs`, `/fs/core`, and `/fs/node` imports re-export it. Legacy `poe-code/safe-js` imports remain available through the CLI package but use a separate runtime; keep factories and errors within one import family.
40
42
 
41
43
  ## Supported features
42
44
 
@@ -549,7 +551,7 @@ Factories return exports to register in `modules`; calling a factory alone does
549
551
 
550
552
  `spawn.retry(definition, options, retryOptions)` and `defaultRetry` use `{ maxAttempts, backoffMs, isErrorRetryable?, isRetryable? }`. `spawn.parallel(calls, options?)` accepts definition/options tuples or spawn-handle factories; options are `check`, `maxConcurrent`, `failFast`, and `signal`. Usage helpers are `createSpawnUsageAccumulator()` and `runWithSpawnUsageAccumulator(accumulator, operation)`.
551
553
 
552
- **Config files.** `parseMcpConfig(json, directory)` accepts `servers` and the three numeric MCP limits, resolving stdio paths against `directory`. `parseFsConfig(json)` accepts `{ adapter: { type, options }, root?, cwd?, readFileMaxBytes?, hostReadMemoryLimit? }`; `resolveFsConfig(config, { registry? })` constructs the adapter for `makeFsModule`. Built-in types are `memory` and `real`; `real` requires an absolute host `options.root`. Outer `root`/`cwd` are absolute virtual paths. Custom registry descriptors supply `validateOptions` and `create`. Signals and injected functions are SDK options, not JSON fields.
554
+ **Config files.** `parseMcpConfig(json, directory)` accepts `servers` and the three numeric MCP limits, resolving stdio paths against `directory`. `parseFsConfig(json)` accepts `{ adapter: { type, options }, root?, cwd?, readFileMaxBytes?, hostReadMemoryLimit? }`; `resolveFsConfig(config, { registry? })` constructs the adapter for `makeFsModule`. Built-in types are `memory` and `real`; `real` requires an absolute host `options.root`. Outer `root`/`cwd` are absolute virtual paths. Custom registry descriptors supply `validateOptions` and `create`. `maxSymlinkFollows` optionally bounds dangling-symlink canonicalization for rooted filesystem modules; it defaults to `Infinity` and accepts the same finite values through filesystem JSON configuration. Actual symlink cycles still fail. Signals and injected functions are SDK options, not JSON fields.
553
555
 
554
556
  </details>
555
557
 
@@ -2524,115 +2524,6 @@ function isRecord2(value) {
2524
2524
  return typeof value === "object" && value !== null && !Array.isArray(value);
2525
2525
  }
2526
2526
 
2527
- // packages/safe-js/src/modules/env.ts
2528
- var EnvAccessError = class extends Error {
2529
- code = "ENV_ACCESS_DENIED";
2530
- variable;
2531
- constructor(variable) {
2532
- super(`Environment access denied for ${JSON.stringify(variable)}.`);
2533
- this.name = "EnvAccessError";
2534
- this.variable = variable;
2535
- hostErrorData.set(this, { code: this.code, variable });
2536
- }
2537
- };
2538
- function makeEnvModule(input) {
2539
- const options = normalizeEnvOptions(Array.isArray(input) ? { allow: input } : input);
2540
- const allowedNames = new Set(options.allow);
2541
- const values = options.values;
2542
- return {
2543
- get(name) {
2544
- const variable = readEnvName(name, "Environment variable name");
2545
- if (!allowedNames.has(variable)) throw new EnvAccessError(variable);
2546
- return readEnvValue(values ?? (typeof process !== "undefined" ? process.env ?? {} : {}), variable);
2547
- }
2548
- };
2549
- }
2550
- function parseEnvConfig(json) {
2551
- return normalizeEnvOptions(JSON.parse(json));
2552
- }
2553
- function normalizeEnvOptions(input) {
2554
- if (input === null || typeof input !== "object" || Array.isArray(input)) {
2555
- throw new TypeError("Environment options must be an object.");
2556
- }
2557
- const fields = Object.getOwnPropertyDescriptors(input);
2558
- for (const key of Reflect.ownKeys(fields)) {
2559
- if (key !== "allow" && key !== "values") {
2560
- throw new TypeError("Unknown environment option.");
2561
- }
2562
- if (!("value" in fields[key])) {
2563
- throw new TypeError("Environment options must not contain accessors.");
2564
- }
2565
- }
2566
- const inputAllow = Object.getOwnPropertyDescriptor(input, "allow")?.value;
2567
- if (!Array.isArray(inputAllow)) {
2568
- throw new TypeError("Environment allow list must be an array of non-empty strings.");
2569
- }
2570
- const allow = [];
2571
- for (let index = 0; index < inputAllow.length; index += 1) {
2572
- const descriptor = Object.getOwnPropertyDescriptor(inputAllow, index);
2573
- if (descriptor === void 0 || !("value" in descriptor)) {
2574
- throw new TypeError("Environment allow list must contain only own data entries.");
2575
- }
2576
- allow.push(readEnvName(descriptor.value, `Environment allow list[${index}]`));
2577
- }
2578
- const inputValues = Object.getOwnPropertyDescriptor(input, "values")?.value;
2579
- if (inputValues === void 0) return { allow };
2580
- if (inputValues === null || typeof inputValues !== "object" || Array.isArray(inputValues)) {
2581
- throw new TypeError("Environment values must be an object.");
2582
- }
2583
- return {
2584
- allow,
2585
- values: Object.fromEntries(allow.map((name) => [name, readEnvValue(inputValues, name)]))
2586
- };
2587
- }
2588
- function readEnvValue(values, name) {
2589
- const descriptor = Object.getOwnPropertyDescriptor(values, name);
2590
- if (descriptor === void 0) return void 0;
2591
- if (!("value" in descriptor)) {
2592
- throw new TypeError(`Environment value for ${JSON.stringify(name)} must not be an accessor.`);
2593
- }
2594
- if (descriptor.value !== void 0 && typeof descriptor.value !== "string") {
2595
- throw new TypeError(
2596
- `Environment value for ${JSON.stringify(name)} must be a string or undefined.`
2597
- );
2598
- }
2599
- return descriptor.value;
2600
- }
2601
- function readEnvName(value, label) {
2602
- if (typeof value !== "string" || value.length === 0) {
2603
- throw new TypeError(`${label} must be a non-empty string.`);
2604
- }
2605
- if (value.includes("\0") || value.includes("=")) {
2606
- throw new TypeError(`${label} must not contain NUL or equals signs.`);
2607
- }
2608
- return value;
2609
- }
2610
-
2611
- // packages/safe-js/src/modules/fail.ts
2612
- var HarnessFailure = class extends Error {
2613
- constructor(message) {
2614
- super(message);
2615
- this.name = "HarnessFailure";
2616
- }
2617
- };
2618
- function makeFailModule() {
2619
- function fail(message) {
2620
- throw new HarnessFailure(readNonEmptyString2(message, "Harness failure message"));
2621
- }
2622
- return {
2623
- default: fail
2624
- };
2625
- }
2626
- function readNonEmptyString2(value, label) {
2627
- if (typeof value !== "string") {
2628
- throw new Error(`${label} must be a non-empty string.`);
2629
- }
2630
- if (value.trim().length === 0) {
2631
- throw new Error(`${label} must be a non-empty string.`);
2632
- }
2633
- return value;
2634
- }
2635
-
2636
2527
  // packages/safe-js/src/modules/fs-config.ts
2637
2528
  import {
2638
2529
  createFileSystem,
@@ -2641,7 +2532,7 @@ import {
2641
2532
  validateFileSystemConfig
2642
2533
  } from "@poe-platform/safe-fs";
2643
2534
  function validateFsConfig(value) {
2644
- const config = readConfigRecord(value, "fs config", ["adapter", "root", "cwd", "readFileMaxBytes", "hostReadMemoryLimit"]);
2535
+ const config = readConfigRecord(value, "fs config", ["adapter", "root", "cwd", "readFileMaxBytes", "hostReadMemoryLimit", "maxSymlinkFollows"]);
2645
2536
  const paths = {};
2646
2537
  for (const key of ["root", "cwd"]) {
2647
2538
  const path = config[key];
@@ -2652,7 +2543,7 @@ function validateFsConfig(value) {
2652
2543
  paths[key] = path;
2653
2544
  }
2654
2545
  const limits = {};
2655
- for (const key of ["readFileMaxBytes", "hostReadMemoryLimit"]) {
2546
+ for (const key of ["readFileMaxBytes", "hostReadMemoryLimit", "maxSymlinkFollows"]) {
2656
2547
  const value2 = config[key];
2657
2548
  if (value2 === void 0) continue;
2658
2549
  if (value2 !== Infinity && (typeof value2 !== "number" || !Number.isSafeInteger(value2) || value2 < 0))
@@ -2701,7 +2592,7 @@ function makeLogModule(sink = createJsonlStdoutSink()) {
2701
2592
  normalizedSink({
2702
2593
  ts: (/* @__PURE__ */ new Date()).toISOString(),
2703
2594
  type: "event",
2704
- name: readNonEmptyString3(name, "Event name"),
2595
+ name: readNonEmptyString2(name, "Event name"),
2705
2596
  payload
2706
2597
  });
2707
2598
  }
@@ -2724,7 +2615,7 @@ function createJsonlStdoutSink() {
2724
2615
  stdoutSinks.set(stream, sink);
2725
2616
  return sink;
2726
2617
  }
2727
- function readNonEmptyString3(value, label) {
2618
+ function readNonEmptyString2(value, label) {
2728
2619
  if (typeof value !== "string") {
2729
2620
  throw new Error(`${label} must be a non-empty string.`);
2730
2621
  }
@@ -2796,43 +2687,6 @@ function toJsonValue(value, seen) {
2796
2687
  return String(value);
2797
2688
  }
2798
2689
 
2799
- // packages/safe-js/src/modules/metric.ts
2800
- function makeMetricModule(npmRunner) {
2801
- return {
2802
- async run(name) {
2803
- const metricName = readNonEmptyString4(name, "Metric name");
2804
- const scriptName = `metric:${metricName}`;
2805
- const stdout = await npmRunner(scriptName);
2806
- const score = parseMetricScore(stdout, scriptName);
2807
- return score;
2808
- }
2809
- };
2810
- }
2811
- function parseMetricScore(stdout, scriptName) {
2812
- if (typeof stdout !== "string") {
2813
- throw new Error(`Metric runner for "${scriptName}" must resolve to a stdout string.`);
2814
- }
2815
- const scoreLine = stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).at(-1);
2816
- if (scoreLine === void 0) {
2817
- throw new Error(`Metric script "${scriptName}" must print a numeric score.`);
2818
- }
2819
- const score = Number(scoreLine);
2820
- if (!Number.isFinite(score)) {
2821
- throw new Error(`Metric script "${scriptName}" must print a numeric score.`);
2822
- }
2823
- return score;
2824
- }
2825
- function readNonEmptyString4(value, label) {
2826
- if (typeof value !== "string") {
2827
- throw new Error(`${label} must be a non-empty string.`);
2828
- }
2829
- const normalizedValue = value.trim();
2830
- if (normalizedValue.length === 0) {
2831
- throw new Error(`${label} must be a non-empty string.`);
2832
- }
2833
- return normalizedValue;
2834
- }
2835
-
2836
2690
  export {
2837
2691
  formatInterpreterError,
2838
2692
  SPAWN_MODES,
@@ -2845,12 +2699,7 @@ export {
2845
2699
  createSpawnUsageAccumulator,
2846
2700
  runWithSpawnUsageAccumulator,
2847
2701
  makeAgentModule,
2848
- EnvAccessError,
2849
- makeEnvModule,
2850
- parseEnvConfig,
2851
- makeFailModule,
2852
2702
  parseFsConfig,
2853
2703
  resolveFsConfig,
2854
- makeLogModule,
2855
- makeMetricModule
2704
+ makeLogModule
2856
2705
  };