@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
@@ -3686,11 +3686,11 @@ function copyBoundedOAuthJson(value, message) {
3686
3686
  import http from "node:http";
3687
3687
 
3688
3688
  // packages/mcp-oauth/src/client/authorization-state.ts
3689
- import crypto from "node:crypto";
3689
+ import crypto2 from "node:crypto";
3690
3690
  function createAuthorizationState(input) {
3691
3691
  const payload = {
3692
3692
  v: 1,
3693
- n: crypto.randomBytes(16).toString("base64url"),
3693
+ n: crypto2.randomBytes(16).toString("base64url"),
3694
3694
  i: input.issuer,
3695
3695
  r: input.requireIssuer
3696
3696
  };
@@ -3753,6 +3753,7 @@ function snapshotLoopbackAuthorizationOptions(options) {
3753
3753
  ...options,
3754
3754
  openBrowser: options.openBrowser?.bind(options),
3755
3755
  readLine: options.readLine?.bind(options),
3756
+ waitForCallback: options.waitForCallback?.bind(options),
3756
3757
  createServer: options.createServer?.bind(options),
3757
3758
  callbackPath: options.callbackPath,
3758
3759
  redirectUri: options.redirectUri,
@@ -3767,6 +3768,7 @@ async function createLoopbackAuthorizationSession(options = {}) {
3767
3768
  const timeoutMs = options.timeoutMs ?? 12e4;
3768
3769
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647)
3769
3770
  throw new Error("OAuth authorization timeoutMs must be a positive supported timer interval");
3771
+ if (options.waitForCallback !== void 0) return createHostedAuthorizationSession(options, timeoutMs);
3770
3772
  const target = loopbackTarget(options);
3771
3773
  const server = options.createServer ? options.createServer() : http.createServer();
3772
3774
  const controller = new AbortController();
@@ -3829,6 +3831,72 @@ function loopbackTarget(options) {
3829
3831
  throw new Error("Invalid OAuth loopback callback path");
3830
3832
  return { port: 0, host: "127.0.0.1", callbackPath };
3831
3833
  }
3834
+ function validateAuthorizationRedirect(options) {
3835
+ if (options.waitForCallback === void 0) {
3836
+ loopbackTarget(options);
3837
+ return;
3838
+ }
3839
+ if (typeof options.waitForCallback !== "function" || options.redirectUri === void 0)
3840
+ throw new Error("Hosted OAuth callbacks require redirectUri and waitForCallback");
3841
+ validateHostedOAuthRedirect(options.redirectUri, options.callbackPath);
3842
+ }
3843
+ function validateHostedOAuthRedirect(redirectUri, callbackPath) {
3844
+ const url = new URL(redirectUri);
3845
+ if (url.protocol !== "https:" || url.username || url.password || url.href.includes("#") || url.port === "0" || oauthCallbackParameters.some((name) => url.searchParams.has(name)) || [...redirectUri].some((char) => char.codePointAt(0) <= 32) || callbackPath !== void 0 && callbackPath !== url.pathname)
3846
+ throw new Error("Invalid hosted OAuth redirect URI");
3847
+ }
3848
+ function createHostedAuthorizationSession(options, timeoutMs) {
3849
+ validateAuthorizationRedirect(options);
3850
+ const redirectUri = options.redirectUri, controller = new AbortController();
3851
+ let used = false;
3852
+ const aborted = () => controller.abort(options.signal?.reason);
3853
+ const timer = setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
3854
+ timer.unref?.();
3855
+ const cleanup = () => {
3856
+ clearTimeout(timer);
3857
+ options.signal?.removeEventListener("abort", aborted);
3858
+ };
3859
+ controller.signal.addEventListener("abort", cleanup, { once: true });
3860
+ options.signal?.addEventListener("abort", aborted, { once: true });
3861
+ if (options.signal?.aborted) aborted();
3862
+ return {
3863
+ redirectUri,
3864
+ async waitForCode(authorizationUrl) {
3865
+ controller.signal.throwIfAborted();
3866
+ if (used) throw new Error("OAuth authorization session has already been used");
3867
+ used = true;
3868
+ try {
3869
+ const authorization = new URL(authorizationUrl);
3870
+ if (authorization.searchParams.getAll("redirect_uri").length !== 1 || authorization.searchParams.get("redirect_uri") !== redirectUri)
3871
+ throw new Error("OAuth authorization redirect mismatch");
3872
+ const callback = await new Promise((resolve, reject) => {
3873
+ const onAbort = () => reject(controller.signal.reason);
3874
+ controller.signal.addEventListener("abort", onAbort, { once: true });
3875
+ void Promise.resolve().then(() => {
3876
+ controller.signal.throwIfAborted();
3877
+ return options.waitForCallback({ authorizationUrl, redirectUri, signal: controller.signal });
3878
+ }).then(resolve, reject).finally(() => controller.signal.removeEventListener("abort", onAbort));
3879
+ });
3880
+ controller.signal.throwIfAborted();
3881
+ const url = new URL(callback), base = new URL(redirectUri);
3882
+ if (url.origin !== base.origin || url.pathname !== base.pathname || url.username || url.password || url.href.includes("#") || [...callback].some((char) => char.codePointAt(0) <= 32)) throw new Error("OAuth callback redirect mismatch");
3883
+ const params = readAuthorizationCallbackParameters(url);
3884
+ for (const name of oauthCallbackParameters) url.searchParams.delete(name);
3885
+ if (url.searchParams.toString() !== base.searchParams.toString()) throw new Error("OAuth callback redirect mismatch");
3886
+ const expected = readExpectedAuthorizationCallback(authorizationUrl);
3887
+ if (expected.state === null) throw new Error("OAuth authorization missing state");
3888
+ validateAuthorizationCallbackBinding(params, expected);
3889
+ if (params.error !== null) throw new OAuthAuthorizationError(params.error, params.errorDescription ?? params.error);
3890
+ return validateAuthorizationCallbackParameters(params, expected);
3891
+ } finally {
3892
+ controller.abort(new Error("OAuth authorization session closed"));
3893
+ }
3894
+ },
3895
+ close() {
3896
+ controller.abort(new Error("OAuth authorization session closed"));
3897
+ }
3898
+ };
3899
+ }
3832
3900
  async function startServer(server, port, host, signal) {
3833
3901
  signal.throwIfAborted();
3834
3902
  return new Promise((resolve, reject) => {
@@ -4076,7 +4144,9 @@ function normalizeStoredOAuthClient(value) {
4076
4144
  if (requestedRedirectUri !== void 0) {
4077
4145
  try {
4078
4146
  if (typeof requestedRedirectUri !== "string") throw new Error("Invalid redirect identity");
4079
- loopbackTarget({ redirectUri: requestedRedirectUri });
4147
+ if (new URL(requestedRedirectUri).protocol === "https:")
4148
+ validateHostedOAuthRedirect(requestedRedirectUri);
4149
+ else loopbackTarget({ redirectUri: requestedRedirectUri });
4080
4150
  } catch {
4081
4151
  throw new Error("Invalid stored OAuth registration redirect identity");
4082
4152
  }
@@ -4128,7 +4198,7 @@ function registrationMatchesRedirect(client, requestedUri, fresh = false) {
4128
4198
  }
4129
4199
 
4130
4200
  // packages/mcp-oauth/src/client/auth-store-session-store.ts
4131
- import crypto2 from "node:crypto";
4201
+ import crypto3 from "node:crypto";
4132
4202
  import path4 from "node:path";
4133
4203
 
4134
4204
  // packages/auth-store/src/encrypted-file-store.ts
@@ -4886,7 +4956,7 @@ function snapshotPersistenceOptions(options) {
4886
4956
  }
4887
4957
  function createNamedSecretStore(key2, options, defaults, namespace) {
4888
4958
  options = snapshotOAuthPersistenceOptions(options);
4889
- const hash = crypto2.createHash("sha256").update(namespace === void 0 ? key2 : JSON.stringify([namespace, key2])).digest("hex");
4959
+ const hash = crypto3.createHash("sha256").update(namespace === void 0 ? key2 : JSON.stringify([namespace, key2])).digest("hex");
4890
4960
  const configuredFilePath = options.fileStore?.filePath;
4891
4961
  const parsedFilePath = configuredFilePath === void 0 ? null : path4.parse(configuredFilePath);
4892
4962
  const fileStore = {
@@ -4968,7 +5038,7 @@ function isStoredOAuthTokensOrMissing(value) {
4968
5038
  return false;
4969
5039
  }
4970
5040
  const scope = getOwnEntry4(value, "scope");
4971
- return scope === void 0 || typeof scope === "string" && scope.trim().length > 0;
5041
+ return scope === void 0 || scope === "" || typeof scope === "string" && scope.trim().length > 0;
4972
5042
  }
4973
5043
  function isNonBlankOwnString(record2, key2) {
4974
5044
  const value = getOwnString(record2, key2);
@@ -5186,12 +5256,12 @@ async function fetchMcpResponse(fetchImplementation, input, init = {}) {
5186
5256
  import { URL as URL2 } from "node:url";
5187
5257
 
5188
5258
  // packages/mcp-oauth/src/client/pkce.ts
5189
- import crypto3 from "node:crypto";
5259
+ import crypto4 from "node:crypto";
5190
5260
  function generateCodeVerifier() {
5191
- return crypto3.randomBytes(32).toString("base64url");
5261
+ return crypto4.randomBytes(32).toString("base64url");
5192
5262
  }
5193
5263
  function generateCodeChallenge(verifier) {
5194
- return crypto3.createHash("sha256").update(verifier).digest("base64url");
5264
+ return crypto4.createHash("sha256").update(verifier).digest("base64url");
5195
5265
  }
5196
5266
 
5197
5267
  // packages/mcp-oauth/src/http-response.ts
@@ -5320,7 +5390,7 @@ async function requestTokens(input) {
5320
5390
  if (method === "client_secret_basic") {
5321
5391
  const encoded = new URLSearchParams({ credential: input.clientId }).toString().slice("credential=".length);
5322
5392
  const encodedSecret = new URLSearchParams({ credential: input.clientSecret }).toString().slice("credential=".length);
5323
- headers.set("Authorization", `Basic ${Buffer.from(`${encoded}:${encodedSecret}`).toString("base64")}`);
5393
+ headers.set("Authorization", `Basic ${btoa(`${encoded}:${encodedSecret}`)}`);
5324
5394
  } else {
5325
5395
  body.set("client_id", input.clientId);
5326
5396
  if (method === "client_secret_post") body.set("client_secret", input.clientSecret);
@@ -5368,7 +5438,7 @@ async function requestTokens(input) {
5368
5438
  throw new Error("OAuth token response has invalid refresh_token");
5369
5439
  const scope = getOwnEntry5(payload, "scope");
5370
5440
  const normalizedRefreshToken = typeof refreshToken === "string" && refreshToken.trim().length > 0 ? refreshToken.trim() : void 0;
5371
- const normalizedScope = normalizeOAuthScope(scope);
5441
+ const normalizedScope = scope === "" ? "" : normalizeOAuthScope(scope);
5372
5442
  if (scope !== void 0 && normalizedScope === void 0)
5373
5443
  throw new Error("Invalid OAuth scope syntax in token response");
5374
5444
  return {
@@ -5494,7 +5564,7 @@ function createDefaultOAuthClientProvider(options) {
5494
5564
  const clientMode = options.client.mode;
5495
5565
  const interactiveEnabled = options.allowInteractive !== false;
5496
5566
  const sessionLockTimeoutMs = options.sessionLockTimeoutMs;
5497
- loopbackTarget(browser);
5567
+ validateAuthorizationRedirect(browser);
5498
5568
  assertPersistenceNamespace(options.persistenceNamespace);
5499
5569
  const clientMetadata = getClientMetadata(options.client);
5500
5570
  const requestedScope = clientMetadata?.scope;
@@ -6116,7 +6186,7 @@ function normalizeStoredTokens(value) {
6116
6186
  const scope = getOwnEntry6(value, "scope");
6117
6187
  const normalizedAccessToken = accessToken?.trim();
6118
6188
  const normalizedRefreshToken = typeof refreshToken === "string" ? refreshToken.trim() : void 0;
6119
- const normalizedScope = normalizeOAuthScope(scope);
6189
+ const normalizedScope = scope === "" ? "" : normalizeOAuthScope(scope);
6120
6190
  if (accessToken === void 0 || normalizedAccessToken === void 0 || normalizedAccessToken.length === 0 || tokenType !== "Bearer" || !(expiresAt === null || typeof expiresAt === "number" && Number.isSafeInteger(expiresAt) && expiresAt <= MAX_JS_DATE_MS3 && Number.isFinite(new Date(expiresAt).getTime())) || refreshToken !== void 0 && (typeof refreshToken !== "string" || normalizedRefreshToken === void 0 || normalizedRefreshToken.length === 0)) {
6121
6191
  return void 0;
6122
6192
  }
@@ -6125,7 +6195,7 @@ function normalizeStoredTokens(value) {
6125
6195
  tokenType,
6126
6196
  expiresAt,
6127
6197
  ...normalizedRefreshToken === void 0 ? {} : { refreshToken: normalizedRefreshToken },
6128
- ...normalizedScope === void 0 || normalizedScope.length === 0 ? {} : { scope: normalizedScope }
6198
+ ...normalizedScope === void 0 ? {} : { scope: normalizedScope }
6129
6199
  };
6130
6200
  }
6131
6201
  function getClientMetadata(client) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSandboxClosure
3
- } from "./chunk-ULPZIHB5.js";
3
+ } from "./chunk-5RLD4TVI.js";
4
4
 
5
5
  // packages/safe-js/src/lint/runtime-modules.ts
6
6
  var AsyncFunction = (async () => void 0).constructor;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  deepCopyToSandbox
3
- } from "./chunk-ULPZIHB5.js";
3
+ } from "./chunk-5RLD4TVI.js";
4
4
 
5
5
  // packages/safe-js/src/modules/harness.ts
6
6
  function makeHarnessModule(frontmatter, meta) {
@@ -10,7 +10,7 @@ import {
10
10
  validateMigrationReconciliation,
11
11
  validateMigrationSemantics,
12
12
  validateSnapshotMigration
13
- } from "./chunk-ZKLJW27N.js";
13
+ } from "./chunk-PMOXSLM5.js";
14
14
  import {
15
15
  DUMP_FORMAT_VERSION,
16
16
  EXECUTION_SEMANTICS,
@@ -20,7 +20,7 @@ import {
20
20
  serializeSafeJSSnapshot,
21
21
  validateDumpEnvelope,
22
22
  validateSnapshotData
23
- } from "./chunk-ULPZIHB5.js";
23
+ } from "./chunk-5RLD4TVI.js";
24
24
  import {
25
25
  parseModule
26
26
  } from "./chunk-A3LXCGMJ.js";
@@ -7,15 +7,15 @@ import {
7
7
  } from "./chunk-ONWFPHUI.js";
8
8
  import {
9
9
  restore
10
- } from "./chunk-MDJ7MJYI.js";
10
+ } from "./chunk-XOMGWE37.js";
11
11
  import {
12
12
  hashParsedAst,
13
13
  hashSource
14
- } from "./chunk-ZKLJW27N.js";
14
+ } from "./chunk-PMOXSLM5.js";
15
15
  import {
16
16
  attachDumpController,
17
17
  createDumpController
18
- } from "./chunk-QTCPJ7FN.js";
18
+ } from "./chunk-XV5OYKND.js";
19
19
  import {
20
20
  DUMP_FORMAT_VERSION,
21
21
  EXECUTION_SEMANTICS,
@@ -107,7 +107,7 @@ import {
107
107
  wrapCallerInjectedBindings,
108
108
  wrapCancelableBindings,
109
109
  wrapHostConstructor
110
- } from "./chunk-ULPZIHB5.js";
110
+ } from "./chunk-5RLD4TVI.js";
111
111
  import {
112
112
  Budget,
113
113
  CompileScope,
@@ -1217,7 +1217,7 @@ var RealmState = class {
1217
1217
  this.assertOpen();
1218
1218
  const closure = readGuestCallback(callback, this);
1219
1219
  this.checkCollection(this.pendingCallbacks.size + 1, this.limits.callbacks, "pending callback");
1220
- const scheduled = this.options.callbackScheduling === "after-prefix";
1220
+ const scheduled = this.options.callbackScheduling === "after-prefix" || this.queuedCallbacks.has(callback);
1221
1221
  const record = {
1222
1222
  closure,
1223
1223
  prefixComplete: false
@@ -1599,7 +1599,7 @@ var RealmState = class {
1599
1599
  await this.dispose();
1600
1600
  this.assertOpen();
1601
1601
  if (this.active !== void 0) throw new SandboxError("reentry");
1602
- const scheduled = this.options.callbackScheduling === "after-prefix";
1602
+ const scheduled = this.options.callbackScheduling === "after-prefix" || this.releaseReconciliation !== void 0 || this.pendingWork.size > 0;
1603
1603
  if (scheduled && (this.phase.getStore()?.active || [...this.pendingCallbacks].some((record) => !record.prefixComplete)))
1604
1604
  throw new SandboxError("reentry");
1605
1605
  const rejectionOwner = scheduled ? this.tracker.startOperation() : void 0;
@@ -1649,13 +1649,13 @@ var RealmState = class {
1649
1649
  } finally {
1650
1650
  this.tracker.finishOperation(rejectionOwner);
1651
1651
  if (this.active === pending) this.active = void 0;
1652
- if (scheduled) this.reconcileWhenIdle();
1652
+ if (scheduled || this.releaseReconciliation !== void 0) this.reconcileWhenIdle();
1653
1653
  }
1654
1654
  }
1655
1655
  close = () => {
1656
1656
  this.closed = true;
1657
1657
  this.controller.abort(new Error("SafeJS realm is closed."));
1658
- if (this.options.callbackScheduling === "after-prefix") {
1658
+ if (this.options.callbackScheduling === "after-prefix" || this.releaseReconciliation !== void 0 || this.pendingWork.size > 0) {
1659
1659
  const disposal = this.dispose();
1660
1660
  if (this.phase.getStore()?.active) {
1661
1661
  void disposal.catch(() => void 0);
@@ -1674,7 +1674,7 @@ var RealmState = class {
1674
1674
  this.closed = true;
1675
1675
  this.controller.abort(new Error("SafeJS realm is closed."));
1676
1676
  this.options.signal?.removeEventListener("abort", this.abort);
1677
- if (this.options.callbackScheduling === "after-prefix") {
1677
+ if (this.options.callbackScheduling === "after-prefix" || this.releaseReconciliation !== void 0 || this.pendingWork.size > 0) {
1678
1678
  this.disposal = Promise.resolve().then(async () => {
1679
1679
  while (this.pendingWork.size > 0) await Promise.allSettled([...this.pendingWork]);
1680
1680
  await this.sourceGraph?.settle();
@@ -1836,16 +1836,24 @@ import { createFsBridge } from "@poe-platform/safe-fs/core";
1836
1836
  var DEFAULT_WRITE_MAX_ATTEMPTS = 3;
1837
1837
  var DEFAULT_WRITE_RETRY_DELAY_MS = 100;
1838
1838
  var LOCKED_FILE_ERROR_CODES = /* @__PURE__ */ new Set(["EACCES", "EBUSY", "EPERM"]);
1839
- var pendingOperations = /* @__PURE__ */ new Map();
1839
+ var adapterOperations = /* @__PURE__ */ new WeakMap();
1840
1840
  var FileSnapshotBackend = class {
1841
1841
  constructor(path, options = {}) {
1842
1842
  this.path = path;
1843
+ const adapter = options.adapter ?? hostFs;
1844
+ let pending = adapterOperations.get(adapter);
1845
+ if (pending === void 0) {
1846
+ pending = /* @__PURE__ */ new Map();
1847
+ adapterOperations.set(adapter, pending);
1848
+ }
1849
+ this.#pendingOperations = pending;
1843
1850
  this.#io = options.adapter === void 0 ? hostFs : createFsBridge(options.adapter, { codec: fsCodec });
1844
1851
  this.#writeMaxAttempts = options.writeMaxAttempts ?? DEFAULT_WRITE_MAX_ATTEMPTS;
1845
1852
  this.#writeRetryDelayMs = options.writeRetryDelayMs ?? DEFAULT_WRITE_RETRY_DELAY_MS;
1846
1853
  }
1847
1854
  path;
1848
1855
  #io;
1856
+ #pendingOperations;
1849
1857
  #writeMaxAttempts;
1850
1858
  #writeRetryDelayMs;
1851
1859
  async read() {
@@ -1863,6 +1871,7 @@ var FileSnapshotBackend = class {
1863
1871
  }
1864
1872
  async write(snapshot) {
1865
1873
  await enqueueOperation(
1874
+ this.#pendingOperations,
1866
1875
  this.path,
1867
1876
  () => writeSnapshotAtomically(this.#io, this.path, snapshot, {
1868
1877
  maxAttempts: this.#writeMaxAttempts,
@@ -1871,7 +1880,7 @@ var FileSnapshotBackend = class {
1871
1880
  );
1872
1881
  }
1873
1882
  async remove() {
1874
- await enqueueOperation(this.path, async () => {
1883
+ await enqueueOperation(this.#pendingOperations, this.path, async () => {
1875
1884
  try {
1876
1885
  await this.#io.unlink(this.path);
1877
1886
  } catch (error) {
@@ -1959,7 +1968,7 @@ async function writeSnapshotOnce(io, temporaryPath, snapshotPath, contents) {
1959
1968
  }
1960
1969
  }
1961
1970
  }
1962
- async function enqueueOperation(path, operation) {
1971
+ async function enqueueOperation(pendingOperations, path, operation) {
1963
1972
  const previous = pendingOperations.get(path) ?? Promise.resolve();
1964
1973
  const pending = previous.catch(() => void 0).then(operation);
1965
1974
  const queued = pending.catch(() => void 0);
@@ -2551,10 +2560,9 @@ function run(source, options = {}) {
2551
2560
  migration: restoredSnapshot?.migration,
2552
2561
  bindings: executionScope.snapshot().bindings,
2553
2562
  clock: options.clock,
2554
- hostCalls: hostCalls.snapshot(),
2555
- replay: hostCalls.snapshotReplay(),
2563
+ hostCalls,
2556
2564
  initialInputs: initialInputs.snapshot,
2557
- promiseReplay: promiseReplay.snapshot(),
2565
+ promiseReplay,
2558
2566
  random,
2559
2567
  sourceHash
2560
2568
  });
@@ -2581,12 +2589,11 @@ function run(source, options = {}) {
2581
2589
  migration: restoredSnapshot?.migration,
2582
2590
  bindings: interpreterSnapshot2.bindings,
2583
2591
  clock: options.clock,
2584
- hostCalls: hostCalls.snapshot(),
2592
+ hostCalls,
2585
2593
  loopIterations: interpreterSnapshot2.loopIterations,
2586
2594
  pendingAwaits: [createPendingAwaitSnapshot(yieldPoint)],
2587
- replay: hostCalls.snapshotReplay(),
2588
2595
  initialInputs: initialInputs.snapshot,
2589
- promiseReplay: promiseReplay.snapshot(),
2596
+ promiseReplay,
2590
2597
  random,
2591
2598
  randomResumeState: typeof yieldPoint.replayState === "number" ? yieldPoint.replayState : void 0,
2592
2599
  sourceHash
@@ -2626,12 +2633,11 @@ function run(source, options = {}) {
2626
2633
  migration: restoredSnapshot?.migration,
2627
2634
  bindings: interpreterSnapshot2.bindings,
2628
2635
  clock: options.clock,
2629
- hostCalls: hostCalls.snapshot(),
2636
+ hostCalls,
2630
2637
  loopIterations: interpreterSnapshot2.loopIterations,
2631
2638
  pendingAwaits: [createPendingAwaitSnapshot(yieldPoint)],
2632
- replay: hostCalls.snapshotReplay(),
2633
2639
  initialInputs: initialInputs.snapshot,
2634
- promiseReplay: promiseReplay.snapshot(),
2640
+ promiseReplay,
2635
2641
  random,
2636
2642
  randomResumeState: typeof yieldPoint.replayState === "number" ? yieldPoint.replayState : void 0,
2637
2643
  sourceHash
@@ -2648,27 +2654,17 @@ function run(source, options = {}) {
2648
2654
  await throwIfReturnedPromiseRejected(result2);
2649
2655
  await throwIfUnhandledPromiseRejected(promiseTracker);
2650
2656
  await activeSnapshotScheduler.finish();
2651
- let replay;
2652
- let replayError;
2653
- try {
2654
- replay = hostCalls.snapshotReplay();
2655
- } catch (error) {
2656
- if (!(error instanceof MissingReplayCapabilityError)) throw error;
2657
- replayError = error.message;
2658
- }
2659
2657
  const snapshot = createRunSnapshot({
2660
2658
  executionSemantics,
2661
2659
  migration: restoredSnapshot?.migration,
2662
2660
  bindings: executionScope.snapshot().bindings,
2663
2661
  clock: options.clock,
2664
- hostCalls: hostCalls.snapshot(),
2665
- replay,
2662
+ hostCalls,
2666
2663
  initialInputs: initialInputs.snapshot,
2667
- promiseReplay: promiseReplay.snapshot(),
2664
+ promiseReplay,
2668
2665
  random,
2669
2666
  sourceHash
2670
2667
  });
2671
- if (replayError !== void 0) snapshot.replayError = replayError;
2672
2668
  dumpController.finalize(snapshot);
2673
2669
  completedSnapshot = snapshot;
2674
2670
  reconcileCompiledValues(
@@ -2845,6 +2841,15 @@ function createExecutableNode(module) {
2845
2841
  };
2846
2842
  }
2847
2843
  function createRunSnapshot(input) {
2844
+ let replay;
2845
+ let replayError;
2846
+ try {
2847
+ replay = input.hostCalls.snapshotReplay();
2848
+ } catch (error) {
2849
+ if (!(error instanceof MissingReplayCapabilityError)) throw error;
2850
+ replayError = error.message;
2851
+ }
2852
+ const hostCalls = input.hostCalls.snapshot();
2848
2853
  const snapshot = {
2849
2854
  version: DUMP_FORMAT_VERSION,
2850
2855
  executionSemantics: input.executionSemantics,
@@ -2852,11 +2857,12 @@ function createRunSnapshot(input) {
2852
2857
  ...input.migration === void 0 ? {} : { migration: structuredClone(input.migration) },
2853
2858
  bindings: input.bindings,
2854
2859
  clock: input.clock?.snapshot(),
2855
- ...input.hostCalls === void 0 || input.hostCalls.length === 0 ? {} : { hostCalls: input.hostCalls },
2860
+ ...hostCalls.length === 0 ? {} : { hostCalls },
2856
2861
  ...input.loopIterations === void 0 ? {} : { loopIterations: input.loopIterations },
2857
- ...input.replay === void 0 ? {} : { replay: input.replay },
2862
+ ...replay === void 0 ? {} : { replay },
2863
+ ...replayError === void 0 ? {} : { replayError },
2858
2864
  ...input.initialInputs === void 0 ? {} : { initialInputs: structuredClone(input.initialInputs) },
2859
- ...input.promiseReplay === void 0 ? {} : { promiseReplay: input.promiseReplay },
2865
+ promiseReplay: input.promiseReplay.snapshot(),
2860
2866
  ...input.pendingAwaits === void 0 || input.pendingAwaits.length === 0 ? {} : { pendingAwaits: input.pendingAwaits },
2861
2867
  random: input.random === void 0 ? void 0 : {
2862
2868
  seed: input.random.seed,
@@ -5,6 +5,7 @@ import {
5
5
  collectComments,
6
6
  formatParseError,
7
7
  hoistedVarDeclarations,
8
+ hostErrorData,
8
9
  numericTypedArrayConstructors,
9
10
  parseModule,
10
11
  tokenize
@@ -11174,6 +11175,158 @@ function hasOnlyRegexLiteralDiagnostics(diagnostics) {
11174
11175
  );
11175
11176
  }
11176
11177
 
11178
+ // packages/safe-js/src/modules/env.ts
11179
+ var EnvAccessError = class extends Error {
11180
+ code = "ENV_ACCESS_DENIED";
11181
+ variable;
11182
+ constructor(variable) {
11183
+ super(`Environment access denied for ${JSON.stringify(variable)}.`);
11184
+ this.name = "EnvAccessError";
11185
+ this.variable = variable;
11186
+ hostErrorData.set(this, { code: this.code, variable });
11187
+ }
11188
+ };
11189
+ function makeEnvModule(input) {
11190
+ const options = normalizeEnvOptions(Array.isArray(input) ? { allow: input } : input);
11191
+ const allowedNames = new Set(options.allow);
11192
+ const values = options.values;
11193
+ return {
11194
+ get(name) {
11195
+ const variable = readEnvName(name, "Environment variable name");
11196
+ if (!allowedNames.has(variable)) throw new EnvAccessError(variable);
11197
+ return readEnvValue(values ?? (typeof process !== "undefined" ? process.env ?? {} : {}), variable);
11198
+ }
11199
+ };
11200
+ }
11201
+ function parseEnvConfig(json) {
11202
+ return normalizeEnvOptions(JSON.parse(json));
11203
+ }
11204
+ function normalizeEnvOptions(input) {
11205
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
11206
+ throw new TypeError("Environment options must be an object.");
11207
+ }
11208
+ const fields = Object.getOwnPropertyDescriptors(input);
11209
+ for (const key of Reflect.ownKeys(fields)) {
11210
+ if (key !== "allow" && key !== "values") {
11211
+ throw new TypeError("Unknown environment option.");
11212
+ }
11213
+ if (!("value" in fields[key])) {
11214
+ throw new TypeError("Environment options must not contain accessors.");
11215
+ }
11216
+ }
11217
+ const inputAllow = Object.getOwnPropertyDescriptor(input, "allow")?.value;
11218
+ if (!Array.isArray(inputAllow)) {
11219
+ throw new TypeError("Environment allow list must be an array of non-empty strings.");
11220
+ }
11221
+ const allow = [];
11222
+ for (let index = 0; index < inputAllow.length; index += 1) {
11223
+ const descriptor = Object.getOwnPropertyDescriptor(inputAllow, index);
11224
+ if (descriptor === void 0 || !("value" in descriptor)) {
11225
+ throw new TypeError("Environment allow list must contain only own data entries.");
11226
+ }
11227
+ allow.push(readEnvName(descriptor.value, `Environment allow list[${index}]`));
11228
+ }
11229
+ const inputValues = Object.getOwnPropertyDescriptor(input, "values")?.value;
11230
+ if (inputValues === void 0) return { allow };
11231
+ if (inputValues === null || typeof inputValues !== "object" || Array.isArray(inputValues)) {
11232
+ throw new TypeError("Environment values must be an object.");
11233
+ }
11234
+ return {
11235
+ allow,
11236
+ values: Object.fromEntries(allow.map((name) => [name, readEnvValue(inputValues, name)]))
11237
+ };
11238
+ }
11239
+ function readEnvValue(values, name) {
11240
+ const descriptor = Object.getOwnPropertyDescriptor(values, name);
11241
+ if (descriptor === void 0) return void 0;
11242
+ if (!("value" in descriptor)) {
11243
+ throw new TypeError(`Environment value for ${JSON.stringify(name)} must not be an accessor.`);
11244
+ }
11245
+ if (descriptor.value !== void 0 && typeof descriptor.value !== "string") {
11246
+ throw new TypeError(
11247
+ `Environment value for ${JSON.stringify(name)} must be a string or undefined.`
11248
+ );
11249
+ }
11250
+ return descriptor.value;
11251
+ }
11252
+ function readEnvName(value, label) {
11253
+ if (typeof value !== "string" || value.length === 0) {
11254
+ throw new TypeError(`${label} must be a non-empty string.`);
11255
+ }
11256
+ if (value.includes("\0") || value.includes("=")) {
11257
+ throw new TypeError(`${label} must not contain NUL or equals signs.`);
11258
+ }
11259
+ return value;
11260
+ }
11261
+
11262
+ // packages/safe-js/src/modules/fail.ts
11263
+ var HarnessFailure = class extends Error {
11264
+ constructor(message) {
11265
+ super(message);
11266
+ this.name = "HarnessFailure";
11267
+ }
11268
+ };
11269
+ function makeFailModule() {
11270
+ function fail(message) {
11271
+ throw new HarnessFailure(readNonEmptyString(message, "Harness failure message"));
11272
+ }
11273
+ return {
11274
+ default: fail
11275
+ };
11276
+ }
11277
+ function readNonEmptyString(value, label) {
11278
+ if (typeof value !== "string") {
11279
+ throw new Error(`${label} must be a non-empty string.`);
11280
+ }
11281
+ if (value.trim().length === 0) {
11282
+ throw new Error(`${label} must be a non-empty string.`);
11283
+ }
11284
+ return value;
11285
+ }
11286
+
11287
+ // packages/safe-js/src/modules/metric.ts
11288
+ function makeMetricModule(npmRunner) {
11289
+ return {
11290
+ async run(name) {
11291
+ const metricName = readNonEmptyString2(name, "Metric name");
11292
+ const scriptName = `metric:${metricName}`;
11293
+ const stdout = await npmRunner(scriptName);
11294
+ const score = parseMetricScore(stdout, scriptName);
11295
+ return score;
11296
+ }
11297
+ };
11298
+ }
11299
+ function parseMetricScore(stdout, scriptName) {
11300
+ if (typeof stdout !== "string") {
11301
+ throw new Error(`Metric runner for "${scriptName}" must resolve to a stdout string.`);
11302
+ }
11303
+ const scoreLine = stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).at(-1);
11304
+ if (scoreLine === void 0) {
11305
+ throw new Error(`Metric script "${scriptName}" must print a numeric score.`);
11306
+ }
11307
+ const score = Number(scoreLine);
11308
+ if (!Number.isFinite(score)) {
11309
+ throw new Error(`Metric script "${scriptName}" must print a numeric score.`);
11310
+ }
11311
+ return score;
11312
+ }
11313
+ function readNonEmptyString2(value, label) {
11314
+ if (typeof value !== "string") {
11315
+ throw new Error(`${label} must be a non-empty string.`);
11316
+ }
11317
+ const normalizedValue = value.trim();
11318
+ if (normalizedValue.length === 0) {
11319
+ throw new Error(`${label} must be a non-empty string.`);
11320
+ }
11321
+ return normalizedValue;
11322
+ }
11323
+
11177
11324
  export {
11178
- lint
11325
+ lint,
11326
+ EnvAccessError,
11327
+ makeEnvModule,
11328
+ parseEnvConfig,
11329
+ HarnessFailure,
11330
+ makeFailModule,
11331
+ makeMetricModule
11179
11332
  };
@@ -2,11 +2,11 @@ import {
2
2
  McpError,
3
3
  connectMcpTransport,
4
4
  normalizeMcpOptions
5
- } from "./chunk-MCFRB5X3.js";
5
+ } from "./chunk-6H5VC5W5.js";
6
6
  import {
7
7
  declareHostOperation,
8
8
  runResources
9
- } from "./chunk-ULPZIHB5.js";
9
+ } from "./chunk-5RLD4TVI.js";
10
10
  import {
11
11
  hostErrorData
12
12
  } from "./chunk-A3LXCGMJ.js";
@@ -2,7 +2,7 @@ import {
2
2
  HostCallJournal,
3
3
  decodeReplayData,
4
4
  validateSnapshotData
5
- } from "./chunk-ULPZIHB5.js";
5
+ } from "./chunk-5RLD4TVI.js";
6
6
  import {
7
7
  CompileScope,
8
8
  SandboxError,