Skip to content

Commit a453dec

Browse files
committed
feat(compartment-mapper)!: implement Compartment Map Transforms
BREAKING CHANGE: The `CompartmentDescriptor.label` property now represents a _canonical name_. `CompartmentDescriptor.path` has been removed. Filenames in created archives are no longer percent-encoded. Compartments no longer have implicit access to ancestors during dynamic requires. Access to the root compartment via policy is now supported using the canonical name `$root$`. Many type changes. # Conflicts: # packages/compartment-mapper/src/capture-lite.js # packages/compartment-mapper/src/policy.js # packages/compartment-mapper/test/capture-lite.test.js
1 parent bfda1d6 commit a453dec

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+4098
-1065
lines changed

packages/compartment-mapper/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"ses": "workspace:^"
6969
},
7070
"devDependencies": {
71+
"@endo/env-options": "workspace:^",
7172
"ava": "^6.1.3",
7273
"babel-eslint": "^10.1.0",
7374
"c8": "^7.14.0",

packages/compartment-mapper/src/archive-lite.js

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
/* Provides functions to create an archive (zip file with a
1+
/**
2+
* Provides functions to create an archive (zip file with a
23
* compartment-map.json) from a partially completed compartment map (it must
34
* mention all packages/compartments as well as inter-compartment references
45
* but does not contain an entry for every module reachable from its entry
@@ -26,6 +27,8 @@
2627
* In fruition of https://github.com/endojs/endo/issues/400, we will be able to
2728
* use original source archives on XS and Node.js, but not on the web until the
2829
* web platform makes further progress on virtual module loaers.
30+
*
31+
* @module
2932
*/
3033

3134
/* eslint no-shadow: 0 */
@@ -36,8 +39,8 @@
3639
* ArchiveResult,
3740
* ArchiveWriter,
3841
* CaptureSourceLocationHook,
39-
* CompartmentMapDescriptor,
4042
* HashPowers,
43+
* PackageCompartmentMapDescriptor,
4144
* ReadFn,
4245
* ReadPowers,
4346
* Sources,
@@ -58,16 +61,7 @@ import { digestCompartmentMap } from './digest.js';
5861

5962
const textEncoder = new TextEncoder();
6063

61-
const { assign, create, freeze } = Object;
62-
63-
/**
64-
* @param {string} rel - a relative URL
65-
* @param {string} abs - a fully qualified URL
66-
* @returns {string}
67-
*/
68-
const resolveLocation = (rel, abs) => new URL(rel, abs).toString();
69-
70-
const { keys } = Object;
64+
const { assign, create, freeze, keys } = Object;
7165

7266
/**
7367
* @param {ArchiveWriter} archive
@@ -77,12 +71,10 @@ const addSourcesToArchive = async (archive, sources) => {
7771
await null;
7872
for (const compartment of keys(sources).sort()) {
7973
const modules = sources[compartment];
80-
const compartmentLocation = resolveLocation(`${compartment}/`, 'file:///');
8174
for (const specifier of keys(modules).sort()) {
82-
const { bytes, location } = modules[specifier];
83-
if (location !== undefined) {
84-
const moduleLocation = resolveLocation(location, compartmentLocation);
85-
const path = new URL(moduleLocation).pathname.slice(1); // elide initial "/"
75+
if ('location' in modules[specifier]) {
76+
const { bytes, location } = modules[specifier];
77+
const path = `${compartment}/${location}`;
8678
if (bytes !== undefined) {
8779
// eslint-disable-next-line no-await-in-loop
8880
await archive.write(path, bytes);
@@ -100,16 +92,16 @@ const captureSourceLocations = async (sources, captureSourceLocation) => {
10092
for (const compartmentName of keys(sources).sort()) {
10193
const modules = sources[compartmentName];
10294
for (const moduleSpecifier of keys(modules).sort()) {
103-
const { sourceLocation } = modules[moduleSpecifier];
104-
if (sourceLocation !== undefined) {
95+
if ('sourceLocation' in modules[moduleSpecifier]) {
96+
const { sourceLocation } = modules[moduleSpecifier];
10597
captureSourceLocation(compartmentName, moduleSpecifier, sourceLocation);
10698
}
10799
}
108100
}
109101
};
110102

111103
/**
112-
* @param {CompartmentMapDescriptor} compartmentMap
104+
* @param {PackageCompartmentMapDescriptor} compartmentMap
113105
* @param {Sources} sources
114106
* @returns {ArchiveResult}
115107
*/
@@ -130,9 +122,11 @@ export const makeArchiveCompartmentMap = (compartmentMap, sources) => {
130122
};
131123
};
132124

125+
const noop = () => {};
126+
133127
/**
134128
* @param {ReadFn | ReadPowers} powers
135-
* @param {CompartmentMapDescriptor} compartmentMap
129+
* @param {PackageCompartmentMapDescriptor} compartmentMap
136130
* @param {ArchiveLiteOptions} [options]
137131
* @returns {Promise<{sources: Sources, compartmentMapBytes: Uint8Array, sha512?: string}>}
138132
*/
@@ -146,6 +140,7 @@ const digestFromMap = async (powers, compartmentMap, options = {}) => {
146140
policy = undefined,
147141
sourceMapHook = undefined,
148142
parserForLanguage: parserForLanguageOption = {},
143+
log: _log = noop,
149144
} = options;
150145

151146
const parserForLanguage = freeze(
@@ -179,6 +174,7 @@ const digestFromMap = async (powers, compartmentMap, options = {}) => {
179174
importHook: consolidatedExitModuleImportHook,
180175
sourceMapHook,
181176
});
177+
182178
// Induce importHook to record all the necessary modules to import the given module specifier.
183179
const { compartment, attenuatorsCompartment } = link(compartmentMap, {
184180
resolve,
@@ -229,7 +225,7 @@ const digestFromMap = async (powers, compartmentMap, options = {}) => {
229225

230226
/**
231227
* @param {ReadFn | ReadPowers} powers
232-
* @param {CompartmentMapDescriptor} compartmentMap
228+
* @param {PackageCompartmentMapDescriptor} compartmentMap
233229
* @param {ArchiveLiteOptions} [options]
234230
* @returns {Promise<{bytes: Uint8Array, sha512?: string}>}
235231
*/
@@ -254,7 +250,7 @@ export const makeAndHashArchiveFromMap = async (
254250

255251
/**
256252
* @param {ReadFn | ReadPowers} powers
257-
* @param {CompartmentMapDescriptor} compartmentMap
253+
* @param {PackageCompartmentMapDescriptor} compartmentMap
258254
* @param {ArchiveLiteOptions} [options]
259255
* @returns {Promise<Uint8Array>}
260256
*/
@@ -269,7 +265,7 @@ export const makeArchiveFromMap = async (powers, compartmentMap, options) => {
269265

270266
/**
271267
* @param {ReadFn | ReadPowers} powers
272-
* @param {CompartmentMapDescriptor} compartmentMap
268+
* @param {PackageCompartmentMapDescriptor} compartmentMap
273269
* @param {ArchiveLiteOptions} [options]
274270
* @returns {Promise<Uint8Array>}
275271
*/
@@ -284,7 +280,7 @@ export const mapFromMap = async (powers, compartmentMap, options) => {
284280

285281
/**
286282
* @param {HashPowers} powers
287-
* @param {CompartmentMapDescriptor} compartmentMap
283+
* @param {PackageCompartmentMapDescriptor} compartmentMap
288284
* @param {ArchiveLiteOptions} [options]
289285
* @returns {Promise<string>}
290286
*/
@@ -302,7 +298,7 @@ export const hashFromMap = async (powers, compartmentMap, options) => {
302298
* @param {WriteFn} write
303299
* @param {ReadFn | ReadPowers} readPowers
304300
* @param {string} archiveLocation
305-
* @param {CompartmentMapDescriptor} compartmentMap
301+
* @param {PackageCompartmentMapDescriptor} compartmentMap
306302
* @param {ArchiveLiteOptions} [options]
307303
*/
308304
export const writeArchiveFromMap = async (

packages/compartment-mapper/src/archive.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* ReadPowers,
2525
* HashPowers,
2626
* WriteFn,
27+
* LogFn,
2728
* } from './types.js'
2829
*/
2930

@@ -169,6 +170,9 @@ export const mapLocation = async (powers, moduleLocation, options = {}) => {
169170
});
170171
};
171172

173+
/** @type {LogFn} */
174+
const noop = () => {};
175+
172176
/**
173177
* @param {HashPowers} powers
174178
* @param {string} moduleLocation
@@ -191,10 +195,12 @@ export const hashLocation = async (powers, moduleLocation, options = {}) => {
191195
workspaceLanguageForExtension,
192196
workspaceCommonjsLanguageForExtension,
193197
workspaceModuleLanguageForExtension,
198+
log = noop,
194199
...otherOptions
195200
} = assignParserForLanguage(options);
196201

197202
const compartmentMap = await mapNodeModules(powers, moduleLocation, {
203+
log,
198204
dev,
199205
strict,
200206
conditions,
@@ -212,6 +218,7 @@ export const hashLocation = async (powers, moduleLocation, options = {}) => {
212218
return hashFromMap(powers, compartmentMap, {
213219
parserForLanguage,
214220
policy,
221+
log,
215222
...otherOptions,
216223
});
217224
};

packages/compartment-mapper/src/bundle-lite.js

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
* } from 'ses'
88
* @import {
99
* BundleOptions,
10-
* CompartmentDescriptor,
11-
* CompartmentMapDescriptor,
1210
* CompartmentSources,
11+
* PackageCompartmentDescriptors,
12+
* PackageCompartmentMapDescriptor,
1313
* MaybeReadPowers,
1414
* ReadFn,
1515
* ReadPowers,
@@ -110,6 +110,11 @@ import { defaultParserForLanguage } from './archive-parsers.js';
110110
import mjsSupport from './bundle-mjs.js';
111111
import cjsSupport from './bundle-cjs.js';
112112
import jsonSupport from './bundle-json.js';
113+
import {
114+
isErrorModuleSource,
115+
isExitModuleSource,
116+
isLocalModuleSource,
117+
} from './guards.js';
113118

114119
const { quote: q } = assert;
115120

@@ -144,7 +149,7 @@ null,
144149
* The first modules are place-holders for the modules that exit
145150
* the compartment map to the host's module system.
146151
*
147-
* @param {Record<string, CompartmentDescriptor>} compartmentDescriptors
152+
* @param {PackageCompartmentDescriptors} compartmentDescriptors
148153
* @param {Record<string, CompartmentSources>} compartmentSources
149154
* @param {string} entryCompartmentName
150155
* @param {string} entryModuleSpecifier
@@ -188,28 +193,18 @@ const sortedModules = (
188193

189194
const source = compartmentSources[compartmentName][moduleSpecifier];
190195
if (source !== undefined) {
191-
const { record, parser, deferredError, bytes, sourceDirname, exit } =
192-
source;
193-
if (exit !== undefined) {
194-
return exit;
195-
}
196-
assert(
197-
bytes !== undefined,
198-
`No bytes for ${moduleSpecifier} in ${compartmentName}`,
199-
);
200-
assert(
201-
parser !== undefined,
202-
`No parser for ${moduleSpecifier} in ${compartmentName}`,
203-
);
204-
assert(
205-
sourceDirname !== undefined,
206-
`No sourceDirname for ${moduleSpecifier} in ${compartmentName}`,
207-
);
208-
if (deferredError) {
196+
if (isErrorModuleSource(source)) {
209197
throw Error(
210-
`Cannot bundle: encountered deferredError ${deferredError}`,
198+
`Cannot bundle: encountered deferredError ${source.deferredError}`,
211199
);
212200
}
201+
if (isExitModuleSource(source)) {
202+
return source.exit;
203+
}
204+
if (!isLocalModuleSource(source)) {
205+
throw new TypeError(`Unexpected source type ${JSON.stringify(source)}`);
206+
}
207+
const { record, parser, bytes, sourceDirname } = source;
213208
if (record) {
214209
const { imports = [], reexports = [] } =
215210
/** @type {PrecompiledStaticModuleInterface} */ (record);
@@ -309,7 +304,7 @@ const getBundlerKitForModule = (module, params) => {
309304

310305
/**
311306
* @param {ReadFn | ReadPowers | MaybeReadPowers} readPowers
312-
* @param {CompartmentMapDescriptor} compartmentMap
307+
* @param {PackageCompartmentMapDescriptor} compartmentMap
313308
* @param {BundleOptions} [options]
314309
* @returns {Promise<string>}
315310
*/
@@ -651,7 +646,7 @@ ${m.bundlerKit.getFunctor()}`,
651646

652647
/**
653648
* @param {ReadFn | ReadPowers | MaybeReadPowers} readPowers
654-
* @param {CompartmentMapDescriptor} compartmentMap
649+
* @param {PackageCompartmentMapDescriptor} compartmentMap
655650
* @param {BundleOptions} [options]
656651
* @returns {Promise<string>}
657652
*/

packages/compartment-mapper/src/bundle.js

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
* } from 'ses'
88
* @import {
99
* BundleOptions,
10-
* CompartmentDescriptor,
11-
* CompartmentMapDescriptor,
1210
* CompartmentSources,
11+
* PackageCompartmentDescriptors,
12+
* PackageCompartmentMapDescriptor,
1313
* MaybeReadPowers,
1414
* ReadFn,
1515
* ReadPowers,
@@ -112,6 +112,11 @@ import { defaultParserForLanguage } from './archive-parsers.js';
112112
import mjsSupport from './bundle-mjs.js';
113113
import cjsSupport from './bundle-cjs.js';
114114
import jsonSupport from './bundle-json.js';
115+
import {
116+
isErrorModuleSource,
117+
isExitModuleSource,
118+
isLocalModuleSource,
119+
} from './guards.js';
115120

116121
const textEncoder = new TextEncoder();
117122

@@ -148,7 +153,7 @@ null,
148153
* The first modules are place-holders for the modules that exit
149154
* the compartment map to the host's module system.
150155
*
151-
* @param {Record<string, CompartmentDescriptor>} compartmentDescriptors
156+
* @param {PackageCompartmentDescriptors} compartmentDescriptors
152157
* @param {Record<string, CompartmentSources>} compartmentSources
153158
* @param {string} entryCompartmentName
154159
* @param {string} entryModuleSpecifier
@@ -192,28 +197,18 @@ const sortedModules = (
192197

193198
const source = compartmentSources[compartmentName][moduleSpecifier];
194199
if (source !== undefined) {
195-
const { record, parser, deferredError, bytes, sourceDirname, exit } =
196-
source;
197-
if (exit !== undefined) {
198-
return exit;
199-
}
200-
assert(
201-
bytes !== undefined,
202-
`No bytes for ${moduleSpecifier} in ${compartmentName}`,
203-
);
204-
assert(
205-
parser !== undefined,
206-
`No parser for ${moduleSpecifier} in ${compartmentName}`,
207-
);
208-
assert(
209-
sourceDirname !== undefined,
210-
`No sourceDirname for ${moduleSpecifier} in ${compartmentName}`,
211-
);
212-
if (deferredError) {
200+
if (isErrorModuleSource(source)) {
213201
throw Error(
214-
`Cannot bundle: encountered deferredError ${deferredError}`,
202+
`Cannot bundle: encountered deferredError ${source.deferredError}`,
215203
);
216204
}
205+
if (isExitModuleSource(source)) {
206+
return source.exit;
207+
}
208+
if (!isLocalModuleSource(source)) {
209+
throw new TypeError(`Unexpected source type ${JSON.stringify(source)}`);
210+
}
211+
const { record, parser, bytes, sourceDirname } = source;
217212
if (record) {
218213
const { imports = [], reexports = [] } =
219214
/** @type {PrecompiledStaticModuleInterface} */ (record);
@@ -314,7 +309,7 @@ const getBundlerKitForModule = (module, params) => {
314309

315310
/**
316311
* @param {ReadFn | ReadPowers | MaybeReadPowers} readPowers
317-
* @param {CompartmentMapDescriptor} compartmentMap
312+
* @param {PackageCompartmentMapDescriptor} compartmentMap
318313
* @param {BundleOptions} [options]
319314
* @returns {Promise<string>}
320315
*/
@@ -657,7 +652,7 @@ ${m.bundlerKit.getFunctor()}`,
657652

658653
/**
659654
* @param {ReadFn | ReadPowers | MaybeReadPowers} readPowers
660-
* @param {CompartmentMapDescriptor} compartmentMap
655+
* @param {PackageCompartmentMapDescriptor} compartmentMap
661656
* @param {BundleOptions} [options]
662657
* @returns {Promise<string>}
663658
*/

0 commit comments

Comments
 (0)