@ricsam/isolate-runtime 0.1.15 → 0.1.17

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.
@@ -670,49 +670,102 @@ function createLocalCustomFunctionsMarshalOptions() {
670
670
  }
671
671
  function createModuleResolver(state) {
672
672
  return async (specifier, referrer) => {
673
- const staticCached = state.staticModuleCache.get(specifier);
673
+ const importerPath = state.moduleToFilename.get(referrer) ?? "<unknown>";
674
+ const importerResolveDir = import_node_path.default.posix.dirname(importerPath);
675
+ const importerStack = state.moduleImportChain.get(importerPath) ?? [];
676
+ const resolvedSpecifier = specifier.startsWith(".") ? import_node_path.default.posix.normalize(import_node_path.default.posix.join(importerResolveDir, specifier)) : specifier;
677
+ state.specifierToImporter.set(resolvedSpecifier, importerPath);
678
+ const staticCached = state.staticModuleCache.get(resolvedSpecifier);
674
679
  if (staticCached)
675
680
  return staticCached;
676
- const cached = state.moduleCache.get(specifier);
681
+ const cached = state.moduleCache.get(resolvedSpecifier);
677
682
  if (cached)
678
683
  return cached;
679
684
  if (!state.moduleLoader) {
680
685
  throw new Error(`No module loader registered. Cannot import: ${specifier}`);
681
686
  }
682
- const importerPath = state.moduleToFilename.get(referrer) ?? "<unknown>";
683
- const importerResolveDir = import_node_path.default.posix.dirname(importerPath);
684
687
  const result = await state.moduleLoader(specifier, {
685
688
  path: importerPath,
686
689
  resolveDir: importerResolveDir
687
690
  });
688
691
  const { code, resolveDir } = result;
692
+ if (result.filename.includes("/")) {
693
+ throw new Error(`moduleLoader returned a filename with slashes: "${result.filename}". ` + `filename must be a basename (e.g. "utils.js"), not a path.`);
694
+ }
695
+ const resolvedFilename = import_node_path.default.posix.join(resolveDir, result.filename);
689
696
  const hash = import_isolate_transform.contentHash(code);
690
- const cacheKey = `${specifier}:${hash}`;
697
+ const cacheKey = `${resolvedSpecifier}:${hash}`;
698
+ const inFlightKey = `${result.static ? "static" : "dynamic"}:${cacheKey}`;
699
+ const staticCachedAfterLoad = state.staticModuleCache.get(resolvedSpecifier);
700
+ if (staticCachedAfterLoad)
701
+ return staticCachedAfterLoad;
702
+ const cachedAfterLoad = state.moduleCache.get(resolvedSpecifier);
703
+ if (cachedAfterLoad)
704
+ return cachedAfterLoad;
691
705
  const hashCached = state.moduleCache.get(cacheKey);
692
706
  if (hashCached)
693
707
  return hashCached;
694
- let transformed = state.transformCache.get(hash);
695
- if (!transformed) {
696
- transformed = await import_isolate_transform.transformModuleCode(code, specifier);
697
- state.transformCache.set(hash, transformed);
698
- }
699
- if (transformed.sourceMap) {
700
- state.sourceMaps.set(specifier, transformed.sourceMap);
701
- }
702
- const mod = await state.isolate.compileModule(transformed.code, {
703
- filename: specifier
704
- });
705
- const resolvedPath = import_node_path.default.posix.join(resolveDir, import_node_path.default.posix.basename(specifier));
706
- state.moduleToFilename.set(mod, resolvedPath);
707
- if (result.static) {
708
- state.staticModuleCache.set(specifier, mod);
709
- } else {
710
- state.moduleCache.set(specifier, mod);
711
- state.moduleCache.set(cacheKey, mod);
708
+ const inFlight = state.moduleLoadsInFlight.get(inFlightKey);
709
+ if (inFlight)
710
+ return inFlight;
711
+ const loadPromise = (async () => {
712
+ let mod;
713
+ try {
714
+ let transformed = state.transformCache.get(hash);
715
+ if (!transformed) {
716
+ transformed = await import_isolate_transform.transformModuleCode(code, resolvedSpecifier);
717
+ state.transformCache.set(hash, transformed);
718
+ }
719
+ if (transformed.sourceMap) {
720
+ state.sourceMaps.set(resolvedSpecifier, transformed.sourceMap);
721
+ }
722
+ mod = await state.isolate.compileModule(transformed.code, {
723
+ filename: resolvedSpecifier
724
+ });
725
+ state.moduleToFilename.set(mod, resolvedFilename);
726
+ state.moduleImportChain.set(resolvedFilename, [...importerStack, importerPath]);
727
+ if (result.static) {
728
+ state.staticModuleCache.set(resolvedSpecifier, mod);
729
+ } else {
730
+ state.moduleCache.set(resolvedSpecifier, mod);
731
+ state.moduleCache.set(cacheKey, mod);
732
+ }
733
+ return mod;
734
+ } catch (err) {
735
+ const error = err instanceof Error ? err : new Error(String(err));
736
+ error.message = `Failed to compile module "${resolvedSpecifier}" (imported by "${importerPath}"):
737
+ ${error.message}`;
738
+ if (importerStack.length > 0) {
739
+ error.message += `
740
+
741
+ Import chain:
742
+ ${[...importerStack, importerPath].join(`
743
+ -> `)}`;
744
+ }
745
+ if (mod) {
746
+ state.moduleToFilename.delete(mod);
747
+ if (result.static) {
748
+ if (state.staticModuleCache.get(resolvedSpecifier) === mod) {
749
+ state.staticModuleCache.delete(resolvedSpecifier);
750
+ }
751
+ } else {
752
+ if (state.moduleCache.get(resolvedSpecifier) === mod) {
753
+ state.moduleCache.delete(resolvedSpecifier);
754
+ }
755
+ if (state.moduleCache.get(cacheKey) === mod) {
756
+ state.moduleCache.delete(cacheKey);
757
+ }
758
+ }
759
+ }
760
+ throw error;
761
+ }
762
+ })();
763
+ state.moduleLoadsInFlight.set(inFlightKey, loadPromise);
764
+ try {
765
+ return await loadPromise;
766
+ } finally {
767
+ state.moduleLoadsInFlight.delete(inFlightKey);
712
768
  }
713
- const resolver = createModuleResolver(state);
714
- await mod.instantiate(state.context, resolver);
715
- return mod;
716
769
  };
717
770
  }
718
771
  function convertFetchCallback(callback) {
@@ -738,10 +791,14 @@ async function createRuntime(options) {
738
791
  handles: {},
739
792
  moduleCache: new Map,
740
793
  staticModuleCache: new Map,
794
+ moduleLoadsInFlight: new Map,
741
795
  transformCache: new Map,
742
796
  moduleToFilename: new Map,
743
797
  sourceMaps: new Map,
798
+ moduleImportChain: new Map,
799
+ specifierToImporter: new Map,
744
800
  pendingCallbacks: [],
801
+ evalChain: Promise.resolve(),
745
802
  moduleLoader: opts.moduleLoader,
746
803
  customFunctions: opts.customFunctions
747
804
  };
@@ -877,6 +934,18 @@ async function createRuntime(options) {
877
934
  throw new Error("Fetch handle not available");
878
935
  }
879
936
  return state.handles.fetch.onClientWebSocketCommand(callback);
937
+ },
938
+ onEvent(callback) {
939
+ if (!state.handles.fetch) {
940
+ throw new Error("Fetch handle not available");
941
+ }
942
+ return state.handles.fetch.onEvent(callback);
943
+ },
944
+ dispatchEvent(event, payload) {
945
+ if (!state.handles.fetch) {
946
+ throw new Error("Fetch handle not available");
947
+ }
948
+ state.handles.fetch.dispatchEvent(event, payload);
880
949
  }
881
950
  };
882
951
  const timersHandle = {
@@ -961,45 +1030,101 @@ async function createRuntime(options) {
961
1030
  testEnvironment: testEnvironmentHandle,
962
1031
  playwright: playwrightHandle,
963
1032
  async eval(code, filenameOrOptions) {
964
- const options2 = typeof filenameOrOptions === "string" ? { filename: filenameOrOptions } : filenameOrOptions;
965
- const filename = import_isolate_protocol.normalizeEntryFilename(options2?.filename);
966
- try {
967
- const transformed = await import_isolate_transform.transformEntryCode(code, filename);
968
- if (transformed.sourceMap) {
969
- state.sourceMaps.set(filename, transformed.sourceMap);
970
- }
971
- const mod = await state.isolate.compileModule(transformed.code, {
972
- filename
973
- });
974
- state.moduleToFilename.set(mod, filename);
975
- const resolver = createModuleResolver(state);
976
- await mod.instantiate(state.context, resolver);
977
- await mod.evaluate();
978
- const ns = mod.namespace;
979
- const runRef = await ns.get("default", { reference: true });
1033
+ const runEval = async () => {
1034
+ const options2 = typeof filenameOrOptions === "string" ? { filename: filenameOrOptions } : filenameOrOptions;
1035
+ const filename = import_isolate_protocol.normalizeEntryFilename(options2?.filename);
980
1036
  try {
981
- await runRef.apply(undefined, [], {
982
- result: { promise: true },
983
- ...options2?.maxExecutionMs ? { timeout: options2.maxExecutionMs } : {}
1037
+ const transformed = await import_isolate_transform.transformEntryCode(code, filename);
1038
+ if (transformed.sourceMap) {
1039
+ state.sourceMaps.set(filename, transformed.sourceMap);
1040
+ }
1041
+ const mod = await state.isolate.compileModule(transformed.code, {
1042
+ filename
984
1043
  });
985
- } finally {
986
- runRef.release();
987
- }
988
- if (state.pendingCallbacks.length > 0) {
989
- await Promise.all(state.pendingCallbacks);
990
- state.pendingCallbacks.length = 0;
991
- }
992
- } catch (err) {
993
- const error = err;
994
- if (error.stack && state.sourceMaps.size > 0) {
995
- error.stack = import_isolate_transform.mapErrorStack(error.stack, state.sourceMaps);
1044
+ state.moduleToFilename.set(mod, filename);
1045
+ state.moduleImportChain.set(filename, []);
1046
+ const resolver = createModuleResolver(state);
1047
+ try {
1048
+ await mod.instantiate(state.context, resolver);
1049
+ } catch (err) {
1050
+ const error = err instanceof Error ? err : new Error(String(err));
1051
+ const specifierMatch = error.message.match(/The requested module '([^']+)'/);
1052
+ const exportMatch = error.message.match(/export named '([^']+)'/);
1053
+ const failingSpecifier = specifierMatch?.[1];
1054
+ const failingExport = exportMatch?.[1];
1055
+ const importerFile = failingSpecifier ? state.specifierToImporter.get(failingSpecifier) : undefined;
1056
+ const details = [];
1057
+ if (importerFile) {
1058
+ const chain = state.moduleImportChain.get(importerFile) ?? [];
1059
+ const fullChain = [...chain, importerFile];
1060
+ if (failingSpecifier)
1061
+ fullChain.push(failingSpecifier);
1062
+ const trimmed = fullChain.length > 12 ? fullChain.slice(-12) : fullChain;
1063
+ const prefix = fullChain.length > 12 ? ` ...
1064
+ ` : "";
1065
+ details.push(`Import chain:
1066
+ ` + prefix + trimmed.map((p) => ` ${p}`).join(`
1067
+ -> `));
1068
+ } else if (failingSpecifier) {
1069
+ for (const [modPath, chain] of state.moduleImportChain) {
1070
+ if (modPath.includes(failingSpecifier) || modPath.endsWith(failingSpecifier)) {
1071
+ const fullChain = [...chain, modPath];
1072
+ const trimmed = fullChain.length > 12 ? fullChain.slice(-12) : fullChain;
1073
+ details.push(`Import chain:
1074
+ ` + trimmed.map((p) => ` ${p}`).join(`
1075
+ -> `));
1076
+ break;
1077
+ }
1078
+ }
1079
+ }
1080
+ if (failingExport && failingSpecifier) {
1081
+ details.push(`Hint: If '${failingExport}' is a TypeScript type/interface, use \`import type\` to prevent it from being resolved at runtime:
1082
+ ` + ` import type { ${failingExport} } from '${failingSpecifier}';`);
1083
+ }
1084
+ const suffix = details.length > 0 ? `
1085
+
1086
+ ` + details.join(`
1087
+
1088
+ `) : "";
1089
+ error.message = `Module instantiation failed: ${error.message}${suffix}`;
1090
+ throw error;
1091
+ }
1092
+ await mod.evaluate();
1093
+ const ns = mod.namespace;
1094
+ const runRef = await ns.get("default", { reference: true });
1095
+ try {
1096
+ await runRef.apply(undefined, [], {
1097
+ result: { promise: true }
1098
+ });
1099
+ } finally {
1100
+ runRef.release();
1101
+ }
1102
+ if (state.pendingCallbacks.length > 0) {
1103
+ await Promise.all(state.pendingCallbacks);
1104
+ state.pendingCallbacks.length = 0;
1105
+ }
1106
+ } catch (err) {
1107
+ const error = err;
1108
+ if (error.stack && state.sourceMaps.size > 0) {
1109
+ error.stack = import_isolate_transform.mapErrorStack(error.stack, state.sourceMaps);
1110
+ }
1111
+ throw error;
996
1112
  }
997
- throw error;
998
- }
1113
+ };
1114
+ const queuedEval = state.evalChain.then(runEval, runEval);
1115
+ state.evalChain = queuedEval.then(() => {
1116
+ return;
1117
+ }, () => {
1118
+ return;
1119
+ });
1120
+ return queuedEval;
999
1121
  },
1000
1122
  clearModuleCache() {
1001
1123
  state.moduleCache.clear();
1124
+ state.moduleLoadsInFlight.clear();
1002
1125
  state.moduleToFilename.clear();
1126
+ state.moduleImportChain.clear();
1127
+ state.specifierToImporter.clear();
1003
1128
  state.sourceMaps.clear();
1004
1129
  },
1005
1130
  async dispose() {
@@ -1017,10 +1142,11 @@ async function createRuntime(options) {
1017
1142
  state.handles.console?.dispose();
1018
1143
  state.handles.core?.dispose();
1019
1144
  state.moduleCache.clear();
1145
+ state.moduleLoadsInFlight.clear();
1020
1146
  state.context.release();
1021
1147
  state.isolate.dispose();
1022
1148
  }
1023
1149
  };
1024
1150
  }
1025
1151
 
1026
- //# debugId=6EFB8447C7EE485F64756E2164756E21
1152
+ //# debugId=AFA261ED8389256764756E2164756E21