@endo/compartment-mapper 1.6.3 → 2.0.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/package.json +12 -16
- package/src/archive-lite.d.ts +7 -7
- package/src/archive-lite.d.ts.map +1 -1
- package/src/archive-lite.js +78 -27
- package/src/archive.d.ts.map +1 -1
- package/src/archive.js +7 -0
- package/src/bundle-lite.d.ts +3 -3
- package/src/bundle-lite.d.ts.map +1 -1
- package/src/bundle-lite.js +19 -24
- package/src/bundle.d.ts +3 -3
- package/src/bundle.d.ts.map +1 -1
- package/src/bundle.js +19 -24
- package/src/capture-lite.d.ts +2 -2
- package/src/capture-lite.d.ts.map +1 -1
- package/src/capture-lite.js +217 -25
- package/src/compartment-map.d.ts +9 -2
- package/src/compartment-map.d.ts.map +1 -1
- package/src/compartment-map.js +737 -254
- package/src/digest.d.ts +22 -2
- package/src/digest.d.ts.map +1 -1
- package/src/digest.js +179 -56
- package/src/generic-graph.d.ts.map +1 -1
- package/src/generic-graph.js +8 -3
- package/src/guards.d.ts +18 -0
- package/src/guards.d.ts.map +1 -0
- package/src/guards.js +109 -0
- package/src/hooks.md +124 -0
- package/src/import-archive-lite.d.ts.map +1 -1
- package/src/import-archive-lite.js +15 -11
- package/src/import-archive.d.ts +5 -19
- package/src/import-archive.d.ts.map +1 -1
- package/src/import-archive.js +7 -27
- package/src/import-hook.d.ts +4 -3
- package/src/import-hook.d.ts.map +1 -1
- package/src/import-hook.js +138 -69
- package/src/import-lite.d.ts +6 -6
- package/src/import-lite.d.ts.map +1 -1
- package/src/import-lite.js +8 -5
- package/src/import.d.ts +3 -3
- package/src/import.d.ts.map +1 -1
- package/src/import.js +16 -6
- package/src/infer-exports.d.ts.map +1 -1
- package/src/infer-exports.js +16 -6
- package/src/link.d.ts +4 -3
- package/src/link.d.ts.map +1 -1
- package/src/link.js +70 -58
- package/src/node-modules.d.ts +4 -3
- package/src/node-modules.d.ts.map +1 -1
- package/src/node-modules.js +482 -114
- package/src/parse-cjs-shared-export-wrapper.d.ts.map +1 -1
- package/src/parse-cjs-shared-export-wrapper.js +3 -1
- package/src/policy-format.d.ts +22 -5
- package/src/policy-format.d.ts.map +1 -1
- package/src/policy-format.js +342 -108
- package/src/policy.d.ts +13 -28
- package/src/policy.d.ts.map +1 -1
- package/src/policy.js +161 -106
- package/src/types/canonical-name.d.ts +97 -0
- package/src/types/canonical-name.d.ts.map +1 -0
- package/src/types/canonical-name.ts +151 -0
- package/src/types/compartment-map-schema.d.ts +114 -35
- package/src/types/compartment-map-schema.d.ts.map +1 -1
- package/src/types/compartment-map-schema.ts +202 -37
- package/src/types/external.d.ts +168 -28
- package/src/types/external.d.ts.map +1 -1
- package/src/types/external.ts +215 -26
- package/src/types/internal.d.ts +23 -42
- package/src/types/internal.d.ts.map +1 -1
- package/src/types/internal.ts +51 -50
- package/src/types/node-modules.d.ts +71 -10
- package/src/types/node-modules.d.ts.map +1 -1
- package/src/types/node-modules.ts +107 -9
- package/src/types/policy-schema.d.ts +26 -11
- package/src/types/policy-schema.d.ts.map +1 -1
- package/src/types/policy-schema.ts +29 -16
- package/src/types/policy.d.ts +6 -2
- package/src/types/policy.d.ts.map +1 -1
- package/src/types/policy.ts +7 -2
- package/src/types/typescript.d.ts +28 -0
- package/src/types/typescript.d.ts.map +1 -1
- package/src/types/typescript.ts +37 -1
package/src/capture-lite.js
CHANGED
|
@@ -30,45 +30,68 @@
|
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
/* eslint no-shadow: 0 */
|
|
33
|
+
/* global globalThis */
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* @import {
|
|
36
37
|
* CaptureLiteOptions,
|
|
37
38
|
* CaptureResult,
|
|
38
|
-
*
|
|
39
|
+
* PackageCompartmentMapDescriptor,
|
|
40
|
+
* PreloadOption,
|
|
41
|
+
* MakeLoadCompartmentsOptions,
|
|
39
42
|
* ReadFn,
|
|
40
43
|
* ReadPowers,
|
|
41
44
|
* Sources,
|
|
45
|
+
* CaptureCompartmentMapOptions,
|
|
42
46
|
* } from './types.js'
|
|
43
47
|
*/
|
|
44
48
|
|
|
49
|
+
import { digestCompartmentMap } from './digest.js';
|
|
45
50
|
import {
|
|
46
51
|
exitModuleImportHookMaker,
|
|
47
52
|
makeImportHookMaker,
|
|
48
53
|
} from './import-hook.js';
|
|
49
54
|
import { link } from './link.js';
|
|
50
55
|
import { resolve } from './node-module-specifier.js';
|
|
56
|
+
import { ATTENUATORS_COMPARTMENT, ENTRY_COMPARTMENT } from './policy-format.js';
|
|
51
57
|
import { detectAttenuators } from './policy.js';
|
|
52
58
|
import { unpackReadPowers } from './powers.js';
|
|
53
|
-
import { digestCompartmentMap } from './digest.js';
|
|
54
59
|
|
|
55
|
-
const { freeze, assign, create } = Object;
|
|
60
|
+
const { freeze, assign, create, keys, entries } = Object;
|
|
61
|
+
const { quote: q } = assert;
|
|
62
|
+
const noop = () => {};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The name of the module to preload if no entry is provided in a {@link PreloadOption} array
|
|
66
|
+
*/
|
|
67
|
+
const DEFAULT_PRELOAD_ENTRY = '.';
|
|
56
68
|
|
|
57
|
-
const
|
|
69
|
+
const DefaultCompartment = /** @type {typeof Compartment} */ (
|
|
70
|
+
// @ts-expect-error globalThis.Compartment is definitely on globalThis.
|
|
71
|
+
globalThis.Compartment
|
|
72
|
+
);
|
|
58
73
|
|
|
59
74
|
/**
|
|
60
|
-
* @param {
|
|
75
|
+
* @param {PackageCompartmentMapDescriptor} compartmentMap
|
|
61
76
|
* @param {Sources} sources
|
|
77
|
+
* @param {CaptureCompartmentMapOptions} options
|
|
62
78
|
* @returns {CaptureResult}
|
|
63
79
|
*/
|
|
64
|
-
const captureCompartmentMap = (
|
|
80
|
+
const captureCompartmentMap = (
|
|
81
|
+
compartmentMap,
|
|
82
|
+
sources,
|
|
83
|
+
{ packageConnectionsHook, log = noop } = {},
|
|
84
|
+
) => {
|
|
65
85
|
const {
|
|
66
86
|
compartmentMap: captureCompartmentMap,
|
|
67
87
|
sources: captureSources,
|
|
68
88
|
newToOldCompartmentNames,
|
|
69
89
|
compartmentRenames,
|
|
70
90
|
oldToNewCompartmentNames,
|
|
71
|
-
} = digestCompartmentMap(compartmentMap, sources
|
|
91
|
+
} = digestCompartmentMap(compartmentMap, sources, {
|
|
92
|
+
packageConnectionsHook,
|
|
93
|
+
log,
|
|
94
|
+
});
|
|
72
95
|
return {
|
|
73
96
|
captureCompartmentMap,
|
|
74
97
|
captureSources,
|
|
@@ -79,12 +102,166 @@ const captureCompartmentMap = (compartmentMap, sources) => {
|
|
|
79
102
|
};
|
|
80
103
|
|
|
81
104
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
105
|
+
* Factory for a function that loads compartments.
|
|
106
|
+
*
|
|
107
|
+
* @param {PackageCompartmentMapDescriptor} compartmentMap Compartment map
|
|
108
|
+
* @param {Sources} sources Sources
|
|
109
|
+
* @param {MakeLoadCompartmentsOptions} [options]
|
|
110
|
+
* @returns {(linkedCompartments: Record<string, Compartment>, entryCompartment: Compartment, attenuatorsCompartment: Compartment) => Promise<void>}
|
|
111
|
+
*/
|
|
112
|
+
const makePreloader = (
|
|
113
|
+
compartmentMap,
|
|
114
|
+
sources,
|
|
115
|
+
{ log = noop, policy, _preload: preload = [] } = {},
|
|
116
|
+
) => {
|
|
117
|
+
const {
|
|
118
|
+
entry: { module: entryModuleSpecifier },
|
|
119
|
+
} = compartmentMap;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Iterates over canonical names in the {@link preload preload array}
|
|
123
|
+
* and loads those which have not yet been loaded.
|
|
124
|
+
*
|
|
125
|
+
* Will not load the "attenuators" `Compartment`, nor will it load any
|
|
126
|
+
* `Compartment` having a non-empty value in `sources` (since it is presumed
|
|
127
|
+
* it has already been loaded).
|
|
128
|
+
*
|
|
129
|
+
* @param {Record<string, Compartment>} compartments
|
|
130
|
+
* @returns {Promise<void>} Resolves when all appropriate compartments are
|
|
131
|
+
* loaded.
|
|
132
|
+
*/
|
|
133
|
+
const preloader = async compartments => {
|
|
134
|
+
/** @type {[compartmentName: string, compartment: Compartment, moduleSpecifier: string][]} */
|
|
135
|
+
const compartmentsToLoad = [];
|
|
136
|
+
|
|
137
|
+
for (const preloadValue of preload) {
|
|
138
|
+
/** @type {string} */
|
|
139
|
+
let canonicalName;
|
|
140
|
+
/** @type {string} */
|
|
141
|
+
let entry;
|
|
142
|
+
if (typeof preloadValue === 'string') {
|
|
143
|
+
canonicalName = preloadValue;
|
|
144
|
+
entry = DEFAULT_PRELOAD_ENTRY;
|
|
145
|
+
} else {
|
|
146
|
+
canonicalName = preloadValue.compartment;
|
|
147
|
+
entry = preloadValue.entry;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// skip; should already be loaded
|
|
151
|
+
if (
|
|
152
|
+
canonicalName !== ATTENUATORS_COMPARTMENT &&
|
|
153
|
+
canonicalName !== ENTRY_COMPARTMENT
|
|
154
|
+
) {
|
|
155
|
+
// TODO: A mapping of canonical name to compartment name is generated by
|
|
156
|
+
// mapNodeModules(), but it is not exposed. Expose it as an option on
|
|
157
|
+
// mapNodeModules() to be mutated and allow it as an option to
|
|
158
|
+
// captureFromMap() so we do not have to do this extra work. The data we
|
|
159
|
+
// need is _also_ generated by digestCompartmentMap() as the
|
|
160
|
+
// newToOldCompartmentNames property, but we cannot time-travel.
|
|
161
|
+
const [compartmentName, compartmentDescriptor] = entries(
|
|
162
|
+
compartmentMap.compartments,
|
|
163
|
+
).find(([, compartment]) => compartment.label === canonicalName) ?? [
|
|
164
|
+
canonicalName,
|
|
165
|
+
compartmentMap.compartments[canonicalName],
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
if (!compartmentDescriptor) {
|
|
169
|
+
throw new ReferenceError(
|
|
170
|
+
`Failed attempting to preload unknown compartment ${q(canonicalName)}`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const compartmentSources = sources[compartmentName];
|
|
175
|
+
|
|
176
|
+
if (keys(compartmentSources).length) {
|
|
177
|
+
log(
|
|
178
|
+
`Refusing to preload Compartment ${q(canonicalName)}; already loaded`,
|
|
179
|
+
);
|
|
180
|
+
} else {
|
|
181
|
+
const compartment = compartments[compartmentName];
|
|
182
|
+
if (!compartment) {
|
|
183
|
+
throw new ReferenceError(
|
|
184
|
+
`No compartment found for ${q(canonicalName)}`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
compartmentsToLoad.push([canonicalName, compartment, entry]);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const { length: compartmentsToLoadCount } = compartmentsToLoad;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* This index increments in the order in which compartments finish
|
|
197
|
+
* loading—_not_ the order in which they began loading.
|
|
198
|
+
*/
|
|
199
|
+
let loadedCompartmentIndex = 0;
|
|
200
|
+
await Promise.all(
|
|
201
|
+
compartmentsToLoad.map(
|
|
202
|
+
async ([compartmentName, compartment, moduleSpecifier]) => {
|
|
203
|
+
await compartment.load(moduleSpecifier);
|
|
204
|
+
loadedCompartmentIndex += 1;
|
|
205
|
+
log(
|
|
206
|
+
`Force-loaded Compartment: ${q(compartmentName)} (${loadedCompartmentIndex}/${compartmentsToLoadCount})`,
|
|
207
|
+
);
|
|
208
|
+
},
|
|
209
|
+
),
|
|
210
|
+
);
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Loads, in order:
|
|
215
|
+
*
|
|
216
|
+
* 1. The entry compartment
|
|
217
|
+
* 2. The attenuators compartment (_if and only if_ `policy` was provided)
|
|
218
|
+
* 3. All modules scheduled for preloading
|
|
219
|
+
*
|
|
220
|
+
* @param {Record<string, Compartment>} linkedCompartments
|
|
221
|
+
* @param {Compartment} entryCompartment
|
|
222
|
+
* @param {Compartment} attenuatorsCompartment
|
|
223
|
+
* @returns {Promise<void>} Resolves when all compartments are loaded.
|
|
224
|
+
*/
|
|
225
|
+
const loadCompartments = async (
|
|
226
|
+
linkedCompartments,
|
|
227
|
+
entryCompartment,
|
|
228
|
+
attenuatorsCompartment,
|
|
229
|
+
) => {
|
|
230
|
+
await entryCompartment.load(entryModuleSpecifier);
|
|
231
|
+
|
|
232
|
+
if (policy) {
|
|
233
|
+
// retain all attenuators.
|
|
234
|
+
await Promise.all(
|
|
235
|
+
detectAttenuators(policy).map(attenuatorSpecifier =>
|
|
236
|
+
attenuatorsCompartment.load(attenuatorSpecifier),
|
|
237
|
+
),
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
await preloader(linkedCompartments);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
return loadCompartments;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* "Captures" the compartment map descriptors and sources from a partially
|
|
249
|
+
* completed compartment map—_without_ creating an archive.
|
|
250
|
+
*
|
|
251
|
+
* The resulting compartment map represents a well-formed dependency graph,
|
|
252
|
+
* laden with useful metadata. This, for example, could be used for automatic
|
|
253
|
+
* policy generation.
|
|
254
|
+
*
|
|
255
|
+
* @param {ReadFn | ReadPowers} readPowers Powers
|
|
256
|
+
* @param {PackageCompartmentMapDescriptor} compartmentMap
|
|
84
257
|
* @param {CaptureLiteOptions} [options]
|
|
85
258
|
* @returns {Promise<CaptureResult>}
|
|
86
259
|
*/
|
|
87
|
-
export const captureFromMap = async (
|
|
260
|
+
export const captureFromMap = async (
|
|
261
|
+
readPowers,
|
|
262
|
+
compartmentMap,
|
|
263
|
+
options = {},
|
|
264
|
+
) => {
|
|
88
265
|
const {
|
|
89
266
|
moduleTransforms,
|
|
90
267
|
syncModuleTransforms,
|
|
@@ -94,14 +271,17 @@ export const captureFromMap = async (powers, compartmentMap, options = {}) => {
|
|
|
94
271
|
policy = undefined,
|
|
95
272
|
sourceMapHook = undefined,
|
|
96
273
|
parserForLanguage: parserForLanguageOption = {},
|
|
97
|
-
Compartment =
|
|
274
|
+
Compartment: CompartmentOption = DefaultCompartment,
|
|
275
|
+
log = noop,
|
|
276
|
+
_preload: preload = [],
|
|
277
|
+
packageConnectionsHook,
|
|
278
|
+
moduleSourceHook,
|
|
98
279
|
} = options;
|
|
99
|
-
|
|
100
280
|
const parserForLanguage = freeze(
|
|
101
281
|
assign(create(null), parserForLanguageOption),
|
|
102
282
|
);
|
|
103
283
|
|
|
104
|
-
const { read, computeSha512 } = unpackReadPowers(
|
|
284
|
+
const { read, computeSha512 } = unpackReadPowers(readPowers);
|
|
105
285
|
|
|
106
286
|
const {
|
|
107
287
|
compartments,
|
|
@@ -111,6 +291,12 @@ export const captureFromMap = async (powers, compartmentMap, options = {}) => {
|
|
|
111
291
|
/** @type {Sources} */
|
|
112
292
|
const sources = Object.create(null);
|
|
113
293
|
|
|
294
|
+
const loadCompartments = makePreloader(compartmentMap, sources, {
|
|
295
|
+
log,
|
|
296
|
+
policy,
|
|
297
|
+
_preload: preload,
|
|
298
|
+
});
|
|
299
|
+
|
|
114
300
|
const consolidatedExitModuleImportHook = exitModuleImportHookMaker({
|
|
115
301
|
modules: exitModules,
|
|
116
302
|
exitModuleImportHook,
|
|
@@ -127,26 +313,32 @@ export const captureFromMap = async (powers, compartmentMap, options = {}) => {
|
|
|
127
313
|
entryModuleSpecifier,
|
|
128
314
|
importHook: consolidatedExitModuleImportHook,
|
|
129
315
|
sourceMapHook,
|
|
316
|
+
moduleSourceHook,
|
|
130
317
|
});
|
|
318
|
+
|
|
131
319
|
// Induce importHook to record all the necessary modules to import the given module specifier.
|
|
132
|
-
const {
|
|
320
|
+
const {
|
|
321
|
+
compartment: entryCompartment,
|
|
322
|
+
compartments: linkedCompartments,
|
|
323
|
+
attenuatorsCompartment,
|
|
324
|
+
} = link(compartmentMap, {
|
|
133
325
|
resolve,
|
|
134
326
|
makeImportHook,
|
|
135
327
|
moduleTransforms,
|
|
136
328
|
syncModuleTransforms,
|
|
137
329
|
parserForLanguage,
|
|
138
330
|
archiveOnly: true,
|
|
139
|
-
Compartment,
|
|
331
|
+
Compartment: CompartmentOption,
|
|
140
332
|
});
|
|
141
|
-
await compartment.load(entryModuleSpecifier);
|
|
142
|
-
if (policy) {
|
|
143
|
-
// retain all attenuators.
|
|
144
|
-
await Promise.all(
|
|
145
|
-
detectAttenuators(policy).map(attenuatorSpecifier =>
|
|
146
|
-
attenuatorsCompartment.load(attenuatorSpecifier),
|
|
147
|
-
),
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
333
|
|
|
151
|
-
|
|
334
|
+
await loadCompartments(
|
|
335
|
+
linkedCompartments,
|
|
336
|
+
entryCompartment,
|
|
337
|
+
attenuatorsCompartment,
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
return captureCompartmentMap(compartmentMap, sources, {
|
|
341
|
+
packageConnectionsHook,
|
|
342
|
+
log,
|
|
343
|
+
});
|
|
152
344
|
};
|
package/src/compartment-map.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
/** @type {(a: string, b: string) => number} */
|
|
2
2
|
export const stringCompare: (a: string, b: string) => number;
|
|
3
|
-
export function
|
|
4
|
-
|
|
3
|
+
export function assertFileCompartmentMap(allegedCompartmentMap: unknown, url?: string): asserts allegedCompartmentMap is FileCompartmentMapDescriptor;
|
|
4
|
+
export function assertDigestedCompartmentDescriptors(allegedCompartments: unknown, url?: string): asserts allegedCompartments is Record<string, DigestedCompartmentDescriptor>;
|
|
5
|
+
export function assertDigestedCompartmentMap(allegedCompartmentMap: unknown, url?: string): asserts allegedCompartmentMap is DigestedCompartmentMapDescriptor;
|
|
6
|
+
export function assertPackageCompartmentMap(allegedCompartmentMap: unknown, url?: string): asserts allegedCompartmentMap is PackageCompartmentMapDescriptor;
|
|
7
|
+
export type AssertFn<T = string> = (value: unknown, keypath: string, url: string) => void;
|
|
8
|
+
import type { FileCompartmentMapDescriptor } from './types.js';
|
|
9
|
+
import type { DigestedCompartmentDescriptor } from './types.js';
|
|
10
|
+
import type { DigestedCompartmentMapDescriptor } from './types.js';
|
|
11
|
+
import type { PackageCompartmentMapDescriptor } from './types.js';
|
|
5
12
|
//# sourceMappingURL=compartment-map.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compartment-map.d.ts","sourceRoot":"","sources":["compartment-map.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compartment-map.d.ts","sourceRoot":"","sources":["compartment-map.js"],"names":[],"mappings":"AAyCA,+CAA+C;AAE/C,4BAFW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAE2B;AA40B/D,gEAJI,OAAO,QACP,MAAM,GACJ,QAAQ,qBAAqB,IAAI,4BAA4B,CASzE;AAQM,0EAJI,OAAO,QACP,MAAM,GACJ,QAAQ,mBAAmB,IAAI,MAAM,CAAC,MAAM,EAAE,6BAA6B,CAAC,CAUxF;AAQM,oEAJI,OAAO,QACP,MAAM,GACJ,QAAQ,qBAAqB,IAAI,gCAAgC,CAS7E;AAOM,mEAJI,OAAO,QACP,MAAM,GACJ,QAAQ,qBAAqB,IAAI,+BAA+B,CAS5E;qBApca,CAAC,aACF,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI;kDA1c1B,YAAY;mDAAZ,YAAY;sDAAZ,YAAY;qDAAZ,YAAY"}
|