jslike 1.4.5 → 1.5.0

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/dist/index.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  var src_exports = {};
21
21
  __export(src_exports, {
22
22
  Environment: () => Environment,
23
+ ExecutionController: () => ExecutionController,
23
24
  InMemoryModuleResolver: () => InMemoryModuleResolver,
24
25
  Interpreter: () => Interpreter,
25
26
  ModuleResolver: () => ModuleResolver,
@@ -5817,19 +5818,36 @@ var Interpreter = class _Interpreter {
5817
5818
  this.moduleCache = /* @__PURE__ */ new Map();
5818
5819
  this.moduleExports = {};
5819
5820
  this.abortSignal = options.abortSignal;
5821
+ this.executionController = options.executionController;
5820
5822
  }
5821
- // Check if execution should be aborted
5823
+ // Check if execution should be aborted (sync version)
5822
5824
  checkAbortSignal() {
5825
+ if (this.executionController) {
5826
+ this.executionController._checkAbortSync();
5827
+ return;
5828
+ }
5823
5829
  if (this.abortSignal && this.abortSignal.aborted) {
5824
5830
  const error = new Error("The operation was aborted");
5825
5831
  error.name = "AbortError";
5826
5832
  throw error;
5827
5833
  }
5828
5834
  }
5835
+ // Checkpoint that returns a promise only when controller is present
5836
+ // When no controller, returns null to signal no await needed
5837
+ _getCheckpointPromise(node, env) {
5838
+ if (this.executionController) {
5839
+ this.executionController._setEnv(env);
5840
+ return this.executionController._checkpoint(node);
5841
+ } else {
5842
+ this.checkAbortSignal();
5843
+ return null;
5844
+ }
5845
+ }
5829
5846
  // Async evaluation for async functions - handles await expressions
5830
5847
  async evaluateAsync(node, env) {
5831
5848
  if (!node) return void 0;
5832
- this.checkAbortSignal();
5849
+ const checkpointPromise = this._getCheckpointPromise(node, env);
5850
+ if (checkpointPromise) await checkpointPromise;
5833
5851
  if (node.type === "AwaitExpression") {
5834
5852
  const promise = await this.evaluateAsync(node.argument, env);
5835
5853
  return await promise;
@@ -6000,6 +6018,8 @@ var Interpreter = class _Interpreter {
6000
6018
  await this.evaluateAsync(node.init, forEnv);
6001
6019
  }
6002
6020
  while (!node.test || await this.evaluateAsync(node.test, forEnv)) {
6021
+ const cp1 = this._getCheckpointPromise(node, forEnv);
6022
+ if (cp1) await cp1;
6003
6023
  const result = await this.evaluateAsync(node.body, forEnv);
6004
6024
  if (result instanceof BreakSignal) {
6005
6025
  break;
@@ -6025,6 +6045,8 @@ var Interpreter = class _Interpreter {
6025
6045
  const declarator = node.left.declarations[0];
6026
6046
  const isConst = node.left.kind === "const";
6027
6047
  for (const value of iterable) {
6048
+ const cp2 = this._getCheckpointPromise(node, forEnv);
6049
+ if (cp2) await cp2;
6028
6050
  const iterEnv = forEnv.extend();
6029
6051
  if (declarator.id.type === "Identifier") {
6030
6052
  iterEnv.define(declarator.id.name, value, isConst);
@@ -6055,6 +6077,8 @@ var Interpreter = class _Interpreter {
6055
6077
  const varName = node.left.declarations[0].id.name;
6056
6078
  forEnv.define(varName, void 0);
6057
6079
  for (const key in obj) {
6080
+ const cp3 = this._getCheckpointPromise(node, forEnv);
6081
+ if (cp3) await cp3;
6058
6082
  forEnv.set(varName, key);
6059
6083
  const result = await this.evaluateAsync(node.body, forEnv);
6060
6084
  if (result instanceof BreakSignal) {
@@ -6071,6 +6095,8 @@ var Interpreter = class _Interpreter {
6071
6095
  }
6072
6096
  if (node.type === "WhileStatement") {
6073
6097
  while (await this.evaluateAsync(node.test, env)) {
6098
+ const cp4 = this._getCheckpointPromise(node, env);
6099
+ if (cp4) await cp4;
6074
6100
  const result = await this.evaluateAsync(node.body, env);
6075
6101
  if (result instanceof BreakSignal) {
6076
6102
  break;
@@ -6086,6 +6112,8 @@ var Interpreter = class _Interpreter {
6086
6112
  }
6087
6113
  if (node.type === "DoWhileStatement") {
6088
6114
  do {
6115
+ const cp5 = this._getCheckpointPromise(node, env);
6116
+ if (cp5) await cp5;
6089
6117
  const result = await this.evaluateAsync(node.body, env);
6090
6118
  if (result instanceof BreakSignal) {
6091
6119
  break;
@@ -6728,6 +6756,7 @@ var Interpreter = class _Interpreter {
6728
6756
  callUserFunction(func, args, callingEnv, thisContext = void 0) {
6729
6757
  const metadata = func.__metadata || func;
6730
6758
  const funcEnv = new Environment(metadata.closure);
6759
+ const funcName = metadata.name || func.name || "anonymous";
6731
6760
  if (thisContext !== void 0) {
6732
6761
  funcEnv.define("this", thisContext);
6733
6762
  }
@@ -6750,15 +6779,46 @@ var Interpreter = class _Interpreter {
6750
6779
  }
6751
6780
  }
6752
6781
  if (metadata.async) {
6782
+ if (this.executionController) {
6783
+ this.executionController._pushCall(funcName);
6784
+ }
6753
6785
  return (async () => {
6786
+ try {
6787
+ if (metadata.expression) {
6788
+ const result = await this.evaluateAsync(metadata.body, funcEnv);
6789
+ if (result instanceof ThrowSignal) {
6790
+ throw result.value;
6791
+ }
6792
+ return result;
6793
+ } else {
6794
+ const result = await this.evaluateAsync(metadata.body, funcEnv);
6795
+ if (result instanceof ReturnValue) {
6796
+ return result.value;
6797
+ }
6798
+ if (result instanceof ThrowSignal) {
6799
+ throw result.value;
6800
+ }
6801
+ return void 0;
6802
+ }
6803
+ } finally {
6804
+ if (this.executionController) {
6805
+ this.executionController._popCall();
6806
+ }
6807
+ }
6808
+ })();
6809
+ } else {
6810
+ if (this.executionController) {
6811
+ this.executionController._pushCall(funcName);
6812
+ }
6813
+ try {
6754
6814
  if (metadata.expression) {
6755
- const result = await this.evaluateAsync(metadata.body, funcEnv);
6815
+ const result = this.evaluate(metadata.body, funcEnv);
6756
6816
  if (result instanceof ThrowSignal) {
6757
6817
  throw result.value;
6758
6818
  }
6759
6819
  return result;
6760
6820
  } else {
6761
- const result = await this.evaluateAsync(metadata.body, funcEnv);
6821
+ const result = this.evaluate(metadata.body, funcEnv);
6762
6822
  if (result instanceof ReturnValue) {
6763
6823
  return result.value;
6764
6824
  }
@@ -6767,23 +6827,10 @@ var Interpreter = class _Interpreter {
6767
6827
  }
6768
6828
  return void 0;
6769
6829
  }
6770
- })();
6771
- } else {
6772
- if (metadata.expression) {
6773
- const result = this.evaluate(metadata.body, funcEnv);
6774
- if (result instanceof ThrowSignal) {
6775
- throw result.value;
6776
- }
6777
- return result;
6778
- } else {
6779
- const result = this.evaluate(metadata.body, funcEnv);
6780
- if (result instanceof ReturnValue) {
6781
- return result.value;
6782
- }
6783
- if (result instanceof ThrowSignal) {
6784
- throw result.value;
6830
+ } finally {
6831
+ if (this.executionController) {
6832
+ this.executionController._popCall();
6785
6833
  }
6786
- return void 0;
6787
6834
  }
6788
6835
  }
6789
6836
  }
@@ -7926,7 +7973,8 @@ var WangInterpreter = class {
7926
7973
  }
7927
7974
  const hasTopLevelReturn = this.hasTopLevelReturn(code);
7928
7975
  const options = {
7929
- moduleResolver: this.moduleResolver
7976
+ moduleResolver: this.moduleResolver,
7977
+ executionController: userOptions.executionController
7930
7978
  // sourceType will be auto-detected from code
7931
7979
  };
7932
7980
  let result;
@@ -8005,6 +8053,191 @@ var InMemoryModuleResolver = class {
8005
8053
  }
8006
8054
  };
8007
8055
 
8056
+ // src/runtime/execution-controller.js
8057
+ var ExecutionController = class {
8058
+ constructor() {
8059
+ this.state = "idle";
8060
+ this.pauseRequested = false;
8061
+ this.abortRequested = false;
8062
+ this.stepCount = 0;
8063
+ this.currentNode = null;
8064
+ this.callStack = [];
8065
+ this.currentEnv = null;
8066
+ this._resolveResume = null;
8067
+ }
8068
+ // --- Control methods (called by user) ---
8069
+ /**
8070
+ * Request pause at next checkpoint. Only works during async evaluation.
8071
+ */
8072
+ pause() {
8073
+ if (this.state === "running") {
8074
+ this.pauseRequested = true;
8075
+ }
8076
+ }
8077
+ /**
8078
+ * Resume execution after pause.
8079
+ */
8080
+ resume() {
8081
+ if (this.state === "paused" && this._resolveResume) {
8082
+ this.pauseRequested = false;
8083
+ this._resolveResume();
8084
+ this._resolveResume = null;
8085
+ }
8086
+ }
8087
+ /**
8088
+ * Abort execution. Works for both sync and async evaluation.
8089
+ * If paused, will resume and then abort.
8090
+ */
8091
+ abort() {
8092
+ this.abortRequested = true;
8093
+ if (this._resolveResume) {
8094
+ this._resolveResume();
8095
+ this._resolveResume = null;
8096
+ }
8097
+ }
8098
+ // --- Status (called by user) ---
8099
+ /**
8100
+ * Get current execution status with full debug information.
8101
+ * @returns {Object} Status object with state, stepCount, currentNode, callStack, and variables
8102
+ */
8103
+ getStatus() {
8104
+ var _a;
8105
+ return {
8106
+ state: this.state,
8107
+ stepCount: this.stepCount,
8108
+ currentNode: ((_a = this.currentNode) == null ? void 0 : _a.type) || null,
8109
+ callStack: [...this.callStack],
8110
+ variables: this._getEnvironmentVariables()
8111
+ };
8112
+ }
8113
+ /**
8114
+ * Check if abort has been requested.
8115
+ * Compatible with AbortSignal interface.
8116
+ */
8117
+ get aborted() {
8118
+ return this.abortRequested;
8119
+ }
8120
+ // --- Internal methods (called by interpreter) ---
8121
+ /**
8122
+ * Mark execution as started. Called by execute().
8123
+ * @internal
8124
+ */
8125
+ _start() {
8126
+ this.state = "running";
8127
+ this.stepCount = 0;
8128
+ this.currentNode = null;
8129
+ this.callStack = [];
8130
+ this.pauseRequested = false;
8131
+ }
8132
+ /**
8133
+ * Mark execution as completed. Called by execute().
8134
+ * @internal
8135
+ */
8136
+ _complete() {
8137
+ this.state = "completed";
8138
+ }
8139
+ /**
8140
+ * Push a function call onto the call stack.
8141
+ * @internal
8142
+ */
8143
+ _pushCall(name) {
8144
+ this.callStack.push(name);
8145
+ }
8146
+ /**
8147
+ * Pop a function call from the call stack.
8148
+ * @internal
8149
+ */
8150
+ _popCall() {
8151
+ this.callStack.pop();
8152
+ }
8153
+ /**
8154
+ * Set the current environment for variable introspection.
8155
+ * @internal
8156
+ */
8157
+ _setEnv(env) {
8158
+ this.currentEnv = env;
8159
+ }
8160
+ /**
8161
+ * Async checkpoint - yields if paused, throws if aborted.
8162
+ * Called at coarse granularity points (loops, function calls).
8163
+ * @internal
8164
+ */
8165
+ async _checkpoint(node) {
8166
+ this.stepCount++;
8167
+ this.currentNode = node;
8168
+ if (this.abortRequested) {
8169
+ this.state = "aborted";
8170
+ const error = new Error("The operation was aborted");
8171
+ error.name = "AbortError";
8172
+ throw error;
8173
+ }
8174
+ if (this.pauseRequested && this.state === "running") {
8175
+ this.state = "paused";
8176
+ await new Promise((resolve) => {
8177
+ this._resolveResume = resolve;
8178
+ });
8179
+ this.state = "running";
8180
+ if (this.abortRequested) {
8181
+ this.state = "aborted";
8182
+ const error = new Error("The operation was aborted");
8183
+ error.name = "AbortError";
8184
+ throw error;
8185
+ }
8186
+ }
8187
+ }
8188
+ /**
8189
+ * Sync abort check - only throws if aborted, cannot pause.
8190
+ * Used in sync evaluate() path.
8191
+ * @internal
8192
+ */
8193
+ _checkAbortSync() {
8194
+ if (this.abortRequested) {
8195
+ this.state = "aborted";
8196
+ const error = new Error("The operation was aborted");
8197
+ error.name = "AbortError";
8198
+ throw error;
8199
+ }
8200
+ }
8201
+ /**
8202
+ * Get all variables from current environment chain.
8203
+ * @internal
8204
+ */
8205
+ _getEnvironmentVariables() {
8206
+ if (!this.currentEnv) return {};
8207
+ const vars = {};
8208
+ let env = this.currentEnv;
8209
+ while (env) {
8210
+ if (env.vars) {
8211
+ for (const [key, value] of env.vars) {
8212
+ if (!(key in vars)) {
8213
+ vars[key] = this._serializeValue(value);
8214
+ }
8215
+ }
8216
+ }
8217
+ env = env.parent;
8218
+ }
8219
+ return vars;
8220
+ }
8221
+ /**
8222
+ * Serialize a value for status reporting.
8223
+ * @internal
8224
+ */
8225
+ _serializeValue(value) {
8226
+ if (value === void 0) return { type: "undefined" };
8227
+ if (value === null) return { type: "null" };
8228
+ if (typeof value === "function" || value && value.__isFunction) {
8229
+ return { type: "function", name: value.name || "anonymous" };
8230
+ }
8231
+ if (Array.isArray(value)) {
8232
+ return { type: "array", length: value.length };
8233
+ }
8234
+ if (typeof value === "object") {
8235
+ return { type: "object", preview: Object.keys(value).slice(0, 5) };
8236
+ }
8237
+ return { type: typeof value, value };
8238
+ }
8239
+ };
8240
+
8008
8241
  // src/index.js
8009
8242
  function containsModuleSyntax(code) {
8010
8243
  if (/^\s*(import|export)\s+/m.test(code)) {
@@ -8071,21 +8304,39 @@ function containsTopLevelAwait(node) {
8071
8304
  return false;
8072
8305
  }
8073
8306
  async function execute(code, env = null, options = {}) {
8074
- const ast = parse4(code, options);
8075
- if (!env) {
8076
- env = createGlobalEnvironment(new Environment());
8307
+ const controller = options.executionController;
8308
+ if (controller) {
8309
+ controller._start();
8077
8310
  }
8078
- const interpreter = new Interpreter(env, {
8079
- moduleResolver: options.moduleResolver,
8080
- abortSignal: options.abortSignal
8081
- });
8082
- const needsAsync = options.sourceType === "module" || containsModuleDeclarations(ast) || containsTopLevelAwait(ast);
8083
- if (needsAsync) {
8084
- const result = await interpreter.evaluateAsync(ast, env);
8085
- return result instanceof ReturnValue ? result.value : result;
8086
- } else {
8087
- const result = interpreter.evaluate(ast, env);
8088
- return result instanceof ReturnValue ? result.value : result;
8311
+ try {
8312
+ const ast = parse4(code, options);
8313
+ if (!env) {
8314
+ env = createGlobalEnvironment(new Environment());
8315
+ }
8316
+ const interpreter = new Interpreter(env, {
8317
+ moduleResolver: options.moduleResolver,
8318
+ abortSignal: options.abortSignal,
8319
+ executionController: controller
8320
+ });
8321
+ const needsAsync = options.sourceType === "module" || containsModuleDeclarations(ast) || containsTopLevelAwait(ast) || controller != null;
8322
+ if (needsAsync) {
8323
+ const result = await interpreter.evaluateAsync(ast, env);
8324
+ if (controller) {
8325
+ controller._complete();
8326
+ }
8327
+ return result instanceof ReturnValue ? result.value : result;
8328
+ } else {
8329
+ const result = interpreter.evaluate(ast, env);
8330
+ if (controller) {
8331
+ controller._complete();
8332
+ }
8333
+ return result instanceof ReturnValue ? result.value : result;
8334
+ }
8335
+ } catch (e) {
8336
+ if (controller && e.name === "AbortError") {
8337
+ controller.state = "aborted";
8338
+ }
8339
+ throw e;
8089
8340
  }
8090
8341
  }
8091
8342
  function createEnvironment() {
@@ -8123,6 +8374,7 @@ var ModuleResolver = class {
8123
8374
  // Annotate the CommonJS export names for ESM import in node:
8124
8375
  0 && (module.exports = {
8125
8376
  Environment,
8377
+ ExecutionController,
8126
8378
  InMemoryModuleResolver,
8127
8379
  Interpreter,
8128
8380
  ModuleResolver,