jslike 1.4.4 → 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.js CHANGED
@@ -5784,19 +5784,36 @@ var Interpreter = class _Interpreter {
5784
5784
  this.moduleCache = /* @__PURE__ */ new Map();
5785
5785
  this.moduleExports = {};
5786
5786
  this.abortSignal = options.abortSignal;
5787
+ this.executionController = options.executionController;
5787
5788
  }
5788
- // Check if execution should be aborted
5789
+ // Check if execution should be aborted (sync version)
5789
5790
  checkAbortSignal() {
5791
+ if (this.executionController) {
5792
+ this.executionController._checkAbortSync();
5793
+ return;
5794
+ }
5790
5795
  if (this.abortSignal && this.abortSignal.aborted) {
5791
5796
  const error = new Error("The operation was aborted");
5792
5797
  error.name = "AbortError";
5793
5798
  throw error;
5794
5799
  }
5795
5800
  }
5801
+ // Checkpoint that returns a promise only when controller is present
5802
+ // When no controller, returns null to signal no await needed
5803
+ _getCheckpointPromise(node, env) {
5804
+ if (this.executionController) {
5805
+ this.executionController._setEnv(env);
5806
+ return this.executionController._checkpoint(node);
5807
+ } else {
5808
+ this.checkAbortSignal();
5809
+ return null;
5810
+ }
5811
+ }
5796
5812
  // Async evaluation for async functions - handles await expressions
5797
5813
  async evaluateAsync(node, env) {
5798
5814
  if (!node) return void 0;
5799
- this.checkAbortSignal();
5815
+ const checkpointPromise = this._getCheckpointPromise(node, env);
5816
+ if (checkpointPromise) await checkpointPromise;
5800
5817
  if (node.type === "AwaitExpression") {
5801
5818
  const promise = await this.evaluateAsync(node.argument, env);
5802
5819
  return await promise;
@@ -5967,6 +5984,8 @@ var Interpreter = class _Interpreter {
5967
5984
  await this.evaluateAsync(node.init, forEnv);
5968
5985
  }
5969
5986
  while (!node.test || await this.evaluateAsync(node.test, forEnv)) {
5987
+ const cp1 = this._getCheckpointPromise(node, forEnv);
5988
+ if (cp1) await cp1;
5970
5989
  const result = await this.evaluateAsync(node.body, forEnv);
5971
5990
  if (result instanceof BreakSignal) {
5972
5991
  break;
@@ -5992,6 +6011,8 @@ var Interpreter = class _Interpreter {
5992
6011
  const declarator = node.left.declarations[0];
5993
6012
  const isConst = node.left.kind === "const";
5994
6013
  for (const value of iterable) {
6014
+ const cp2 = this._getCheckpointPromise(node, forEnv);
6015
+ if (cp2) await cp2;
5995
6016
  const iterEnv = forEnv.extend();
5996
6017
  if (declarator.id.type === "Identifier") {
5997
6018
  iterEnv.define(declarator.id.name, value, isConst);
@@ -6022,6 +6043,8 @@ var Interpreter = class _Interpreter {
6022
6043
  const varName = node.left.declarations[0].id.name;
6023
6044
  forEnv.define(varName, void 0);
6024
6045
  for (const key in obj) {
6046
+ const cp3 = this._getCheckpointPromise(node, forEnv);
6047
+ if (cp3) await cp3;
6025
6048
  forEnv.set(varName, key);
6026
6049
  const result = await this.evaluateAsync(node.body, forEnv);
6027
6050
  if (result instanceof BreakSignal) {
@@ -6038,6 +6061,8 @@ var Interpreter = class _Interpreter {
6038
6061
  }
6039
6062
  if (node.type === "WhileStatement") {
6040
6063
  while (await this.evaluateAsync(node.test, env)) {
6064
+ const cp4 = this._getCheckpointPromise(node, env);
6065
+ if (cp4) await cp4;
6041
6066
  const result = await this.evaluateAsync(node.body, env);
6042
6067
  if (result instanceof BreakSignal) {
6043
6068
  break;
@@ -6053,6 +6078,8 @@ var Interpreter = class _Interpreter {
6053
6078
  }
6054
6079
  if (node.type === "DoWhileStatement") {
6055
6080
  do {
6081
+ const cp5 = this._getCheckpointPromise(node, env);
6082
+ if (cp5) await cp5;
6056
6083
  const result = await this.evaluateAsync(node.body, env);
6057
6084
  if (result instanceof BreakSignal) {
6058
6085
  break;
@@ -6695,6 +6722,7 @@ var Interpreter = class _Interpreter {
6695
6722
  callUserFunction(func, args, callingEnv, thisContext = void 0) {
6696
6723
  const metadata = func.__metadata || func;
6697
6724
  const funcEnv = new Environment(metadata.closure);
6725
+ const funcName = metadata.name || func.name || "anonymous";
6698
6726
  if (thisContext !== void 0) {
6699
6727
  funcEnv.define("this", thisContext);
6700
6728
  }
@@ -6717,15 +6745,46 @@ var Interpreter = class _Interpreter {
6717
6745
  }
6718
6746
  }
6719
6747
  if (metadata.async) {
6748
+ if (this.executionController) {
6749
+ this.executionController._pushCall(funcName);
6750
+ }
6720
6751
  return (async () => {
6752
+ try {
6753
+ if (metadata.expression) {
6754
+ const result = await this.evaluateAsync(metadata.body, funcEnv);
6755
+ if (result instanceof ThrowSignal) {
6756
+ throw result.value;
6757
+ }
6758
+ return result;
6759
+ } else {
6760
+ const result = await this.evaluateAsync(metadata.body, funcEnv);
6761
+ if (result instanceof ReturnValue) {
6762
+ return result.value;
6763
+ }
6764
+ if (result instanceof ThrowSignal) {
6765
+ throw result.value;
6766
+ }
6767
+ return void 0;
6768
+ }
6769
+ } finally {
6770
+ if (this.executionController) {
6771
+ this.executionController._popCall();
6772
+ }
6773
+ }
6774
+ })();
6775
+ } else {
6776
+ if (this.executionController) {
6777
+ this.executionController._pushCall(funcName);
6778
+ }
6779
+ try {
6721
6780
  if (metadata.expression) {
6722
- const result = await this.evaluateAsync(metadata.body, funcEnv);
6781
+ const result = this.evaluate(metadata.body, funcEnv);
6723
6782
  if (result instanceof ThrowSignal) {
6724
6783
  throw result.value;
6725
6784
  }
6726
6785
  return result;
6727
6786
  } else {
6728
- const result = await this.evaluateAsync(metadata.body, funcEnv);
6787
+ const result = this.evaluate(metadata.body, funcEnv);
6729
6788
  if (result instanceof ReturnValue) {
6730
6789
  return result.value;
6731
6790
  }
@@ -6734,23 +6793,10 @@ var Interpreter = class _Interpreter {
6734
6793
  }
6735
6794
  return void 0;
6736
6795
  }
6737
- })();
6738
- } else {
6739
- if (metadata.expression) {
6740
- const result = this.evaluate(metadata.body, funcEnv);
6741
- if (result instanceof ThrowSignal) {
6742
- throw result.value;
6743
- }
6744
- return result;
6745
- } else {
6746
- const result = this.evaluate(metadata.body, funcEnv);
6747
- if (result instanceof ReturnValue) {
6748
- return result.value;
6749
- }
6750
- if (result instanceof ThrowSignal) {
6751
- throw result.value;
6796
+ } finally {
6797
+ if (this.executionController) {
6798
+ this.executionController._popCall();
6752
6799
  }
6753
- return void 0;
6754
6800
  }
6755
6801
  }
6756
6802
  }
@@ -7398,6 +7444,12 @@ var Interpreter = class _Interpreter {
7398
7444
  if (Array.isArray(arg)) {
7399
7445
  return { __spread: true, __values: arg };
7400
7446
  }
7447
+ if (typeof arg === "string") {
7448
+ return { __spread: true, __values: [...arg] };
7449
+ }
7450
+ if (arg !== null && arg !== void 0 && typeof arg[Symbol.iterator] === "function") {
7451
+ return { __spread: true, __values: [...arg] };
7452
+ }
7401
7453
  if (typeof arg === "object" && arg !== null) {
7402
7454
  return { __spread: true, __values: Object.entries(arg) };
7403
7455
  }
@@ -7887,7 +7939,8 @@ var WangInterpreter = class {
7887
7939
  }
7888
7940
  const hasTopLevelReturn = this.hasTopLevelReturn(code);
7889
7941
  const options = {
7890
- moduleResolver: this.moduleResolver
7942
+ moduleResolver: this.moduleResolver,
7943
+ executionController: userOptions.executionController
7891
7944
  // sourceType will be auto-detected from code
7892
7945
  };
7893
7946
  let result;
@@ -7966,6 +8019,191 @@ var InMemoryModuleResolver = class {
7966
8019
  }
7967
8020
  };
7968
8021
 
8022
+ // src/runtime/execution-controller.js
8023
+ var ExecutionController = class {
8024
+ constructor() {
8025
+ this.state = "idle";
8026
+ this.pauseRequested = false;
8027
+ this.abortRequested = false;
8028
+ this.stepCount = 0;
8029
+ this.currentNode = null;
8030
+ this.callStack = [];
8031
+ this.currentEnv = null;
8032
+ this._resolveResume = null;
8033
+ }
8034
+ // --- Control methods (called by user) ---
8035
+ /**
8036
+ * Request pause at next checkpoint. Only works during async evaluation.
8037
+ */
8038
+ pause() {
8039
+ if (this.state === "running") {
8040
+ this.pauseRequested = true;
8041
+ }
8042
+ }
8043
+ /**
8044
+ * Resume execution after pause.
8045
+ */
8046
+ resume() {
8047
+ if (this.state === "paused" && this._resolveResume) {
8048
+ this.pauseRequested = false;
8049
+ this._resolveResume();
8050
+ this._resolveResume = null;
8051
+ }
8052
+ }
8053
+ /**
8054
+ * Abort execution. Works for both sync and async evaluation.
8055
+ * If paused, will resume and then abort.
8056
+ */
8057
+ abort() {
8058
+ this.abortRequested = true;
8059
+ if (this._resolveResume) {
8060
+ this._resolveResume();
8061
+ this._resolveResume = null;
8062
+ }
8063
+ }
8064
+ // --- Status (called by user) ---
8065
+ /**
8066
+ * Get current execution status with full debug information.
8067
+ * @returns {Object} Status object with state, stepCount, currentNode, callStack, and variables
8068
+ */
8069
+ getStatus() {
8070
+ var _a;
8071
+ return {
8072
+ state: this.state,
8073
+ stepCount: this.stepCount,
8074
+ currentNode: ((_a = this.currentNode) == null ? void 0 : _a.type) || null,
8075
+ callStack: [...this.callStack],
8076
+ variables: this._getEnvironmentVariables()
8077
+ };
8078
+ }
8079
+ /**
8080
+ * Check if abort has been requested.
8081
+ * Compatible with AbortSignal interface.
8082
+ */
8083
+ get aborted() {
8084
+ return this.abortRequested;
8085
+ }
8086
+ // --- Internal methods (called by interpreter) ---
8087
+ /**
8088
+ * Mark execution as started. Called by execute().
8089
+ * @internal
8090
+ */
8091
+ _start() {
8092
+ this.state = "running";
8093
+ this.stepCount = 0;
8094
+ this.currentNode = null;
8095
+ this.callStack = [];
8096
+ this.pauseRequested = false;
8097
+ }
8098
+ /**
8099
+ * Mark execution as completed. Called by execute().
8100
+ * @internal
8101
+ */
8102
+ _complete() {
8103
+ this.state = "completed";
8104
+ }
8105
+ /**
8106
+ * Push a function call onto the call stack.
8107
+ * @internal
8108
+ */
8109
+ _pushCall(name) {
8110
+ this.callStack.push(name);
8111
+ }
8112
+ /**
8113
+ * Pop a function call from the call stack.
8114
+ * @internal
8115
+ */
8116
+ _popCall() {
8117
+ this.callStack.pop();
8118
+ }
8119
+ /**
8120
+ * Set the current environment for variable introspection.
8121
+ * @internal
8122
+ */
8123
+ _setEnv(env) {
8124
+ this.currentEnv = env;
8125
+ }
8126
+ /**
8127
+ * Async checkpoint - yields if paused, throws if aborted.
8128
+ * Called at coarse granularity points (loops, function calls).
8129
+ * @internal
8130
+ */
8131
+ async _checkpoint(node) {
8132
+ this.stepCount++;
8133
+ this.currentNode = node;
8134
+ if (this.abortRequested) {
8135
+ this.state = "aborted";
8136
+ const error = new Error("The operation was aborted");
8137
+ error.name = "AbortError";
8138
+ throw error;
8139
+ }
8140
+ if (this.pauseRequested && this.state === "running") {
8141
+ this.state = "paused";
8142
+ await new Promise((resolve) => {
8143
+ this._resolveResume = resolve;
8144
+ });
8145
+ this.state = "running";
8146
+ if (this.abortRequested) {
8147
+ this.state = "aborted";
8148
+ const error = new Error("The operation was aborted");
8149
+ error.name = "AbortError";
8150
+ throw error;
8151
+ }
8152
+ }
8153
+ }
8154
+ /**
8155
+ * Sync abort check - only throws if aborted, cannot pause.
8156
+ * Used in sync evaluate() path.
8157
+ * @internal
8158
+ */
8159
+ _checkAbortSync() {
8160
+ if (this.abortRequested) {
8161
+ this.state = "aborted";
8162
+ const error = new Error("The operation was aborted");
8163
+ error.name = "AbortError";
8164
+ throw error;
8165
+ }
8166
+ }
8167
+ /**
8168
+ * Get all variables from current environment chain.
8169
+ * @internal
8170
+ */
8171
+ _getEnvironmentVariables() {
8172
+ if (!this.currentEnv) return {};
8173
+ const vars = {};
8174
+ let env = this.currentEnv;
8175
+ while (env) {
8176
+ if (env.vars) {
8177
+ for (const [key, value] of env.vars) {
8178
+ if (!(key in vars)) {
8179
+ vars[key] = this._serializeValue(value);
8180
+ }
8181
+ }
8182
+ }
8183
+ env = env.parent;
8184
+ }
8185
+ return vars;
8186
+ }
8187
+ /**
8188
+ * Serialize a value for status reporting.
8189
+ * @internal
8190
+ */
8191
+ _serializeValue(value) {
8192
+ if (value === void 0) return { type: "undefined" };
8193
+ if (value === null) return { type: "null" };
8194
+ if (typeof value === "function" || value && value.__isFunction) {
8195
+ return { type: "function", name: value.name || "anonymous" };
8196
+ }
8197
+ if (Array.isArray(value)) {
8198
+ return { type: "array", length: value.length };
8199
+ }
8200
+ if (typeof value === "object") {
8201
+ return { type: "object", preview: Object.keys(value).slice(0, 5) };
8202
+ }
8203
+ return { type: typeof value, value };
8204
+ }
8205
+ };
8206
+
7969
8207
  // src/index.js
7970
8208
  function containsModuleSyntax(code) {
7971
8209
  if (/^\s*(import|export)\s+/m.test(code)) {
@@ -7989,8 +8227,10 @@ function parse4(code, options = {}) {
7989
8227
  sourceType,
7990
8228
  locations: true,
7991
8229
  // Track source locations for better error messages
7992
- allowReturnOutsideFunction: true
8230
+ allowReturnOutsideFunction: true,
7993
8231
  // Allow top-level return statements
8232
+ allowAwaitOutsideFunction: true
8233
+ // Allow top-level await
7994
8234
  });
7995
8235
  } catch (error) {
7996
8236
  throw new SyntaxError(
@@ -8030,21 +8270,39 @@ function containsTopLevelAwait(node) {
8030
8270
  return false;
8031
8271
  }
8032
8272
  async function execute(code, env = null, options = {}) {
8033
- const ast = parse4(code, options);
8034
- if (!env) {
8035
- env = createGlobalEnvironment(new Environment());
8273
+ const controller = options.executionController;
8274
+ if (controller) {
8275
+ controller._start();
8036
8276
  }
8037
- const interpreter = new Interpreter(env, {
8038
- moduleResolver: options.moduleResolver,
8039
- abortSignal: options.abortSignal
8040
- });
8041
- const needsAsync = options.sourceType === "module" || containsModuleDeclarations(ast) || containsTopLevelAwait(ast);
8042
- if (needsAsync) {
8043
- const result = await interpreter.evaluateAsync(ast, env);
8044
- return result instanceof ReturnValue ? result.value : result;
8045
- } else {
8046
- const result = interpreter.evaluate(ast, env);
8047
- return result instanceof ReturnValue ? result.value : result;
8277
+ try {
8278
+ const ast = parse4(code, options);
8279
+ if (!env) {
8280
+ env = createGlobalEnvironment(new Environment());
8281
+ }
8282
+ const interpreter = new Interpreter(env, {
8283
+ moduleResolver: options.moduleResolver,
8284
+ abortSignal: options.abortSignal,
8285
+ executionController: controller
8286
+ });
8287
+ const needsAsync = options.sourceType === "module" || containsModuleDeclarations(ast) || containsTopLevelAwait(ast) || controller != null;
8288
+ if (needsAsync) {
8289
+ const result = await interpreter.evaluateAsync(ast, env);
8290
+ if (controller) {
8291
+ controller._complete();
8292
+ }
8293
+ return result instanceof ReturnValue ? result.value : result;
8294
+ } else {
8295
+ const result = interpreter.evaluate(ast, env);
8296
+ if (controller) {
8297
+ controller._complete();
8298
+ }
8299
+ return result instanceof ReturnValue ? result.value : result;
8300
+ }
8301
+ } catch (e) {
8302
+ if (controller && e.name === "AbortError") {
8303
+ controller.state = "aborted";
8304
+ }
8305
+ throw e;
8048
8306
  }
8049
8307
  }
8050
8308
  function createEnvironment() {
@@ -8081,6 +8339,7 @@ var ModuleResolver = class {
8081
8339
  };
8082
8340
  export {
8083
8341
  Environment,
8342
+ ExecutionController,
8084
8343
  InMemoryModuleResolver,
8085
8344
  Interpreter,
8086
8345
  ModuleResolver,
@@ -5658,8 +5658,10 @@ function parse4(code, options = {}) {
5658
5658
  sourceType,
5659
5659
  locations: true,
5660
5660
  // Track source locations for better error messages
5661
- allowReturnOutsideFunction: true
5661
+ allowReturnOutsideFunction: true,
5662
5662
  // Allow top-level return statements
5663
+ allowAwaitOutsideFunction: true
5664
+ // Allow top-level await
5663
5665
  });
5664
5666
  } catch (error) {
5665
5667
  throw new SyntaxError(
@@ -5633,8 +5633,10 @@ function parse4(code, options = {}) {
5633
5633
  sourceType,
5634
5634
  locations: true,
5635
5635
  // Track source locations for better error messages
5636
- allowReturnOutsideFunction: true
5636
+ allowReturnOutsideFunction: true,
5637
5637
  // Allow top-level return statements
5638
+ allowAwaitOutsideFunction: true
5639
+ // Allow top-level await
5638
5640
  });
5639
5641
  } catch (error) {
5640
5642
  throw new SyntaxError(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jslike",
3
- "version": "1.4.4",
3
+ "version": "1.5.0",
4
4
  "description": "Production-ready JavaScript interpreter with full ES6+ support using Acorn parser",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",