@endo/compartment-mapper 1.5.0 → 1.6.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/README.md +278 -111
- package/bundle.d.ts +1 -1
- package/bundle.js +4 -1
- package/functor-lite.d.ts +3 -0
- package/functor-lite.d.ts.map +1 -0
- package/functor-lite.js +4 -0
- package/functor.d.ts +3 -0
- package/functor.d.ts.map +1 -0
- package/functor.js +4 -0
- package/index.d.ts +1 -1
- package/index.js +4 -1
- package/package.json +8 -4
- package/script-lite.d.ts +3 -0
- package/script-lite.d.ts.map +1 -0
- package/script-lite.js +4 -0
- package/script.d.ts +3 -0
- package/script.d.ts.map +1 -0
- package/script.js +4 -0
- package/src/archive-lite.js +1 -1
- package/src/bundle-cjs.d.ts +1 -1
- package/src/bundle-cjs.d.ts.map +1 -1
- package/src/bundle-cjs.js +57 -28
- package/src/bundle-json.d.ts.map +1 -1
- package/src/bundle-json.js +2 -3
- package/src/bundle-lite.d.ts +91 -0
- package/src/bundle-lite.d.ts.map +1 -0
- package/src/bundle-lite.js +668 -0
- package/src/bundle-mjs.d.ts +2 -2
- package/src/bundle-mjs.d.ts.map +1 -1
- package/src/bundle-mjs.js +36 -19
- package/src/bundle.d.ts +48 -10
- package/src/bundle.d.ts.map +1 -1
- package/src/bundle.js +391 -125
- package/src/capture-lite.js +1 -1
- package/src/import-hook.d.ts +3 -16
- package/src/import-hook.d.ts.map +1 -1
- package/src/import-hook.js +11 -18
- package/src/import-lite.d.ts.map +1 -1
- package/src/import-lite.js +4 -2
- package/src/node-modules.d.ts +4 -47
- package/src/node-modules.d.ts.map +1 -1
- package/src/node-modules.js +121 -121
- package/src/parse-archive-cjs.d.ts.map +1 -1
- package/src/parse-archive-cjs.js +8 -3
- package/src/parse-cjs-shared-export-wrapper.d.ts.map +1 -1
- package/src/parse-cjs-shared-export-wrapper.js +2 -10
- package/src/parse-cjs.js +1 -1
- package/src/parse-mjs.js +2 -2
- package/src/policy.d.ts.map +1 -1
- package/src/policy.js +4 -7
- package/src/search.d.ts +6 -12
- package/src/search.d.ts.map +1 -1
- package/src/search.js +29 -12
- package/src/types/compartment-map-schema.d.ts +5 -0
- package/src/types/compartment-map-schema.d.ts.map +1 -1
- package/src/types/compartment-map-schema.ts +5 -0
- package/src/types/external.d.ts +112 -8
- package/src/types/external.d.ts.map +1 -1
- package/src/types/external.ts +127 -8
- package/src/types/internal.d.ts +85 -13
- package/src/types/internal.d.ts.map +1 -1
- package/src/types/internal.ts +106 -13
- package/src/types/node-modules.d.ts +79 -0
- package/src/types/node-modules.d.ts.map +1 -0
- package/src/types/node-modules.ts +89 -0
- package/src/types/node-powers.d.ts +4 -4
- package/src/types/node-powers.d.ts.map +1 -1
- package/src/types/node-powers.ts +4 -4
- package/src/types/powers.d.ts +2 -2
- package/src/types/powers.d.ts.map +1 -1
- package/src/types/powers.ts +2 -2
package/README.md
CHANGED
|
@@ -11,8 +11,10 @@ attacks and some supply chain attacks.
|
|
|
11
11
|
Since most Node.js packages do not modify objects in global scope,
|
|
12
12
|
many libraries and applications work in Compartments without modification.
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
## Evaluating an application from a file system
|
|
15
|
+
|
|
16
|
+
The `importLocation` function evaluates a compartmentalized application off the
|
|
17
|
+
file system.
|
|
16
18
|
The `globals` are properties to add to the `globalThis` in the global scope
|
|
17
19
|
of the application's main package compartment.
|
|
18
20
|
The `modules` are built-in modules to grant the application's main package
|
|
@@ -20,23 +22,20 @@ compartment.
|
|
|
20
22
|
|
|
21
23
|
```js
|
|
22
24
|
import fs from "fs";
|
|
25
|
+
import { fileURLToPath } from "url";
|
|
23
26
|
import { importLocation } from "@endo/compartment-mapper";
|
|
24
27
|
|
|
25
28
|
// ...
|
|
26
29
|
|
|
27
|
-
const
|
|
28
|
-
const globals = { console };
|
|
29
|
-
|
|
30
|
-
const read = async location =>
|
|
31
|
-
fs.promises.readFile(new URL(location).pathname);
|
|
30
|
+
const read = async location => fs.promises.readFile(fileURLToPath(location));
|
|
32
31
|
|
|
33
|
-
const { namespace } = await importLocation(
|
|
32
|
+
const { namespace: moduleExports } = await importLocation(
|
|
34
33
|
read,
|
|
35
|
-
|
|
34
|
+
moduleSpecifier,
|
|
36
35
|
{
|
|
37
|
-
globals,
|
|
38
|
-
modules
|
|
39
|
-
}
|
|
36
|
+
globals: { console },
|
|
37
|
+
modules: { fs },
|
|
38
|
+
},
|
|
40
39
|
);
|
|
41
40
|
```
|
|
42
41
|
|
|
@@ -46,93 +45,264 @@ The application using the compartment mapper is responsible for applying the
|
|
|
46
45
|
necessary).
|
|
47
46
|
The compartment mapper is also not coupled specifically to Node.js IO and does
|
|
48
47
|
not import any powerful modules like `fs`.
|
|
49
|
-
The
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
The caller must provide read powers in the first argument as either a ReadPowers
|
|
49
|
+
object or as a standalone `read` function. ReadPowers has optional functions
|
|
50
|
+
which can be used to unlock compatibility features. When `fileURLToPath` is
|
|
51
|
+
available, `__dirname` and `__filename` will be provided to CJS modules. When
|
|
52
|
+
`requireResolve` is available, it will be called whenever a CJS module calls
|
|
53
|
+
[`require.resolve()`].
|
|
55
54
|
|
|
56
|
-
```
|
|
55
|
+
```ts
|
|
57
56
|
type ReadPowers = {
|
|
58
57
|
read: (location: string) => Promise<Uint8Array>,
|
|
59
58
|
canonical: (location: string) => Promise<string>,
|
|
60
|
-
computeSha512
|
|
61
|
-
fileURLToPath
|
|
62
|
-
pathToFileURL
|
|
63
|
-
requireResolve
|
|
59
|
+
computeSha512?: (bytes: Uint8Array) => string,
|
|
60
|
+
fileURLToPath?: (location: string | URL) => string,
|
|
61
|
+
pathToFileURL?: (path: string) => URL,
|
|
62
|
+
requireResolve?: (
|
|
63
|
+
fromLocation: string,
|
|
64
|
+
specifier: string,
|
|
65
|
+
options?: { paths?: string[] },
|
|
66
|
+
) => string
|
|
64
67
|
}
|
|
65
68
|
```
|
|
66
69
|
|
|
67
|
-
>
|
|
68
|
-
>
|
|
69
|
-
>
|
|
70
|
-
> of globals and built-in modules to third-party packages within the
|
|
70
|
+
> [!NOTE]
|
|
71
|
+
> TODO: A future version will allow application authors to distribute their
|
|
72
|
+
> choices of globals and built-in modules to third-party packages within the
|
|
71
73
|
> application, as with [LavaMoat].
|
|
72
74
|
|
|
73
|
-
The `importLocation` function uses `loadLocation`.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
The `importLocation` function internally uses `loadLocation`.
|
|
76
|
+
Use `loadLocation` to defer execution or evaluate multiple times with varying
|
|
77
|
+
globals or modules in the same process.
|
|
78
|
+
`loadLocation` returns an Application object with an
|
|
77
79
|
`import({ globals?, modules? })` method.
|
|
78
80
|
|
|
81
|
+
## Writing an application archive
|
|
82
|
+
|
|
79
83
|
Use `writeArchive` to capture an application in an archival format.
|
|
80
84
|
Archives are `zip` files with a `compartment-map.json` manifest file.
|
|
81
85
|
|
|
82
86
|
```js
|
|
83
87
|
import fs from "fs";
|
|
88
|
+
import { fileURLToPath } from "url";
|
|
84
89
|
import { writeArchive } from "@endo/compartment-mapper";
|
|
85
90
|
|
|
86
|
-
const read = async location =>
|
|
87
|
-
fs.promises.readFile(new URL(location).pathname);
|
|
91
|
+
const read = async location => fs.promises.readFile(fileURLToPath(location));
|
|
88
92
|
const write = async (location, content) =>
|
|
89
|
-
fs.promises.writeFile(
|
|
93
|
+
fs.promises.writeFile(fileURLToPath(location), content);
|
|
90
94
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
);
|
|
95
|
+
const moduleSpecifier = new URL('app.js', import.meta.url).toString();
|
|
96
|
+
const archiveLocation = new URL('app.zip', import.meta.url).toString();
|
|
97
|
+
|
|
98
|
+
// Write to `archiveLocation`.
|
|
99
|
+
await writeArchive(write, read, archiveLocation, moduleSpecifier);
|
|
97
100
|
```
|
|
98
101
|
|
|
99
|
-
The `writeArchive` function uses `makeArchive`.
|
|
102
|
+
The `writeArchive` function internally uses `makeArchive`.
|
|
100
103
|
Using `makeArchive` directly gives you the archive bytes.
|
|
101
104
|
|
|
105
|
+
## Evaluating an application from an archive
|
|
106
|
+
|
|
102
107
|
Use `importArchive` to run an application from an archive.
|
|
103
108
|
Note the similarity to `importLocation`.
|
|
104
109
|
|
|
105
110
|
```js
|
|
106
111
|
import fs from "fs";
|
|
112
|
+
import { fileURLToPath } from "url";
|
|
107
113
|
import { importArchive } from "@endo/compartment-mapper";
|
|
108
114
|
|
|
109
115
|
// ...
|
|
110
116
|
|
|
111
|
-
const
|
|
112
|
-
const globals = { console };
|
|
117
|
+
const read = async location => fs.promises.readFile(fileURLToPath(location));
|
|
113
118
|
|
|
114
|
-
const
|
|
115
|
-
fs.promises.readFile(new URL(location).pathname);
|
|
116
|
-
|
|
117
|
-
const { namespace } = await importArchive(
|
|
119
|
+
const { namespace: moduleExports } = await importArchive(
|
|
118
120
|
read,
|
|
119
121
|
archiveLocation,
|
|
120
122
|
{
|
|
121
|
-
globals,
|
|
122
|
-
modules
|
|
123
|
-
}
|
|
123
|
+
globals: { console },
|
|
124
|
+
modules: { fs },
|
|
125
|
+
},
|
|
124
126
|
);
|
|
125
127
|
```
|
|
126
128
|
|
|
127
|
-
The `importArchive` function composes `loadArchive` and
|
|
129
|
+
The `importArchive` function internally composes `loadArchive` and
|
|
130
|
+
`parseArchive`.
|
|
128
131
|
Use `loadArchive` to defer execution or run multiple times with varying
|
|
129
|
-
globals.
|
|
132
|
+
globals or modules in the same process.
|
|
130
133
|
Use `parseArchive` to construct a runner from the bytes of an archive.
|
|
131
|
-
|
|
132
|
-
|
|
134
|
+
`loadArchive` and `parseArchive` return an Application object with an
|
|
135
|
+
`import({ globals?, modules? })` method.
|
|
136
|
+
|
|
137
|
+
`loadArchive` and `parseArchive` do not run the archived application,
|
|
138
|
+
so they can be used to safely check its hash.
|
|
139
|
+
|
|
140
|
+
# Script bundles
|
|
141
|
+
|
|
142
|
+
From `@endo/compartment-mapper/script.js`, the `makeScript` function is similar
|
|
143
|
+
to `makeArchive` but generates a string of JavaScript suitable for `eval` or
|
|
144
|
+
embedding in a web page with a `<script>`.
|
|
145
|
+
Endo uses this "bundle" format to bootstrap an environment up to the point it
|
|
146
|
+
can call `importArchive`, so bundles are at least suitable for creating a
|
|
147
|
+
script that subsumes `ses`, `@endo/compartment-mapper/import-archive.js`, and
|
|
148
|
+
other parts of Endo, but is not as feature-complete as `importArchive`.
|
|
133
149
|
|
|
134
|
-
|
|
135
|
-
|
|
150
|
+
```js
|
|
151
|
+
import url from "url";
|
|
152
|
+
import fs from "fs";
|
|
153
|
+
import { makeScript } from "@endo/compartment-mapper/script.js";
|
|
154
|
+
import { makeReadPowers } from "@endo/compartment-mapper/node-powers.js";
|
|
155
|
+
const readPowers = makeReadPowers({ fs, url });
|
|
156
|
+
const options = {}; // if any
|
|
157
|
+
const script = await makeScript(readPowers, moduleSpecifier, options);
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The script is suitable for evaluating as a script in a web environment.
|
|
161
|
+
The script is in UTF-8 format and uses non-ASCII characters, so may require
|
|
162
|
+
headers or tags to specify the encoding.
|
|
163
|
+
|
|
164
|
+
```html
|
|
165
|
+
<meta charset="utf-8">
|
|
166
|
+
<script src="script.js"></script>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Evaluation of `script` returns the emulated exports namespace of the entry
|
|
170
|
+
module.
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
const script = await makeScript(readPowers, moduleSpecifier, options);
|
|
174
|
+
|
|
175
|
+
// This one weird trick evaluates your script in global scope instead of
|
|
176
|
+
// lexical scope.
|
|
177
|
+
const globalEval = eval;
|
|
178
|
+
const moduleExports = globalEval(script);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Scripts can include ESM, CJS, and JSON modules, but no other module languages
|
|
182
|
+
like bytes or text.
|
|
183
|
+
|
|
184
|
+
> [!WARNING]
|
|
185
|
+
> Scripts do not support [live
|
|
186
|
+
> bindings](https://developer.mozilla.org/en-US/docs/Glossary/Binding), dynamic
|
|
187
|
+
> `import`, or `import.meta`.
|
|
188
|
+
> Scripts do not isolate modules to a compartment.
|
|
189
|
+
|
|
190
|
+
`makeScript` accepts all the options of `makeArchive` and:
|
|
191
|
+
|
|
192
|
+
- `sourceUrlPrefix` (string, default `""`):
|
|
193
|
+
Specifies a prefix to occur on each module's `sourceURL` comment, as injected
|
|
194
|
+
at runtime.
|
|
195
|
+
Should generally end with `/` if non-empty.
|
|
196
|
+
This can improve stack traces.
|
|
197
|
+
- `format` (`"cjs"` or `undefined`, default `undefined`):
|
|
198
|
+
By default, `makeBundle` generates a bundle that can be evaluated in any
|
|
199
|
+
context.
|
|
200
|
+
By specifying `"cjs"`, the bundle can assume there is a host CommonJS
|
|
201
|
+
`require` function available for resolving modules that exit the bundle.
|
|
202
|
+
The default is `require` on `globalThis`.
|
|
203
|
+
The `require` function can be overridden with a curried runtime option.
|
|
204
|
+
- `useEvaluate` (boolean, default `false`):
|
|
205
|
+
Disabled by default, for bundles that may be embedded on a web page with a
|
|
206
|
+
`no-unsafe-eval` Content Security Policy.
|
|
207
|
+
Enable for any environment that can use `eval` or other suitable evaluator
|
|
208
|
+
(like a Hardened JavaScript `Compartment`).
|
|
209
|
+
|
|
210
|
+
By default and when `useEvaluate` is explicitly `false`, the text of a module
|
|
211
|
+
includes an array of module evaluator functions.
|
|
212
|
+
|
|
213
|
+
> [!WARNING]
|
|
214
|
+
> Example is illustrative and neither a compatibility guarantee nor even
|
|
215
|
+
> precise.
|
|
216
|
+
|
|
217
|
+
```js
|
|
218
|
+
(modules => options => {
|
|
219
|
+
/* ...linker runtime... */
|
|
220
|
+
for (const module of modules) {
|
|
221
|
+
module(/* linking convention */);
|
|
222
|
+
}
|
|
223
|
+
)([
|
|
224
|
+
// 1. bundle ./dependency.js
|
|
225
|
+
function () { /* ... */ },
|
|
226
|
+
// 2. bundle ./dependent.js
|
|
227
|
+
function () { /* ... */ },
|
|
228
|
+
])(/* runtime options */)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Each of these functions is generated by [Endo's emulation of a JavaScript
|
|
232
|
+
`ModuleSource`
|
|
233
|
+
constructor](https://github.com/endojs/endo/blob/master/packages/module-source/DESIGN.md),
|
|
234
|
+
which we use elsewhere in the Compartment Mapper to emulate Compartment
|
|
235
|
+
module systems at runtime, as in the Compartment Mapper's own `importArchive`.
|
|
236
|
+
|
|
237
|
+
With `useEvaluate`, the script instead embeds the text for each module as a
|
|
238
|
+
string, along with a package-relative source URL, and uses an `eval` function
|
|
239
|
+
to produce the corresponding `function`.
|
|
240
|
+
|
|
241
|
+
```js
|
|
242
|
+
(modules => options => {
|
|
243
|
+
/* ...linker runtime... */
|
|
244
|
+
for (const [module, sourceURL] of modules) {
|
|
245
|
+
evalWithSourceURL(module, sourceURL)(/* linking convention */);
|
|
246
|
+
}
|
|
247
|
+
)([
|
|
248
|
+
// 1. bundle ./dependency.js
|
|
249
|
+
["(function () { /* ... */ })", "bundle/dependency.js"],
|
|
250
|
+
// 2. bundle ./dependent.js
|
|
251
|
+
["(function () { /* ... */ })", "bundle/dependent.js"],
|
|
252
|
+
])(/* runtime options */)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
With `useEvaluate`, the bundle will instead capture a string for
|
|
256
|
+
each module function and use an indirect `eval` to revive them.
|
|
257
|
+
This can make the file locations and line numbers in stack traces more
|
|
258
|
+
useful.
|
|
259
|
+
|
|
260
|
+
From `@endo/compartment-mapper/script-lite.js`, the `makeScriptFromMap` takes
|
|
261
|
+
a compartment map, like that generated by `mapNodeModules` in
|
|
262
|
+
`@endo/compartment-mapper/node-modules.js` instead of the entry module's
|
|
263
|
+
location.
|
|
264
|
+
The `-lite.js` modules, in general, do not entrain a specific compartment
|
|
265
|
+
mapper.
|
|
266
|
+
|
|
267
|
+
# Functor bundles
|
|
268
|
+
|
|
269
|
+
From `@endo/compartment-mapper/functor.js`, the `makeFunctor` function is similar
|
|
270
|
+
to `makeScript` but generates a string of JavaScript suitable for `eval` but *not*
|
|
271
|
+
suitable for embedding as a script. But, the completion value of the script
|
|
272
|
+
is a function that accepts runtime options and returns the entry module's emulated
|
|
273
|
+
module exports namespace, adding a level of indirection.
|
|
274
|
+
|
|
275
|
+
In this example, we use a Hardened JavaScript `Compartment` to confine the
|
|
276
|
+
execution of the functor and its modules.
|
|
277
|
+
|
|
278
|
+
```js
|
|
279
|
+
const functorScript = await makeFunctor(readPowers, moduleSpecifier, options);
|
|
280
|
+
const compartment = new Compartment();
|
|
281
|
+
const moduleExports = compartment.evaluate(functorScript)({
|
|
282
|
+
require,
|
|
283
|
+
evaluate: compartment.evaluate,
|
|
284
|
+
sourceUrlPrefix: 'file:///Users/you/project/',
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
The functor runtime options include:
|
|
289
|
+
|
|
290
|
+
- `evaluate`: for functors made with `useEvaluate`,
|
|
291
|
+
specifies a function to use to evaluate each module.
|
|
292
|
+
The default evaluator is indirect `eval`.
|
|
293
|
+
- `require`: for functors made with `format` of `"cjs"`, provides the behavior
|
|
294
|
+
for `require` calls that exit the bundle to the host environment.
|
|
295
|
+
Defaults to the `require` in lexical scope.
|
|
296
|
+
- `sourceUrlPrefix`: specifies a prefix to occur on each module's `sourceURL` comment,
|
|
297
|
+
as injected at runtime.
|
|
298
|
+
Overrides the `sourceUrlPrefix` provided to `makeFunctor`, if any.
|
|
299
|
+
|
|
300
|
+
From `@endo/compartment-mapper/functor-lite.js`, the `makeFunctorFromMap` takes
|
|
301
|
+
a compartment map, like that generated by `mapNodeModules` in
|
|
302
|
+
`@endo/compartment-mapper/node-modules.js` instead of the entry module's
|
|
303
|
+
location.
|
|
304
|
+
The `-lite.js` modules, in general, do not entrain a specific compartment
|
|
305
|
+
mapper.
|
|
136
306
|
|
|
137
307
|
# Package Descriptors
|
|
138
308
|
|
|
@@ -164,7 +334,7 @@ in a parent directory, under `node_modules`.
|
|
|
164
334
|
The `main`, `browser`, and `exports` properties determine the modules each
|
|
165
335
|
package exports to other compartments.
|
|
166
336
|
|
|
167
|
-
The `exports` property describes [package entry points]
|
|
337
|
+
The `exports` property describes [package entry points] and can be influenced
|
|
168
338
|
by build _conditions_.
|
|
169
339
|
Currently, the only conditions supported by the compartment mapper are
|
|
170
340
|
`import`, `browser`, and `endo`.
|
|
@@ -177,13 +347,11 @@ The `endo` condition only indicates that this tool is in use.
|
|
|
177
347
|
If no `exports` apply to the root of the compartment namespace (`"."`),
|
|
178
348
|
the `main` property serves as a default.
|
|
179
349
|
|
|
180
|
-
>
|
|
181
|
-
>
|
|
182
|
-
> A future version may also respect the `imports` property.
|
|
350
|
+
> [!NOTE]
|
|
351
|
+
> TODO: A future version may also respect the `imports` property.
|
|
183
352
|
|
|
184
|
-
>
|
|
185
|
-
>
|
|
186
|
-
> A future version may also respect wildcard patterns in `exports` and
|
|
353
|
+
> [!NOTE]
|
|
354
|
+
> TODO: A future version may also respect wildcard patterns in `exports` and
|
|
187
355
|
> `imports`.
|
|
188
356
|
|
|
189
357
|
The `files` property indicates all of the files in the package that
|
|
@@ -195,10 +363,9 @@ With the compartment mapper, just as in Node.js, a module specifier that has no
|
|
|
195
363
|
extension may refer either to the file with the `js` extension, or if that file
|
|
196
364
|
does not exist, to the `index.js` file in the directory with the same name.
|
|
197
365
|
|
|
198
|
-
>
|
|
199
|
-
>
|
|
200
|
-
>
|
|
201
|
-
> future version of the compartment mapper will collect these in archives.
|
|
366
|
+
> [!NOTE]
|
|
367
|
+
> TODO: The compartment mapper does not yet do anything with the `files` globs
|
|
368
|
+
> but a future version of the compartment mapper will collect these in archives.
|
|
202
369
|
> The compartment mapper should eventually provide the means for any
|
|
203
370
|
> compartment to access its own files using an attenuated `fs` module or
|
|
204
371
|
> `fetch` global, in conjunction with usable values for `import.meta.url` in
|
|
@@ -206,29 +373,28 @@ does not exist, to the `index.js` file in the directory with the same name.
|
|
|
206
373
|
|
|
207
374
|
## Language Extensions
|
|
208
375
|
|
|
209
|
-
|
|
210
|
-
|
|
376
|
+
Node.js version 14 or greater treats `.mjs` files as ECMAScript modules and
|
|
377
|
+
`.cjs` files as CommonJS modules.
|
|
211
378
|
The `.js` extension indicates a CommonJS module by default, to maintain
|
|
212
379
|
backward compatibility.
|
|
213
|
-
However, packages
|
|
214
|
-
|
|
380
|
+
However, packages with `type` "module" will treat a `.js` file as an ECMAScript
|
|
381
|
+
module.
|
|
215
382
|
|
|
216
383
|
Many Node.js applications using CommonJS modules expect to be able to `require`
|
|
217
384
|
a JSON file like `package.json`.
|
|
218
|
-
The compartment mapper supports loading JSON modules from any type of
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
385
|
+
The compartment mapper therefore supports loading JSON modules from any type of
|
|
386
|
+
module, but using this feature may limit compatibility with the Node.js platform
|
|
387
|
+
(in which importing a JSON module requires [import attributes] including
|
|
388
|
+
`type: "json"`).
|
|
222
389
|
|
|
223
390
|
The compartment mapper supports loading CommonJS modules from ECMAScript
|
|
224
|
-
modules as well as ECMAScript modules
|
|
391
|
+
modules as well as loading ECMAScript modules from CommonJS modules.
|
|
225
392
|
This presumes that the CommonJS modules exclusively use `require` calls with a
|
|
226
393
|
single string argument, where `require` is not lexically bound, to declare
|
|
227
394
|
their shallow dependencies, so that these modules and their transitive
|
|
228
395
|
dependencies can be loaded before any module executes.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
Node.js platform.
|
|
396
|
+
Use of this feature may limit compatibility with the Node.js platform, which did
|
|
397
|
+
not support loading ECMAScript modules from CommonJS modules until version 22.
|
|
232
398
|
|
|
233
399
|
The compartment mapper supports language plugins.
|
|
234
400
|
The languages supported by default are:
|
|
@@ -245,8 +411,8 @@ The languages supported by default are:
|
|
|
245
411
|
|
|
246
412
|
The compartment mapper accepts extensions to this set of languages with
|
|
247
413
|
the `parserForLanguage` option supported by many functions.
|
|
248
|
-
See
|
|
249
|
-
parsers.
|
|
414
|
+
See [src/types/external.ts](./src/types/external.ts) for the type and expected
|
|
415
|
+
behavior of parsers.
|
|
250
416
|
|
|
251
417
|
These language identifiers are keys for the `moduleTransforms` and
|
|
252
418
|
`syncModuleTransforms` options, which may map each language to a transform
|
|
@@ -256,9 +422,9 @@ The language identifiers are also the values for a `languageForExtension`,
|
|
|
256
422
|
configure additional extension-to-language mappings for a module and its
|
|
257
423
|
transitive dependencies.
|
|
258
424
|
|
|
259
|
-
For any package that has `type` set to
|
|
425
|
+
For any package that has `type` set to "module" in its `package.json`,
|
|
260
426
|
`moduleLangaugeForExtension` will precede `languageForExtension`.
|
|
261
|
-
|
|
427
|
+
For any packages with `type` set to "commonjs" or simply not set,
|
|
262
428
|
`commonjsLanguageForExtension` will precede `languageForExtension`.
|
|
263
429
|
This provides an hook for mapping TypeScript's `.ts` to either `.cts` or
|
|
264
430
|
`.mts`.
|
|
@@ -279,14 +445,12 @@ override the extension-to-language mapping.
|
|
|
279
445
|
}
|
|
280
446
|
```
|
|
281
447
|
|
|
282
|
-
>
|
|
283
|
-
>
|
|
284
|
-
>
|
|
285
|
-
> import maps.
|
|
448
|
+
> [!NOTE]
|
|
449
|
+
> TODO: The compartment mapper may elect to respect some properties specified
|
|
450
|
+
> for import maps.
|
|
286
451
|
|
|
287
|
-
>
|
|
288
|
-
>
|
|
289
|
-
> A future version of the compartment mapper may add support for
|
|
452
|
+
> [!NOTE]
|
|
453
|
+
> TODO: A future version of the compartment mapper may add support for
|
|
290
454
|
> source-to-source translation in the scope of a package or compartment.
|
|
291
455
|
> This would be expressed in `package.json` using a property like
|
|
292
456
|
> `translate` that would contain a map from file extension
|
|
@@ -300,10 +464,10 @@ override the extension-to-language mapping.
|
|
|
300
464
|
> non-browser compartment during bundling, so the translator can be excluded
|
|
301
465
|
> from the production application and archived applications.
|
|
302
466
|
|
|
303
|
-
>
|
|
304
|
-
>
|
|
305
|
-
>
|
|
306
|
-
>
|
|
467
|
+
> [!NOTE]
|
|
468
|
+
> TODO: The compartment mapper may also add support for compartment map plugins
|
|
469
|
+
> that would recognize packages in `devDependencies` that need to introduce
|
|
470
|
+
> globals.
|
|
307
471
|
> For example, _packages_ that use JSX and a virtual DOM would be able to add a
|
|
308
472
|
> module-to-module translator and endow the compartment with the `h` the
|
|
309
473
|
> translated modules need.
|
|
@@ -344,25 +508,27 @@ certain conditions:
|
|
|
344
508
|
|
|
345
509
|
# Design
|
|
346
510
|
|
|
347
|
-
Each of the
|
|
511
|
+
Each workflow of the compartment mapper executes a portion of a sequence
|
|
348
512
|
of underlying internals.
|
|
349
513
|
|
|
350
|
-
* search (
|
|
351
|
-
until successfully finding and reading a `package.json` for
|
|
352
|
-
application.
|
|
353
|
-
* map compartments from Node.js packages
|
|
354
|
-
|
|
514
|
+
* search ([search.js](./src/search.js)): Scan the parent directories of a given
|
|
515
|
+
`moduleSpecifier` until successfully finding and reading a `package.json` for
|
|
516
|
+
the containing application.
|
|
517
|
+
* map compartments from Node.js packages
|
|
518
|
+
([node-modules.js](./src/node-modules.js)): Find and gather all the
|
|
519
|
+
`package.json` files for the application's transitive dependencies.
|
|
355
520
|
Use these to construct a compartment map describing how to construct a
|
|
356
521
|
`Compartment` for each application package and how to link the modules each
|
|
357
522
|
exports in the compartments that import them.
|
|
358
|
-
* load compartments (
|
|
359
|
-
implicitly through `compartment.import`, create a module graph for the
|
|
523
|
+
* load compartments ([archive.js](./src/archive.js)): Using `compartment.load`,
|
|
524
|
+
or implicitly through `compartment.import`, create a module graph for the
|
|
360
525
|
application's entire working set.
|
|
361
526
|
When creating an archive, this does not execute any of the modules.
|
|
362
527
|
The compartment mapper uses the compartments and a special `importHook` that
|
|
363
528
|
records the text of every module the main module needed.
|
|
364
|
-
* import modules (
|
|
365
|
-
working
|
|
529
|
+
* import modules ([import.js](./src/import.js),
|
|
530
|
+
[import-archive.js](./src/import-archive.js)): Actually execute the working
|
|
531
|
+
set.
|
|
366
532
|
|
|
367
533
|
Around this sequence, we can enter late or depart early to store or retrieve an
|
|
368
534
|
archive.
|
|
@@ -449,7 +615,7 @@ type Compartment = {
|
|
|
449
615
|
scopes: ScopeMap,
|
|
450
616
|
// The name of the realm to run the compartment within.
|
|
451
617
|
// The default is a single frozen realm that has no name.
|
|
452
|
-
realm
|
|
618
|
+
realm?: RealmName // TODO
|
|
453
619
|
};
|
|
454
620
|
|
|
455
621
|
// Location is the URL relative to the compartment-map.json's
|
|
@@ -471,10 +637,10 @@ type CompartmentModule = {
|
|
|
471
637
|
// TODO an absent compartment name may imply either
|
|
472
638
|
// that the module is an internal alias of the
|
|
473
639
|
// same compartment, or given by the user.
|
|
474
|
-
compartment
|
|
640
|
+
compartment?: CompartmentName,
|
|
475
641
|
// The name of the module in the foreign compartment's
|
|
476
642
|
// module namespace:
|
|
477
|
-
module
|
|
643
|
+
module?: ExternalModuleSpecifier,
|
|
478
644
|
};
|
|
479
645
|
|
|
480
646
|
// FileLocation is a URL for a module's file relative to the location of the
|
|
@@ -594,13 +760,14 @@ The rules defined by policy get preserved in the compartment map and enforced in
|
|
|
594
760
|
|
|
595
761
|
The shape of the `policy` object is based on `policy.json` from LavaMoat. MetaMask's [LavaMoat] generates a `policy.json` file that serves the same purposes, using a tool called TOFU: _trust on first use_.
|
|
596
762
|
|
|
597
|
-
>
|
|
598
|
-
>
|
|
599
|
-
>
|
|
763
|
+
> [!NOTE]
|
|
764
|
+
> TODO: Endo policy support is intended to reach parity with LavaMoat's
|
|
765
|
+
> policy.json.
|
|
600
766
|
> Policy generation may be ported to Endo.
|
|
601
767
|
|
|
602
|
-
|
|
603
768
|
[LavaMoat]: https://github.com/LavaMoat/lavamoat
|
|
604
769
|
[Compartments]: ../ses/README.md#compartment
|
|
605
770
|
[Policy Demo]: ./demo/policy/README.md
|
|
771
|
+
[import attributes]: https://nodejs.org/docs/latest/api/esm.html#import-attributes
|
|
606
772
|
[package entry points]: https://nodejs.org/api/esm.html#esm_package_entry_points
|
|
773
|
+
[`require.resolve()`]: https://nodejs.org/docs/latest/api/modules.html#requireresolverequest-options
|
package/bundle.d.ts
CHANGED
package/bundle.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"functor-lite.d.ts","sourceRoot":"","sources":["functor-lite.js"],"names":[],"mappings":""}
|
package/functor-lite.js
ADDED
package/functor.d.ts
ADDED
package/functor.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"functor.d.ts","sourceRoot":"","sources":["functor.js"],"names":[],"mappings":""}
|
package/functor.js
ADDED
package/index.d.ts
CHANGED
|
@@ -4,5 +4,5 @@ export { compartmentMapForNodeModules } from "./src/node-modules.js";
|
|
|
4
4
|
export { loadLocation, importLocation } from "./src/import.js";
|
|
5
5
|
export { makeArchive, makeAndHashArchive, writeArchive, mapLocation, hashLocation } from "./src/archive.js";
|
|
6
6
|
export { parseArchive, loadArchive, importArchive } from "./src/import-archive.js";
|
|
7
|
-
export { makeBundle, writeBundle } from "./src/bundle.js";
|
|
7
|
+
export { makeScript as makeBundle, writeScript as writeBundle } from "./src/bundle.js";
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -16,4 +16,7 @@ export {
|
|
|
16
16
|
} from './src/import-archive.js';
|
|
17
17
|
export { search } from './src/search.js';
|
|
18
18
|
export { compartmentMapForNodeModules } from './src/node-modules.js';
|
|
19
|
-
export {
|
|
19
|
+
export {
|
|
20
|
+
makeScript as makeBundle,
|
|
21
|
+
writeScript as writeBundle,
|
|
22
|
+
} from './src/bundle.js';
|