@agoric/swingset-vat 0.32.3-dev-3dacdc3.0.3dacdc3 → 0.32.3-dev-2db363c.0.2db363c
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/package.json +20 -12
- package/tools/bundleTool.js +210 -171
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/swingset-vat",
|
|
3
|
-
"version": "0.32.3-dev-
|
|
3
|
+
"version": "0.32.3-dev-2db363c.0.2db363c",
|
|
4
4
|
"description": "Vat/Container Launcher",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"lint:eslint": "yarn run -T eslint ."
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@agoric/xsnap": "0.14.3-dev-
|
|
25
|
+
"@agoric/xsnap": "0.14.3-dev-2db363c.0.2db363c",
|
|
26
26
|
"@types/better-sqlite3": "^7.6.13",
|
|
27
27
|
"@types/microtime": "^2.1.0",
|
|
28
28
|
"@types/tmp": "^0.2.0",
|
|
@@ -30,15 +30,15 @@
|
|
|
30
30
|
"ava": "^6.4.1"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@agoric/internal": "0.3.3-dev-
|
|
34
|
-
"@agoric/kmarshal": "0.1.1-dev-
|
|
35
|
-
"@agoric/store": "0.9.3-dev-
|
|
36
|
-
"@agoric/swing-store": "0.9.2-dev-
|
|
37
|
-
"@agoric/swingset-liveslots": "0.10.3-dev-
|
|
38
|
-
"@agoric/swingset-xsnap-supervisor": "0.10.3-dev-
|
|
39
|
-
"@agoric/time": "0.3.3-dev-
|
|
40
|
-
"@agoric/vat-data": "0.5.3-dev-
|
|
41
|
-
"@agoric/xsnap-lockdown": "0.14.1-dev-
|
|
33
|
+
"@agoric/internal": "0.3.3-dev-2db363c.0.2db363c",
|
|
34
|
+
"@agoric/kmarshal": "0.1.1-dev-2db363c.0.2db363c",
|
|
35
|
+
"@agoric/store": "0.9.3-dev-2db363c.0.2db363c",
|
|
36
|
+
"@agoric/swing-store": "0.9.2-dev-2db363c.0.2db363c",
|
|
37
|
+
"@agoric/swingset-liveslots": "0.10.3-dev-2db363c.0.2db363c",
|
|
38
|
+
"@agoric/swingset-xsnap-supervisor": "0.10.3-dev-2db363c.0.2db363c",
|
|
39
|
+
"@agoric/time": "0.3.3-dev-2db363c.0.2db363c",
|
|
40
|
+
"@agoric/vat-data": "0.5.3-dev-2db363c.0.2db363c",
|
|
41
|
+
"@agoric/xsnap-lockdown": "0.14.1-dev-2db363c.0.2db363c",
|
|
42
42
|
"@endo/base64": "^1.0.12",
|
|
43
43
|
"@endo/bundle-source": "^4.1.2",
|
|
44
44
|
"@endo/captp": "^4.4.8",
|
|
@@ -89,10 +89,18 @@
|
|
|
89
89
|
"example": "examples"
|
|
90
90
|
},
|
|
91
91
|
"ava": {
|
|
92
|
+
"extensions": {
|
|
93
|
+
"js": true,
|
|
94
|
+
"ts": "module"
|
|
95
|
+
},
|
|
92
96
|
"workerThreads": false,
|
|
93
97
|
"files": [
|
|
94
98
|
"test/**/*.test.*"
|
|
95
99
|
],
|
|
100
|
+
"nodeArguments": [
|
|
101
|
+
"--import=ts-blank-space/register",
|
|
102
|
+
"--no-warnings"
|
|
103
|
+
],
|
|
96
104
|
"require": [
|
|
97
105
|
"@agoric/internal/src/ava-force-exit.mjs",
|
|
98
106
|
"@endo/init/debug.js"
|
|
@@ -105,5 +113,5 @@
|
|
|
105
113
|
"typeCoverage": {
|
|
106
114
|
"atLeast": 76.72
|
|
107
115
|
},
|
|
108
|
-
"gitHead": "
|
|
116
|
+
"gitHead": "2db363cfdc60b99cc644823e99bced301cf748b5"
|
|
109
117
|
}
|
package/tools/bundleTool.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { makeNodeBundleCache as wrappedMaker } from '@endo/bundle-source/cache.js';
|
|
2
2
|
import styles from 'ansi-styles'; // less authority than 'chalk'
|
|
3
|
-
import
|
|
3
|
+
import * as fsPromises from 'fs/promises';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
4
5
|
import { fileURLToPath } from 'url';
|
|
5
6
|
import path from 'path';
|
|
6
7
|
import { setTimeout as delay } from 'timers/promises';
|
|
8
|
+
import { makeDirectoryLock } from '@agoric/internal/src/build-cache.js';
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* @import {EReturn} from '@endo/far';
|
|
12
|
+
* @import {BuildCacheEvent} from '@agoric/internal/src/build-cache-types.js';
|
|
13
|
+
* @import {BundleOptions, ModuleFormat} from '@endo/bundle-source';
|
|
10
14
|
*/
|
|
11
15
|
|
|
12
16
|
/** @typedef {EReturn<typeof wrappedMaker>} WrappedBundleCache */
|
|
17
|
+
/** @typedef {BundleOptions<ModuleFormat> & Parameters<typeof wrappedMaker>[1]} BundleCacheOptions */
|
|
13
18
|
/**
|
|
14
19
|
* @typedef {{
|
|
15
20
|
* sourceSpec?: string;
|
|
@@ -31,18 +36,139 @@ import { setTimeout as delay } from 'timers/promises';
|
|
|
31
36
|
* loadRegistry: <T extends Record<string, BundleRegistryEntry>>(registry: T) => Promise<LoadedRegistryBundles<T>>;
|
|
32
37
|
* }} BundleCache
|
|
33
38
|
*/
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {{
|
|
41
|
+
* onBundleToolEvent?: (event: BundleToolEvent) => void,
|
|
42
|
+
* }} BundleToolEventSink
|
|
43
|
+
*/
|
|
44
|
+
/**
|
|
45
|
+
* @typedef {{
|
|
46
|
+
* type: 'bundle-source-log';
|
|
47
|
+
* args: unknown[];
|
|
48
|
+
* phase: string;
|
|
49
|
+
* timestamp: number;
|
|
50
|
+
* }} BundleSourceLogEvent
|
|
51
|
+
*/
|
|
52
|
+
/**
|
|
53
|
+
* @typedef {{
|
|
54
|
+
* type: 'registry-loaded';
|
|
55
|
+
* args: string[];
|
|
56
|
+
* phase: '[registry]';
|
|
57
|
+
* elapsedMs: number;
|
|
58
|
+
* bundleCount: number;
|
|
59
|
+
* }} RegistryLoadedEvent
|
|
60
|
+
*/
|
|
61
|
+
/**
|
|
62
|
+
* @typedef {BundleSourceLogEvent | RegistryLoadedEvent | BuildCacheEvent} BundleToolEvent
|
|
63
|
+
*/
|
|
64
|
+
/**
|
|
65
|
+
* @typedef {{
|
|
66
|
+
* delayMs: (ms: number) => Promise<unknown>,
|
|
67
|
+
* eventSink?: BundleToolEventSink,
|
|
68
|
+
* isPidAlive: (pid: number) => boolean,
|
|
69
|
+
* loadModule: Parameters<typeof wrappedMaker>[2],
|
|
70
|
+
* monotonicNow: () => number,
|
|
71
|
+
* now: () => number,
|
|
72
|
+
* pid: number,
|
|
73
|
+
* }} BundleToolPowers
|
|
74
|
+
*/
|
|
34
75
|
|
|
35
76
|
const BUNDLE_LOCK_ACQUIRE_TIMEOUT_MS = 5 * 60_000;
|
|
36
77
|
const BUNDLE_STALE_LOCK_MS = 60_000;
|
|
37
78
|
|
|
79
|
+
const defaultBundleToolEventSink = {
|
|
80
|
+
onBundleToolEvent: event => {
|
|
81
|
+
if (
|
|
82
|
+
event.type !== 'bundle-source-log' &&
|
|
83
|
+
event.type !== 'registry-loaded'
|
|
84
|
+
) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const { args = [], phase = '[check]', type } = event;
|
|
88
|
+
if (type === 'bundle-source-log') {
|
|
89
|
+
const joined = args.map(arg => String(arg)).join(' ');
|
|
90
|
+
if (joined.includes(' valid:')) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const flattened = args.map(arg =>
|
|
95
|
+
// Don't print stack traces.
|
|
96
|
+
arg instanceof Error ? arg.message : arg,
|
|
97
|
+
);
|
|
98
|
+
console.log(
|
|
99
|
+
`${styles.dim.open}[bundleTool]${phase}`,
|
|
100
|
+
...flattened,
|
|
101
|
+
styles.dim.close,
|
|
102
|
+
);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
harden(defaultBundleToolEventSink);
|
|
106
|
+
|
|
107
|
+
const inferLogPhase = args => {
|
|
108
|
+
const joined = args.map(arg => String(arg)).join(' ');
|
|
109
|
+
let phase = '[check]';
|
|
110
|
+
if (joined.includes(' add:') || joined.includes(' bundled ')) {
|
|
111
|
+
phase = '[rebuilt]';
|
|
112
|
+
}
|
|
113
|
+
return phase;
|
|
114
|
+
};
|
|
115
|
+
harden(inferLogPhase);
|
|
116
|
+
|
|
38
117
|
/**
|
|
39
|
-
* @param {
|
|
40
|
-
* @
|
|
41
|
-
* @
|
|
42
|
-
|
|
118
|
+
* @param {Partial<BundleToolPowers>} [options]
|
|
119
|
+
* @returns {BundleToolPowers}
|
|
120
|
+
* @alpha
|
|
121
|
+
*/
|
|
122
|
+
export const makeAmbientBundleToolPowers = (options = {}) => {
|
|
123
|
+
const {
|
|
124
|
+
delayMs = ms => delay(ms),
|
|
125
|
+
eventSink = defaultBundleToolEventSink,
|
|
126
|
+
isPidAlive = lockPid => {
|
|
127
|
+
if (!Number.isInteger(lockPid) || lockPid <= 0) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
process.kill(lockPid, 0);
|
|
132
|
+
return true;
|
|
133
|
+
} catch {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
loadModule = specifier => import(specifier),
|
|
138
|
+
monotonicNow = () => performance.now(),
|
|
139
|
+
now = () => Date.now(),
|
|
140
|
+
pid = process.pid,
|
|
141
|
+
} = options;
|
|
142
|
+
return harden({
|
|
143
|
+
delayMs,
|
|
144
|
+
eventSink,
|
|
145
|
+
isPidAlive,
|
|
146
|
+
loadModule,
|
|
147
|
+
monotonicNow,
|
|
148
|
+
now,
|
|
149
|
+
pid,
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
harden(makeAmbientBundleToolPowers);
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @param {string} destPath
|
|
156
|
+
* @param {BundleToolPowers} powers
|
|
157
|
+
* @param {BundleCacheOptions} [options]
|
|
43
158
|
* @returns {Promise<BundleCache>}
|
|
159
|
+
* @alpha
|
|
44
160
|
*/
|
|
45
|
-
export const makeNodeBundleCache = async (
|
|
161
|
+
export const makeNodeBundleCache = async (destPath, powers, options = {}) => {
|
|
162
|
+
const {
|
|
163
|
+
delayMs,
|
|
164
|
+
eventSink = defaultBundleToolEventSink,
|
|
165
|
+
isPidAlive,
|
|
166
|
+
loadModule,
|
|
167
|
+
monotonicNow,
|
|
168
|
+
now,
|
|
169
|
+
pid,
|
|
170
|
+
} = powers;
|
|
171
|
+
|
|
46
172
|
/** @param {string} sourceSpec */
|
|
47
173
|
const canonicalizeSourceSpec = sourceSpec => {
|
|
48
174
|
if (sourceSpec.startsWith('file:')) {
|
|
@@ -66,147 +192,83 @@ export const makeNodeBundleCache = async (dest, options, loadModule, pid) => {
|
|
|
66
192
|
}
|
|
67
193
|
};
|
|
68
194
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
195
|
+
const onEvent = eventSink.onBundleToolEvent || (() => {});
|
|
196
|
+
const optionsKeyFor = options0 => {
|
|
197
|
+
// Include options in the key on purpose: bundle output may differ by
|
|
198
|
+
// bundle-source options, and correctness prefers over-segmentation over
|
|
199
|
+
// accidental cache aliasing.
|
|
200
|
+
try {
|
|
201
|
+
return JSON.stringify(options0 || {});
|
|
202
|
+
} catch {
|
|
203
|
+
return '[unstringifiable-options]';
|
|
78
204
|
}
|
|
79
|
-
const flattened = args.map(arg =>
|
|
80
|
-
// Don't print stack traces.
|
|
81
|
-
arg instanceof Error ? arg.message : arg,
|
|
82
|
-
);
|
|
83
|
-
console.log(
|
|
84
|
-
// Make all messages prefixed and dim.
|
|
85
|
-
`${styles.dim.open}[bundleTool]${phase}`,
|
|
86
|
-
...flattened,
|
|
87
|
-
styles.dim.close,
|
|
88
|
-
);
|
|
89
205
|
};
|
|
206
|
+
const sanitizeName = name => name.replace(/[^a-zA-Z0-9._-]/g, '-');
|
|
207
|
+
const getTargetNames = (canonicalRootPath, targetName, options0) => {
|
|
208
|
+
const requestedTargetName =
|
|
209
|
+
targetName || path.basename(canonicalRootPath, '.js');
|
|
210
|
+
const optionsKey = optionsKeyFor(options0);
|
|
211
|
+
const hashKey = createHash('sha256')
|
|
212
|
+
.update(`${canonicalRootPath}\n${optionsKey}`)
|
|
213
|
+
.digest('hex')
|
|
214
|
+
.slice(0, 12);
|
|
215
|
+
const cacheTargetName = `${sanitizeName(requestedTargetName)}-${hashKey}`;
|
|
216
|
+
return harden({ requestedTargetName, cacheTargetName, optionsKey });
|
|
217
|
+
};
|
|
218
|
+
const log = (...args) => {
|
|
219
|
+
onEvent({
|
|
220
|
+
type: 'bundle-source-log',
|
|
221
|
+
args,
|
|
222
|
+
phase: inferLogPhase(args),
|
|
223
|
+
timestamp: now(),
|
|
224
|
+
});
|
|
225
|
+
};
|
|
226
|
+
|
|
90
227
|
const rawCache = await wrappedMaker(
|
|
91
|
-
|
|
228
|
+
destPath,
|
|
92
229
|
{ log, ...options },
|
|
93
230
|
loadModule,
|
|
94
231
|
pid,
|
|
95
232
|
);
|
|
96
|
-
const lockRoot = path.resolve(
|
|
233
|
+
const lockRoot = path.resolve(destPath, '.bundle-locks');
|
|
97
234
|
/** @type {Map<string, Promise<unknown>>} */
|
|
98
235
|
const inProcessLoads = new Map();
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const isPidAlive = lockPid => {
|
|
112
|
-
if (!Number.isInteger(lockPid) || lockPid <= 0) {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
try {
|
|
116
|
-
process.kill(lockPid, 0);
|
|
117
|
-
return true;
|
|
118
|
-
} catch {
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
const maybeBreakStaleLock = async () => {
|
|
124
|
-
const ownerTxt = await readFile(ownerPath, 'utf8').catch(() => undefined);
|
|
125
|
-
let lockInfo;
|
|
126
|
-
if (typeof ownerTxt === 'string') {
|
|
127
|
-
try {
|
|
128
|
-
lockInfo = JSON.parse(ownerTxt);
|
|
129
|
-
} catch {
|
|
130
|
-
// Another process may be in the middle of writing owner.json.
|
|
131
|
-
// Treat malformed data as unknown owner and rely on age checks below.
|
|
132
|
-
lockInfo = undefined;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (
|
|
136
|
-
lockInfo &&
|
|
137
|
-
Number.isInteger(lockInfo.pid) &&
|
|
138
|
-
!isPidAlive(lockInfo.pid)
|
|
139
|
-
) {
|
|
140
|
-
await rm(lockPath, { recursive: true, force: true });
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
try {
|
|
144
|
-
const st = await stat(lockPath);
|
|
145
|
-
const ageMs = Date.now() - st.mtimeMs;
|
|
146
|
-
if (ageMs >= BUNDLE_STALE_LOCK_MS) {
|
|
147
|
-
await rm(lockPath, { recursive: true, force: true });
|
|
148
|
-
return true;
|
|
149
|
-
}
|
|
150
|
-
} catch {
|
|
151
|
-
return true;
|
|
152
|
-
}
|
|
153
|
-
return false;
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
// Cross-process lock via mkdir(). Retriable on EEXIST.
|
|
157
|
-
for (;;) {
|
|
158
|
-
try {
|
|
159
|
-
await mkdir(lockPath);
|
|
160
|
-
await writeFile(
|
|
161
|
-
ownerPath,
|
|
162
|
-
JSON.stringify({ pid: process.pid, createdAt: Date.now() }),
|
|
163
|
-
'utf8',
|
|
164
|
-
);
|
|
165
|
-
break;
|
|
166
|
-
} catch (e) {
|
|
167
|
-
if (e && typeof e === 'object' && 'code' in e && e.code === 'EEXIST') {
|
|
168
|
-
const brokeStaleLock = await maybeBreakStaleLock();
|
|
169
|
-
if (brokeStaleLock) {
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
const waitedMs = performance.now() - started;
|
|
173
|
-
if (waitedMs >= BUNDLE_LOCK_ACQUIRE_TIMEOUT_MS) {
|
|
174
|
-
throw Error(
|
|
175
|
-
`Timed out waiting for bundle lock ${lockPath} after ${waitedMs}ms`,
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
await delay(20);
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
throw e;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
try {
|
|
186
|
-
return await duringLock();
|
|
187
|
-
} finally {
|
|
188
|
-
await rm(lockPath, { recursive: true, force: true });
|
|
189
|
-
}
|
|
190
|
-
};
|
|
236
|
+
const { withLock } = makeDirectoryLock({
|
|
237
|
+
fs: fsPromises,
|
|
238
|
+
delayMs,
|
|
239
|
+
now,
|
|
240
|
+
pid,
|
|
241
|
+
isPidAlive,
|
|
242
|
+
lockRoot,
|
|
243
|
+
staleLockMs: BUNDLE_STALE_LOCK_MS,
|
|
244
|
+
acquireTimeoutMs: BUNDLE_LOCK_ACQUIRE_TIMEOUT_MS,
|
|
245
|
+
onEvent,
|
|
246
|
+
});
|
|
191
247
|
|
|
192
248
|
const cache = harden({
|
|
193
249
|
...rawCache,
|
|
194
250
|
add: (rootPath, targetName, log0, options0) => {
|
|
195
251
|
const canonicalRootPath = canonicalizeSourceSpec(rootPath);
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
252
|
+
const { requestedTargetName, cacheTargetName } = getTargetNames(
|
|
253
|
+
canonicalRootPath,
|
|
254
|
+
targetName,
|
|
255
|
+
options0,
|
|
256
|
+
);
|
|
257
|
+
return withLock(requestedTargetName, () =>
|
|
258
|
+
rawCache.add(canonicalRootPath, cacheTargetName, log0, options0),
|
|
200
259
|
);
|
|
201
260
|
},
|
|
202
261
|
validateOrAdd: (rootPath, targetName, log0, options0) => {
|
|
203
262
|
const canonicalRootPath = canonicalizeSourceSpec(rootPath);
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
263
|
+
const { requestedTargetName, cacheTargetName } = getTargetNames(
|
|
264
|
+
canonicalRootPath,
|
|
265
|
+
targetName,
|
|
266
|
+
options0,
|
|
267
|
+
);
|
|
268
|
+
return withLock(requestedTargetName, () =>
|
|
207
269
|
rawCache.validateOrAdd(
|
|
208
270
|
canonicalRootPath,
|
|
209
|
-
|
|
271
|
+
cacheTargetName,
|
|
210
272
|
log0,
|
|
211
273
|
options0,
|
|
212
274
|
),
|
|
@@ -214,23 +276,21 @@ export const makeNodeBundleCache = async (dest, options, loadModule, pid) => {
|
|
|
214
276
|
},
|
|
215
277
|
load: async (rootPath, targetName, log0, options0) => {
|
|
216
278
|
const canonicalRootPath = canonicalizeSourceSpec(rootPath);
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
const key = `${
|
|
220
|
-
options0 || {},
|
|
221
|
-
)}`;
|
|
279
|
+
const { requestedTargetName, cacheTargetName, optionsKey } =
|
|
280
|
+
getTargetNames(canonicalRootPath, targetName, options0);
|
|
281
|
+
const key = `${cacheTargetName}:${canonicalRootPath}:${optionsKey}`;
|
|
222
282
|
const found = inProcessLoads.get(key);
|
|
223
283
|
if (found) {
|
|
224
284
|
return found;
|
|
225
285
|
}
|
|
226
|
-
const pending =
|
|
286
|
+
const pending = withLock(requestedTargetName, async () => {
|
|
227
287
|
const { bundleFileName } = await rawCache.validateOrAdd(
|
|
228
288
|
canonicalRootPath,
|
|
229
|
-
|
|
289
|
+
cacheTargetName,
|
|
230
290
|
log0,
|
|
231
291
|
options0,
|
|
232
292
|
);
|
|
233
|
-
const bundlePath = path.resolve(
|
|
293
|
+
const bundlePath = path.resolve(destPath, bundleFileName);
|
|
234
294
|
return import(bundlePath).then(m => harden(m.default));
|
|
235
295
|
});
|
|
236
296
|
inProcessLoads.set(key, pending);
|
|
@@ -250,7 +310,7 @@ export const makeNodeBundleCache = async (dest, options, loadModule, pid) => {
|
|
|
250
310
|
* @returns {Promise<LoadedRegistryBundles<T>>}
|
|
251
311
|
*/
|
|
252
312
|
const loadRegistry = async registry => {
|
|
253
|
-
const started =
|
|
313
|
+
const started = monotonicNow();
|
|
254
314
|
await null;
|
|
255
315
|
try {
|
|
256
316
|
const loaded = await Promise.all(
|
|
@@ -270,11 +330,16 @@ export const makeNodeBundleCache = async (dest, options, loadModule, pid) => {
|
|
|
270
330
|
harden(Object.fromEntries(loaded))
|
|
271
331
|
);
|
|
272
332
|
} finally {
|
|
273
|
-
const elapsedMs =
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
333
|
+
const elapsedMs = monotonicNow() - started;
|
|
334
|
+
onEvent({
|
|
335
|
+
type: 'registry-loaded',
|
|
336
|
+
phase: '[registry]',
|
|
337
|
+
args: [
|
|
338
|
+
`loaded ${Object.keys(registry).length} bundle(s) in ${elapsedMs}ms`,
|
|
339
|
+
],
|
|
340
|
+
elapsedMs,
|
|
341
|
+
bundleCount: Object.keys(registry).length,
|
|
342
|
+
});
|
|
278
343
|
}
|
|
279
344
|
};
|
|
280
345
|
return /** @type {BundleCache} */ (
|
|
@@ -285,41 +350,14 @@ export const makeNodeBundleCache = async (dest, options, loadModule, pid) => {
|
|
|
285
350
|
);
|
|
286
351
|
};
|
|
287
352
|
|
|
288
|
-
/** @type {Map<string, Promise<BundleCache>>} */
|
|
289
|
-
const providedCaches = new Map();
|
|
290
|
-
|
|
291
353
|
/**
|
|
292
|
-
* Make a new bundle cache for the destination. If there is already one for that
|
|
293
|
-
* destination, return it.
|
|
294
|
-
*
|
|
295
354
|
* @param {string} dest
|
|
296
|
-
* @param {object} options
|
|
297
|
-
* @param {string} [options.format]
|
|
298
|
-
* @param {boolean} [options.dev]
|
|
299
|
-
* @param {number} [options.byteLimit=490_000] - Maximum bundle size in bytes before
|
|
300
|
-
* falling back to optimizations that may reduce legibility.
|
|
301
|
-
* @param {(id: string) => Promise<any>} loadModule
|
|
302
355
|
* @param {number} [pid]
|
|
303
356
|
* @returns {Promise<BundleCache>}
|
|
357
|
+
* @alpha
|
|
304
358
|
*/
|
|
305
|
-
export const
|
|
306
|
-
|
|
307
|
-
// store the promise instead of awaiting to prevent a race
|
|
308
|
-
let bundleCache = providedCaches.get(uniqueDest);
|
|
309
|
-
if (!bundleCache) {
|
|
310
|
-
bundleCache = makeNodeBundleCache(dest, options, loadModule, pid);
|
|
311
|
-
providedCaches.set(uniqueDest, bundleCache);
|
|
312
|
-
}
|
|
313
|
-
return bundleCache;
|
|
314
|
-
};
|
|
315
|
-
harden(provideBundleCache);
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* @param {string} dest
|
|
319
|
-
* @returns {Promise<BundleCache>}
|
|
320
|
-
*/
|
|
321
|
-
export const unsafeMakeBundleCache = dest =>
|
|
322
|
-
makeNodeBundleCache(dest, {}, s => import(s));
|
|
359
|
+
export const unsafeMakeBundleCache = (dest, pid = process.pid) =>
|
|
360
|
+
makeNodeBundleCache(dest, makeAmbientBundleToolPowers({ pid }));
|
|
323
361
|
|
|
324
362
|
/**
|
|
325
363
|
* This is a hack to share bundles across multiple SwingSet instances. It is
|
|
@@ -344,6 +382,7 @@ export const sharedBundleCachePath = fileURLToPath(
|
|
|
344
382
|
* separately for use in other contexts that need to know where the shared
|
|
345
383
|
* bundles are located, but that also makes it more likely to cause conflicts if
|
|
346
384
|
* used in multiple places at once.
|
|
385
|
+
* @alpha
|
|
347
386
|
*/
|
|
348
387
|
export const unsafeSharedBundleCache = unsafeMakeBundleCache(
|
|
349
388
|
sharedBundleCachePath,
|