@ms-cloudpack/esm-stub-utilities 0.6.26 → 0.7.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.
Files changed (51) hide show
  1. package/lib/__fixtures__/loadStubWorker.d.ts +3 -0
  2. package/lib/__fixtures__/loadStubWorker.d.ts.map +1 -0
  3. package/lib/__fixtures__/loadStubWorker.js +122 -0
  4. package/lib/__fixtures__/loadStubWorker.js.map +1 -0
  5. package/lib/__fixtures__/tryImportStub.d.ts +44 -0
  6. package/lib/__fixtures__/tryImportStub.d.ts.map +1 -0
  7. package/lib/__fixtures__/tryImportStub.js +79 -0
  8. package/lib/__fixtures__/tryImportStub.js.map +1 -0
  9. package/lib/createESMStub.d.ts +11 -5
  10. package/lib/createESMStub.d.ts.map +1 -1
  11. package/lib/createESMStub.js +42 -52
  12. package/lib/createESMStub.js.map +1 -1
  13. package/lib/index.d.ts +1 -1
  14. package/lib/index.d.ts.map +1 -1
  15. package/lib/index.js +0 -1
  16. package/lib/index.js.map +1 -1
  17. package/lib/runInSandbox.d.ts +10 -7
  18. package/lib/runInSandbox.d.ts.map +1 -1
  19. package/lib/runInSandbox.js +139 -183
  20. package/lib/runInSandbox.js.map +1 -1
  21. package/lib/types/SandboxWorkerResponse.d.ts +21 -0
  22. package/lib/types/SandboxWorkerResponse.d.ts.map +1 -0
  23. package/lib/types/SandboxWorkerResponse.js +2 -0
  24. package/lib/types/SandboxWorkerResponse.js.map +1 -0
  25. package/lib/types/StubError.d.ts +27 -0
  26. package/lib/types/StubError.d.ts.map +1 -0
  27. package/lib/types/StubError.js +2 -0
  28. package/lib/types/StubError.js.map +1 -0
  29. package/lib/types/StubExportInfo.d.ts +18 -0
  30. package/lib/types/StubExportInfo.d.ts.map +1 -0
  31. package/lib/types/StubExportInfo.js +2 -0
  32. package/lib/types/StubExportInfo.js.map +1 -0
  33. package/lib/worker/globals.d.ts +8 -0
  34. package/lib/worker/globals.d.ts.map +1 -0
  35. package/lib/worker/globals.js +22 -0
  36. package/lib/worker/globals.js.map +1 -0
  37. package/lib/worker/initBrowserEnvironment.d.ts +5 -0
  38. package/lib/worker/initBrowserEnvironment.d.ts.map +1 -0
  39. package/lib/worker/initBrowserEnvironment.js +67 -0
  40. package/lib/worker/initBrowserEnvironment.js.map +1 -0
  41. package/lib/worker/worker.d.ts +2 -0
  42. package/lib/worker/worker.d.ts.map +1 -0
  43. package/lib/worker/worker.js +85 -0
  44. package/lib/worker/worker.js.map +1 -0
  45. package/lib/writeESMStub.d.ts +5 -1
  46. package/lib/writeESMStub.d.ts.map +1 -1
  47. package/lib/writeESMStub.js +21 -17
  48. package/lib/writeESMStub.js.map +1 -1
  49. package/package.json +5 -5
  50. package/browserEnvironment/index.cjs +0 -81
  51. package/browserEnvironment/typings.d.ts +0 -5
@@ -0,0 +1,3 @@
1
+ export type StubImportRequest = import('./tryImportStub.js').StubImportRequest;
2
+ export type StubImportResponse = import('./tryImportStub.js').StubImportResponse;
3
+ //# sourceMappingURL=loadStubWorker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadStubWorker.d.ts","sourceRoot":"","sources":["../../src/__fixtures__/loadStubWorker.js"],"names":[],"mappings":"gCAQa,OAAO,oBAAoB,EAAE,iBAAiB;iCAC9C,OAAO,oBAAoB,EAAE,kBAAkB"}
@@ -0,0 +1,122 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
3
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
4
+ // This file is used in tests to verify that a stub file can be imported in a Node environment
5
+ // without interference from Jest's wrappers and transpilation.
6
+ /**
7
+ * @typedef {import('./tryImportStub.js').StubImportRequest} StubImportRequest
8
+ * @typedef {import('./tryImportStub.js').StubImportResponse} StubImportResponse
9
+ */
10
+ /** */
11
+ import { format } from 'pretty-format';
12
+ import v8 from 'v8';
13
+ import { isMainThread, parentPort } from 'worker_threads';
14
+ import { initBrowserEnvironment } from '../worker/initBrowserEnvironment.js';
15
+ import { cleanUpGlobals, getGlobalProperties } from '../worker/globals.js';
16
+ if (isMainThread || !parentPort) {
17
+ throw new Error('This file should only be loaded in a worker thread');
18
+ }
19
+ // These modules may expect a browser environment
20
+ await initBrowserEnvironment();
21
+ const initialGlobalProperties = getGlobalProperties();
22
+ /**
23
+ * Type helper for sending messages to the parent.
24
+ * @param {StubImportResponse} message
25
+ */
26
+ function emitMessage(message) {
27
+ parentPort?.postMessage(message);
28
+ }
29
+ parentPort.on('message', (/** @type {StubImportRequest} */ request) => {
30
+ if (typeof request.stubPath !== 'string') {
31
+ emitMessage({
32
+ type: 'error',
33
+ message: `Unexpected request format: ${JSON.stringify(request)}`,
34
+ });
35
+ }
36
+ else {
37
+ // Import the stub and send a reponse
38
+ // (`void` ignores the promise result since callbacks can't be async)
39
+ void tryImportStub(request);
40
+ }
41
+ });
42
+ /** @param {StubImportRequest} request */
43
+ async function tryImportStub(request) {
44
+ const { stubPath, testCode } = request;
45
+ let moduleExports;
46
+ let step = 'Import';
47
+ try {
48
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
49
+ moduleExports = await import(stubPath);
50
+ step = 'Running test code';
51
+ if (testCode) {
52
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
53
+ new Function('moduleExports', testCode)(moduleExports);
54
+ }
55
+ }
56
+ catch (err) {
57
+ emitMessage({
58
+ type: 'error',
59
+ message: `${step} failed: ${ /** @type {Error} */(err).stack || String(err)}`,
60
+ });
61
+ return;
62
+ }
63
+ finally {
64
+ cleanUpGlobals(initialGlobalProperties);
65
+ }
66
+ try {
67
+ emitMessage({
68
+ type: 'result',
69
+ value: toSerializable(moduleExports),
70
+ });
71
+ }
72
+ catch (err) {
73
+ emitMessage({
74
+ type: 'error',
75
+ message: `Error formatting or serializing exports: ${ /** @type {Error} */(err).stack || String(err)}`,
76
+ });
77
+ }
78
+ }
79
+ /**
80
+ * Convert a value into an IPC-serializable form.
81
+ * @param {*} value
82
+ * @returns {*}
83
+ */
84
+ function toSerializable(value, depth = 1, encountered = new WeakSet()) {
85
+ if (depth > 3) {
86
+ return '...';
87
+ }
88
+ if (value && typeof value === 'object') {
89
+ // track encountered objects to avoid entering circular references
90
+ if (encountered.has(value)) {
91
+ return '[Circular]';
92
+ }
93
+ encountered.add(value);
94
+ if (Array.isArray(value)) {
95
+ return value.map((v) => toSerializable(v, depth + 1, encountered));
96
+ }
97
+ if (Object.keys(value).length || value.constructor?.name === 'Object') {
98
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, toSerializable(v, depth + 1, encountered)]));
99
+ }
100
+ // Always format other objects (even if they're serializable) to avoid comparison issues
101
+ // in expect(...).toEqual() (like "TypeError: Method Map.prototype.entries called on incompatible receiver #<Map>")
102
+ return formatValue(value);
103
+ }
104
+ try {
105
+ // try to serialize the same way postMessage does, per
106
+ // https://nodejs.org/api/worker_threads.html#portpostmessagevalue-transferlist
107
+ v8.serialize(value);
108
+ return value;
109
+ }
110
+ catch {
111
+ // not serializable, so format the value instead
112
+ return formatValue(value);
113
+ }
114
+ }
115
+ /**
116
+ * Pretty-format a value, removing newlines.
117
+ * (`pretty-format` has a `min` option, but it removes additional detail which isn't desired.)
118
+ */
119
+ function formatValue(/** @type {*} */ value) {
120
+ return format(value, {}).replace(/\n/g, '').replace(/\s+/g, ' ');
121
+ }
122
+ //# sourceMappingURL=loadStubWorker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadStubWorker.js","sourceRoot":"","sources":["../../src/__fixtures__/loadStubWorker.js"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-unsafe-return */\n/* eslint-disable @typescript-eslint/no-unsafe-argument */\n\n// This file is used in tests to verify that a stub file can be imported in a Node environment\n// without interference from Jest's wrappers and transpilation.\n\n/**\n * @typedef {import('./tryImportStub.js').StubImportRequest} StubImportRequest\n * @typedef {import('./tryImportStub.js').StubImportResponse} StubImportResponse\n */\n/** */\nimport { format } from 'pretty-format';\nimport v8 from 'v8';\nimport { isMainThread, parentPort } from 'worker_threads';\nimport { initBrowserEnvironment } from '../worker/initBrowserEnvironment.js';\nimport { cleanUpGlobals, getGlobalProperties } from '../worker/globals.js';\n\nif (isMainThread || !parentPort) {\n throw new Error('This file should only be loaded in a worker thread');\n}\n\n// These modules may expect a browser environment\nawait initBrowserEnvironment();\nconst initialGlobalProperties = getGlobalProperties();\n\n/**\n * Type helper for sending messages to the parent.\n * @param {StubImportResponse} message\n */\nfunction emitMessage(message) {\n parentPort?.postMessage(message);\n}\n\nparentPort.on('message', (/** @type {StubImportRequest} */ request) => {\n if (typeof request.stubPath !== 'string') {\n emitMessage({\n type: 'error',\n message: `Unexpected request format: ${JSON.stringify(request)}`,\n });\n } else {\n // Import the stub and send a reponse\n // (`void` ignores the promise result since callbacks can't be async)\n void tryImportStub(request);\n }\n});\n\n/** @param {StubImportRequest} request */\nasync function tryImportStub(request) {\n const { stubPath, testCode } = request;\n let moduleExports;\n let step = 'Import';\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n moduleExports = await import(stubPath);\n step = 'Running test code';\n if (testCode) {\n // eslint-disable-next-line @typescript-eslint/no-implied-eval\n new Function('moduleExports', testCode)(moduleExports);\n }\n } catch (err) {\n emitMessage({\n type: 'error',\n message: `${step} failed: ${/** @type {Error} */ (err).stack || String(err)}`,\n });\n return;\n } finally {\n cleanUpGlobals(initialGlobalProperties);\n }\n\n try {\n emitMessage({\n type: 'result',\n value: toSerializable(moduleExports),\n });\n } catch (err) {\n emitMessage({\n type: 'error',\n message: `Error formatting or serializing exports: ${/** @type {Error} */ (err).stack || String(err)}`,\n });\n }\n}\n\n/**\n * Convert a value into an IPC-serializable form.\n * @param {*} value\n * @returns {*}\n */\nfunction toSerializable(value, depth = 1, encountered = new WeakSet()) {\n if (depth > 3) {\n return '...';\n }\n\n if (value && typeof value === 'object') {\n // track encountered objects to avoid entering circular references\n if (encountered.has(value)) {\n return '[Circular]';\n }\n encountered.add(value);\n\n if (Array.isArray(value)) {\n return value.map((v) => toSerializable(v, depth + 1, encountered));\n }\n\n if (Object.keys(value).length || value.constructor?.name === 'Object') {\n return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, toSerializable(v, depth + 1, encountered)]));\n }\n\n // Always format other objects (even if they're serializable) to avoid comparison issues\n // in expect(...).toEqual() (like \"TypeError: Method Map.prototype.entries called on incompatible receiver #<Map>\")\n return formatValue(value);\n }\n\n try {\n // try to serialize the same way postMessage does, per\n // https://nodejs.org/api/worker_threads.html#portpostmessagevalue-transferlist\n v8.serialize(value);\n return value;\n } catch {\n // not serializable, so format the value instead\n return formatValue(value);\n }\n}\n\n/**\n * Pretty-format a value, removing newlines.\n * (`pretty-format` has a `min` option, but it removes additional detail which isn't desired.)\n */\nfunction formatValue(/** @type {*} */ value) {\n return format(value, {}).replace(/\\n/g, '').replace(/\\s+/g, ' ');\n}\n"]}
@@ -0,0 +1,44 @@
1
+ export type StubImportRequest = {
2
+ stubPath: string;
3
+ /**
4
+ * Test function body which receives a parameter `moduleExports`.
5
+ * The result is not returned, but this can be used to verify that an expected API is available
6
+ * (code calling it doesn't throw) even if it doesn't show up in the named exports.
7
+ */
8
+ testCode?: string;
9
+ };
10
+ type StubImportResult = {
11
+ type: 'result';
12
+ /**
13
+ * IPC-friendly result of importing the module.
14
+ * Any non-IPC-friendy values (such as functions) will be pretty-printed as strings.
15
+ */
16
+ value: unknown;
17
+ };
18
+ export type StubImportResponse = StubImportResult | {
19
+ type: 'error';
20
+ message: string;
21
+ };
22
+ /**
23
+ * Initialize a worker used for importing stubs.
24
+ * (The worker is reused between tests for now because it's expensive to set up and tear down;
25
+ * this can be changed later for certain tests if needed.)
26
+ */
27
+ export declare function ensureStubWorker(): void;
28
+ /**
29
+ * Import the stub in a worker and return the result.
30
+ * Note that non-IPC-friendly values (such as functions) will be pretty-printed as strings.
31
+ *
32
+ * Why use a worker?
33
+ * - Ensure that the module is imported in a "vanilla" way, without interference from Jest
34
+ * transpilation or custom module resolution
35
+ * - Allow the module to run in a simulated browser environment if needed
36
+ * - Avoid potential pollution of the current process's globals
37
+ *
38
+ * (The worker is reused between tests for now because it's expensive to set up and tear down;
39
+ * this can be changed later for certain tests if needed.)
40
+ */
41
+ export declare function tryImportStub(params: StubImportRequest): Promise<unknown>;
42
+ export declare function cleanUpStubWorker(): Promise<void>;
43
+ export {};
44
+ //# sourceMappingURL=tryImportStub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tryImportStub.d.ts","sourceRoot":"","sources":["../../src/__fixtures__/tryImportStub.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,QAAQ,CAAC;IACf;;;OAGG;IACH,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AACF,MAAM,MAAM,kBAAkB,GAAG,gBAAgB,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAOvF;;;;GAIG;AACH,wBAAgB,gBAAgB,SAoB/B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,CAuB/E;AAED,wBAAsB,iBAAiB,kBAWtC"}
@@ -0,0 +1,79 @@
1
+ import path from 'path';
2
+ import { fileURLToPath } from 'url';
3
+ import { Worker } from 'worker_threads';
4
+ const dirname = path.dirname(fileURLToPath(import.meta.url));
5
+ const testWorkerPath = path.join(dirname, 'loadStubWorker.js');
6
+ let worker;
7
+ /**
8
+ * Initialize a worker used for importing stubs.
9
+ * (The worker is reused between tests for now because it's expensive to set up and tear down;
10
+ * this can be changed later for certain tests if needed.)
11
+ */
12
+ export function ensureStubWorker() {
13
+ if (worker)
14
+ return;
15
+ worker = new Worker(testWorkerPath);
16
+ // An 'error' event from a worker means an unhandled exception and it's about to exit.
17
+ // Don't throw an error in this fallback handler in case the error occurred during a call
18
+ // to `tryImportStub` (which has its own error handler that can fail the calling test),
19
+ // but save the error to be included in the 'exit' handler's error message.
20
+ let lastError;
21
+ worker.on('error', (err) => {
22
+ lastError = err;
23
+ });
24
+ worker.on('exit', (code) => {
25
+ throw new Error(lastError
26
+ ? `Worker exited due to an uncaught exception: ${lastError.stack}`
27
+ : `Worker exited unexpectedly with code ${code || 0}`);
28
+ });
29
+ }
30
+ /**
31
+ * Import the stub in a worker and return the result.
32
+ * Note that non-IPC-friendly values (such as functions) will be pretty-printed as strings.
33
+ *
34
+ * Why use a worker?
35
+ * - Ensure that the module is imported in a "vanilla" way, without interference from Jest
36
+ * transpilation or custom module resolution
37
+ * - Allow the module to run in a simulated browser environment if needed
38
+ * - Avoid potential pollution of the current process's globals
39
+ *
40
+ * (The worker is reused between tests for now because it's expensive to set up and tear down;
41
+ * this can be changed later for certain tests if needed.)
42
+ */
43
+ export async function tryImportStub(params) {
44
+ ensureStubWorker();
45
+ return new Promise((resolve, reject) => {
46
+ const onMessage = (response) => {
47
+ worker?.off('error', onError);
48
+ if (response.type === 'error') {
49
+ // error importing the module
50
+ reject(response.message);
51
+ }
52
+ else {
53
+ resolve(response.value);
54
+ }
55
+ };
56
+ // unhandled exception (not expected)
57
+ const onError = (err) => {
58
+ worker?.off('message', onMessage);
59
+ reject(err);
60
+ };
61
+ worker?.once('message', onMessage);
62
+ worker?.once('error', onError);
63
+ worker?.postMessage(params);
64
+ });
65
+ }
66
+ export async function cleanUpStubWorker() {
67
+ if (worker) {
68
+ // Remove all listeners so that the exit handler set up during initialization doesn't throw
69
+ worker.removeAllListeners();
70
+ try {
71
+ await worker.terminate();
72
+ }
73
+ catch {
74
+ // ignore
75
+ }
76
+ worker = undefined;
77
+ }
78
+ }
79
+ //# sourceMappingURL=tryImportStub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tryImportStub.js","sourceRoot":"","sources":["../../src/__fixtures__/tryImportStub.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAwBxC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7D,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;AAE/D,IAAI,MAA0B,CAAC;AAE/B;;;;GAIG;AACH,MAAM,UAAU,gBAAgB;IAC9B,IAAI,MAAM;QAAE,OAAO;IAEnB,MAAM,GAAG,IAAI,MAAM,CAAC,cAAc,CAAC,CAAC;IAEpC,sFAAsF;IACtF,yFAAyF;IACzF,uFAAuF;IACvF,2EAA2E;IAC3E,IAAI,SAA4B,CAAC;IACjC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACzB,SAAS,GAAG,GAAG,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QACzB,MAAM,IAAI,KAAK,CACb,SAAS;YACP,CAAC,CAAC,+CAA+C,SAAS,CAAC,KAAK,EAAE;YAClE,CAAC,CAAC,wCAAwC,IAAI,IAAI,CAAC,EAAE,CACxD,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAyB;IAC3D,gBAAgB,EAAE,CAAC;IAEnB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,SAAS,GAAG,CAAC,QAA4B,EAAE,EAAE;YACjD,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9B,IAAI,QAAQ,CAAC,IAAI,KAAK,OAAO,EAAE;gBAC7B,6BAA6B;gBAC7B,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;aAC1B;iBAAM;gBACL,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;aACzB;QACH,CAAC,CAAC;QACF,qCAAqC;QACrC,MAAM,OAAO,GAAG,CAAC,GAAU,EAAE,EAAE;YAC7B,MAAM,EAAE,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YAClC,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC;QACF,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACnC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAE/B,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,IAAI,MAAM,EAAE;QACV,2FAA2F;QAC3F,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAC5B,IAAI;YACF,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;SAC1B;QAAC,MAAM;YACN,SAAS;SACV;QACD,MAAM,GAAG,SAAS,CAAC;KACpB;AACH,CAAC","sourcesContent":["import path from 'path';\nimport { fileURLToPath } from 'url';\nimport { Worker } from 'worker_threads';\n\n// this type is used by loadStubWorker.js\n// (it's test-specific, so no reason to put it in the types folder)\nexport type StubImportRequest = {\n stubPath: string;\n /**\n * Test function body which receives a parameter `moduleExports`.\n * The result is not returned, but this can be used to verify that an expected API is available\n * (code calling it doesn't throw) even if it doesn't show up in the named exports.\n */\n testCode?: string;\n};\n\ntype StubImportResult = {\n type: 'result';\n /**\n * IPC-friendly result of importing the module.\n * Any non-IPC-friendy values (such as functions) will be pretty-printed as strings.\n */\n value: unknown;\n};\nexport type StubImportResponse = StubImportResult | { type: 'error'; message: string };\n\nconst dirname = path.dirname(fileURLToPath(import.meta.url));\nconst testWorkerPath = path.join(dirname, 'loadStubWorker.js');\n\nlet worker: Worker | undefined;\n\n/**\n * Initialize a worker used for importing stubs.\n * (The worker is reused between tests for now because it's expensive to set up and tear down;\n * this can be changed later for certain tests if needed.)\n */\nexport function ensureStubWorker() {\n if (worker) return;\n\n worker = new Worker(testWorkerPath);\n\n // An 'error' event from a worker means an unhandled exception and it's about to exit.\n // Don't throw an error in this fallback handler in case the error occurred during a call\n // to `tryImportStub` (which has its own error handler that can fail the calling test),\n // but save the error to be included in the 'exit' handler's error message.\n let lastError: Error | undefined;\n worker.on('error', (err) => {\n lastError = err;\n });\n worker.on('exit', (code) => {\n throw new Error(\n lastError\n ? `Worker exited due to an uncaught exception: ${lastError.stack}`\n : `Worker exited unexpectedly with code ${code || 0}`,\n );\n });\n}\n\n/**\n * Import the stub in a worker and return the result.\n * Note that non-IPC-friendly values (such as functions) will be pretty-printed as strings.\n *\n * Why use a worker?\n * - Ensure that the module is imported in a \"vanilla\" way, without interference from Jest\n * transpilation or custom module resolution\n * - Allow the module to run in a simulated browser environment if needed\n * - Avoid potential pollution of the current process's globals\n *\n * (The worker is reused between tests for now because it's expensive to set up and tear down;\n * this can be changed later for certain tests if needed.)\n */\nexport async function tryImportStub(params: StubImportRequest): Promise<unknown> {\n ensureStubWorker();\n\n return new Promise((resolve, reject) => {\n const onMessage = (response: StubImportResponse) => {\n worker?.off('error', onError);\n if (response.type === 'error') {\n // error importing the module\n reject(response.message);\n } else {\n resolve(response.value);\n }\n };\n // unhandled exception (not expected)\n const onError = (err: Error) => {\n worker?.off('message', onMessage);\n reject(err);\n };\n worker?.once('message', onMessage);\n worker?.once('error', onError);\n\n worker?.postMessage(params);\n });\n}\n\nexport async function cleanUpStubWorker() {\n if (worker) {\n // Remove all listeners so that the exit handler set up during initialization doesn't throw\n worker.removeAllListeners();\n try {\n await worker.terminate();\n } catch {\n // ignore\n }\n worker = undefined;\n }\n}\n"]}
@@ -1,8 +1,14 @@
1
+ import type { StubError } from './types/StubError.js';
1
2
  /**
2
- * Generates an ESM stub for CommonJS modules.
3
- * @param entryPath - The full path to the CommonJS entry file.
4
- * @param stubPath - The path where the stub file will live. This is important for determining
5
- * relative import paths.
3
+ * Generates an ESM stub for a CommonJS module.
4
+ * @returns The stub file contents, or an error object if the module could not be stubbed.
5
+ * Should only throw an error on bad input or other cases that likely indicate either a bug
6
+ * or bad configuration (as opposed to a problem with the file being stubbed).
6
7
  */
7
- export declare function createESMStub(entryPath: string, stubPath: string): Promise<string>;
8
+ export declare function createESMStub(params: {
9
+ /** The full path to the CommonJS entry file. */
10
+ entryPath: string;
11
+ /** The path where the stub file will live. This is important for determining relative import paths. */
12
+ stubPath: string;
13
+ }): Promise<string | StubError>;
8
14
  //# sourceMappingURL=createESMStub.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createESMStub.d.ts","sourceRoot":"","sources":["../src/createESMStub.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA8DxF"}
1
+ {"version":3,"file":"createESMStub.d.ts","sourceRoot":"","sources":["../src/createESMStub.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE;IAC1C,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,uGAAuG;IACvG,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA4D9B"}
@@ -1,71 +1,61 @@
1
+ import os from 'os';
1
2
  import path from 'path';
2
3
  import { slash } from '@ms-cloudpack/path-string-parsing';
3
4
  import { runInSandbox } from './runInSandbox.js';
4
5
  import { createJSONStub } from './createJSONStub.js';
5
6
  import { isValidIdentifierName } from './isValidIdentifierName.js';
6
7
  /**
7
- * Generates an ESM stub for CommonJS modules.
8
- * @param entryPath - The full path to the CommonJS entry file.
9
- * @param stubPath - The path where the stub file will live. This is important for determining
10
- * relative import paths.
8
+ * Generates an ESM stub for a CommonJS module.
9
+ * @returns The stub file contents, or an error object if the module could not be stubbed.
10
+ * Should only throw an error on bad input or other cases that likely indicate either a bug
11
+ * or bad configuration (as opposed to a problem with the file being stubbed).
11
12
  */
12
- export async function createESMStub(entryPath, stubPath) {
13
+ export async function createESMStub(params) {
14
+ const { entryPath, stubPath } = params;
13
15
  if (path.extname(entryPath).toLowerCase() === '.json') {
14
16
  return createJSONStub(entryPath);
15
17
  }
16
- const result = [];
17
- const relativePath = './' + slash(path.relative(path.dirname(stubPath), entryPath));
18
18
  // Run the entry point file in a sandbox to determine the exports
19
- const packageExport = (await runInSandbox(entryPath));
20
- const isExportObject = typeof packageExport === 'object';
21
- const isExportFunction = typeof packageExport === 'function';
22
- if (isExportFunction || (isExportObject && !Array.isArray(packageExport))) {
23
- // Make sure to filter keywords.
24
- const namedExports = Object.keys(packageExport).filter((name) => isValidIdentifierName(name));
25
- const hasDefaultExport = 'default' in packageExport;
26
- if (namedExports.length || hasDefaultExport) {
27
- result.push(`import ${isExportFunction ? '' : '* as '}packageExport from "${relativePath}";`);
28
- if (namedExports.length) {
29
- result.push(`const { ${namedExports.join(', ')} } = packageExport;`);
30
- }
31
- if (hasDefaultExport) {
32
- result.push(`const defaultExport = (packageExport && packageExport.default?.default) ? packageExport?.default.default : packageExport?.default;`);
33
- result.push(`export default defaultExport;`);
34
- }
35
- else {
36
- result.push(`export default packageExport;`);
37
- }
38
- if (namedExports.length) {
39
- result.push(`export { ${namedExports.join(', ')} }`);
40
- }
41
- }
42
- else {
43
- // No named exports...
44
- // Object with no members? Must be a polyfill. Just import it and don't export anything.
45
- if (isExportFunction) {
46
- result.push(`export { default } from "${relativePath}";`);
47
- }
48
- else {
49
- result.push(`import "${relativePath}";`);
50
- }
51
- }
19
+ const exportInfo = await runInSandbox(entryPath);
20
+ if ('error' in exportInfo) {
21
+ return exportInfo.error;
52
22
  }
53
- else if (typeof packageExport === 'boolean' || typeof packageExport === 'number') {
54
- // Export as default.
55
- result.push(`export default ${packageExport};`);
23
+ let relativePath;
24
+ if (os.platform() === 'win32' && path.parse(entryPath).root !== path.parse(stubPath).root) {
25
+ // Different drive letters. These can't be relative to each other.
26
+ relativePath = entryPath;
56
27
  }
57
- else if (typeof packageExport === 'string') {
58
- // Export a constant.
59
- result.push(`export default ${JSON.stringify(packageExport)};`);
28
+ else {
29
+ relativePath = './' + slash(path.relative(path.dirname(stubPath), entryPath));
30
+ }
31
+ const exportType = exportInfo.type;
32
+ if (exportType === 'none') {
33
+ // No exports. Just import the module.
34
+ return `import "${relativePath}";`;
35
+ }
36
+ if (exportType === 'other') {
37
+ // Some kind of value. Import then re-export as default.
38
+ // (Note that even for values such as numbers which we could in theory rewrite inline,
39
+ // we don't know how the value is calculated at runtime or what other side effects the module
40
+ // might have, so we should preserve the import.)
41
+ return `import moduleExport from "${relativePath}";\nexport default moduleExport;`;
60
42
  }
61
- else if (Array.isArray(packageExport)) {
62
- result.push(`import packageExport from "${relativePath}";`);
63
- result.push('export default packageExport;');
43
+ // Object or function. Export both named properties (if any, and filtering keywords) and a default.
44
+ const namedExports = exportInfo.keys.filter((name) => isValidIdentifierName(name));
45
+ const hasDefaultExport = exportInfo.keys.includes('default');
46
+ const stub = [`import moduleExport from "${relativePath}";`];
47
+ if (namedExports.length) {
48
+ stub.push(`const { ${namedExports.join(', ')} } = moduleExport;`);
49
+ }
50
+ if (hasDefaultExport) {
51
+ stub.push(`const defaultExport = (moduleExport?.default?.default) ?? moduleExport?.default;`, `export default defaultExport;`);
64
52
  }
65
53
  else {
66
- // Unable to import the module in node, or it exported undefined for some reason.
67
- result.push(`import "${relativePath}";`);
54
+ stub.push(`export default moduleExport;`);
55
+ }
56
+ if (namedExports.length) {
57
+ stub.push(`export { ${namedExports.join(', ')} }`);
68
58
  }
69
- return result.join('\n');
59
+ return stub.join('\n');
70
60
  }
71
61
  //# sourceMappingURL=createESMStub.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"createESMStub.js","sourceRoot":"","sources":["../src/createESMStub.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\nimport { slash } from '@ms-cloudpack/path-string-parsing';\nimport { runInSandbox } from './runInSandbox.js';\nimport { createJSONStub } from './createJSONStub.js';\nimport { isValidIdentifierName } from './isValidIdentifierName.js';\n\n/**\n * Generates an ESM stub for CommonJS modules.\n * @param entryPath - The full path to the CommonJS entry file.\n * @param stubPath - The path where the stub file will live. This is important for determining\n * relative import paths.\n */\nexport async function createESMStub(entryPath: string, stubPath: string): Promise<string> {\n if (path.extname(entryPath).toLowerCase() === '.json') {\n return createJSONStub(entryPath);\n }\n\n const result = [];\n const relativePath = './' + slash(path.relative(path.dirname(stubPath), entryPath));\n\n // Run the entry point file in a sandbox to determine the exports\n const packageExport = (await runInSandbox(entryPath)) as Record<string, unknown>;\n\n const isExportObject = typeof packageExport === 'object';\n const isExportFunction = typeof packageExport === 'function';\n\n if (isExportFunction || (isExportObject && !Array.isArray(packageExport))) {\n // Make sure to filter keywords.\n const namedExports = Object.keys(packageExport).filter((name) => isValidIdentifierName(name));\n const hasDefaultExport = 'default' in packageExport;\n\n if (namedExports.length || hasDefaultExport) {\n result.push(`import ${isExportFunction ? '' : '* as '}packageExport from \"${relativePath}\";`);\n\n if (namedExports.length) {\n result.push(`const { ${namedExports.join(', ')} } = packageExport;`);\n }\n\n if (hasDefaultExport) {\n result.push(\n `const defaultExport = (packageExport && packageExport.default?.default) ? packageExport?.default.default : packageExport?.default;`,\n );\n result.push(`export default defaultExport;`);\n } else {\n result.push(`export default packageExport;`);\n }\n\n if (namedExports.length) {\n result.push(`export { ${namedExports.join(', ')} }`);\n }\n } else {\n // No named exports...\n // Object with no members? Must be a polyfill. Just import it and don't export anything.\n if (isExportFunction) {\n result.push(`export { default } from \"${relativePath}\";`);\n } else {\n result.push(`import \"${relativePath}\";`);\n }\n }\n } else if (typeof packageExport === 'boolean' || typeof packageExport === 'number') {\n // Export as default.\n result.push(`export default ${packageExport};`);\n } else if (typeof packageExport === 'string') {\n // Export a constant.\n result.push(`export default ${JSON.stringify(packageExport)};`);\n } else if (Array.isArray(packageExport)) {\n result.push(`import packageExport from \"${relativePath}\";`);\n result.push('export default packageExport;');\n } else {\n // Unable to import the module in node, or it exported undefined for some reason.\n result.push(`import \"${relativePath}\";`);\n }\n\n return result.join('\\n');\n}\n"]}
1
+ {"version":3,"file":"createESMStub.js","sourceRoot":"","sources":["../src/createESMStub.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAGnE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAKnC;IACC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAEvC,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,KAAK,OAAO,EAAE;QACrD,OAAO,cAAc,CAAC,SAAS,CAAC,CAAC;KAClC;IAED,iEAAiE;IACjE,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,CAAC;IACjD,IAAI,OAAO,IAAI,UAAU,EAAE;QACzB,OAAO,UAAU,CAAC,KAAK,CAAC;KACzB;IAED,IAAI,YAAoB,CAAC;IACzB,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE;QACzF,kEAAkE;QAClE,YAAY,GAAG,SAAS,CAAC;KAC1B;SAAM;QACL,YAAY,GAAG,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;KAC/E;IAED,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC;IAEnC,IAAI,UAAU,KAAK,MAAM,EAAE;QACzB,sCAAsC;QACtC,OAAO,WAAW,YAAY,IAAI,CAAC;KACpC;IAED,IAAI,UAAU,KAAK,OAAO,EAAE;QAC1B,wDAAwD;QACxD,sFAAsF;QACtF,6FAA6F;QAC7F,iDAAiD;QACjD,OAAO,6BAA6B,YAAY,kCAAkC,CAAC;KACpF;IAED,mGAAmG;IACnG,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC;IACnF,MAAM,gBAAgB,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAE7D,MAAM,IAAI,GAAG,CAAC,6BAA6B,YAAY,IAAI,CAAC,CAAC;IAE7D,IAAI,YAAY,CAAC,MAAM,EAAE;QACvB,IAAI,CAAC,IAAI,CAAC,WAAW,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;KACnE;IAED,IAAI,gBAAgB,EAAE;QACpB,IAAI,CAAC,IAAI,CACP,kFAAkF,EAClF,+BAA+B,CAChC,CAAC;KACH;SAAM;QACL,IAAI,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;KAC3C;IAED,IAAI,YAAY,CAAC,MAAM,EAAE;QACvB,IAAI,CAAC,IAAI,CAAC,YAAY,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;KACpD;IAED,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC","sourcesContent":["import os from 'os';\nimport path from 'path';\nimport { slash } from '@ms-cloudpack/path-string-parsing';\nimport { runInSandbox } from './runInSandbox.js';\nimport { createJSONStub } from './createJSONStub.js';\nimport { isValidIdentifierName } from './isValidIdentifierName.js';\nimport type { StubError } from './types/StubError.js';\n\n/**\n * Generates an ESM stub for a CommonJS module.\n * @returns The stub file contents, or an error object if the module could not be stubbed.\n * Should only throw an error on bad input or other cases that likely indicate either a bug\n * or bad configuration (as opposed to a problem with the file being stubbed).\n */\nexport async function createESMStub(params: {\n /** The full path to the CommonJS entry file. */\n entryPath: string;\n /** The path where the stub file will live. This is important for determining relative import paths. */\n stubPath: string;\n}): Promise<string | StubError> {\n const { entryPath, stubPath } = params;\n\n if (path.extname(entryPath).toLowerCase() === '.json') {\n return createJSONStub(entryPath);\n }\n\n // Run the entry point file in a sandbox to determine the exports\n const exportInfo = await runInSandbox(entryPath);\n if ('error' in exportInfo) {\n return exportInfo.error;\n }\n\n let relativePath: string;\n if (os.platform() === 'win32' && path.parse(entryPath).root !== path.parse(stubPath).root) {\n // Different drive letters. These can't be relative to each other.\n relativePath = entryPath;\n } else {\n relativePath = './' + slash(path.relative(path.dirname(stubPath), entryPath));\n }\n\n const exportType = exportInfo.type;\n\n if (exportType === 'none') {\n // No exports. Just import the module.\n return `import \"${relativePath}\";`;\n }\n\n if (exportType === 'other') {\n // Some kind of value. Import then re-export as default.\n // (Note that even for values such as numbers which we could in theory rewrite inline,\n // we don't know how the value is calculated at runtime or what other side effects the module\n // might have, so we should preserve the import.)\n return `import moduleExport from \"${relativePath}\";\\nexport default moduleExport;`;\n }\n\n // Object or function. Export both named properties (if any, and filtering keywords) and a default.\n const namedExports = exportInfo.keys.filter((name) => isValidIdentifierName(name));\n const hasDefaultExport = exportInfo.keys.includes('default');\n\n const stub = [`import moduleExport from \"${relativePath}\";`];\n\n if (namedExports.length) {\n stub.push(`const { ${namedExports.join(', ')} } = moduleExport;`);\n }\n\n if (hasDefaultExport) {\n stub.push(\n `const defaultExport = (moduleExport?.default?.default) ?? moduleExport?.default;`,\n `export default defaultExport;`,\n );\n } else {\n stub.push(`export default moduleExport;`);\n }\n\n if (namedExports.length) {\n stub.push(`export { ${namedExports.join(', ')} }`);\n }\n\n return stub.join('\\n');\n}\n"]}
package/lib/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { createESMStub } from './createESMStub.js';
2
1
  export { writeESMStub } from './writeESMStub.js';
2
+ export type { StubError } from './types/StubError.js';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC"}
package/lib/index.js CHANGED
@@ -1,3 +1,2 @@
1
- export { createESMStub } from './createESMStub.js';
2
1
  export { writeESMStub } from './writeESMStub.js';
3
2
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC","sourcesContent":["export { createESMStub } from './createESMStub.js';\nexport { writeESMStub } from './writeESMStub.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC","sourcesContent":["export { writeESMStub } from './writeESMStub.js';\nexport type { StubError } from './types/StubError.js';\n"]}
@@ -1,12 +1,15 @@
1
- import { NodeVM } from 'vm2';
2
- /** Reset `vms` and `sandboxRoots` for testing */
3
- export declare function _clearSandboxCaches(): void;
4
- /** Get `vms` state for testing */
5
- export declare function _getSandboxVms(): Record<string, NodeVM>;
1
+ import type { StubExportInfo } from './types/StubExportInfo.js';
2
+ import type { StubError } from './types/StubError.js';
3
+ /** Stop the worker for testing */
4
+ export declare function _stopWorker(): Promise<void>;
6
5
  /**
7
6
  * Run a CommonJS file within a sandboxed environment.
8
7
  * @param entryPath - Path to a CommonJS file to run in a sandbox (does not work with ESM files)
9
- * @returns The module's exports
8
+ * @returns Info about the file's exports, or error info if there was an issue.
9
+ * Should only throw an error on bad input or other cases that likely indicate either a bug
10
+ * or bad configuration (as opposed to a problem with the file being stubbed).
10
11
  */
11
- export declare function runInSandbox(entryPath: string): Promise<unknown>;
12
+ export declare function runInSandbox(entryPath: string): Promise<StubExportInfo | {
13
+ error: StubError;
14
+ }>;
12
15
  //# sourceMappingURL=runInSandbox.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runInSandbox.d.ts","sourceRoot":"","sources":["../src/runInSandbox.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAW,MAAM,KAAK,CAAC;AAwBtC,iDAAiD;AACjD,wBAAgB,mBAAmB,SAGlC;AAED,kCAAkC;AAClC,wBAAgB,cAAc,2BAE7B;AA2GD;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,oBAkGnD"}
1
+ {"version":3,"file":"runInSandbox.d.ts","sourceRoot":"","sources":["../src/runInSandbox.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEhE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AActD,kCAAkC;AAClC,wBAAsB,WAAW,kBAGhC;AAsED;;;;;;GAMG;AAEH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC,CAsFpG"}