@endo/compartment-mapper 1.6.3 → 2.1.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 (95) hide show
  1. package/package.json +24 -14
  2. package/src/archive-lite.d.ts +7 -7
  3. package/src/archive-lite.d.ts.map +1 -1
  4. package/src/archive-lite.js +81 -30
  5. package/src/archive.d.ts.map +1 -1
  6. package/src/archive.js +7 -0
  7. package/src/bundle-lite.d.ts +3 -3
  8. package/src/bundle-lite.d.ts.map +1 -1
  9. package/src/bundle-lite.js +19 -24
  10. package/src/bundle.d.ts +3 -3
  11. package/src/bundle.d.ts.map +1 -1
  12. package/src/bundle.js +19 -24
  13. package/src/capture-lite.d.ts +2 -2
  14. package/src/capture-lite.d.ts.map +1 -1
  15. package/src/capture-lite.js +243 -25
  16. package/src/compartment-map.d.ts +9 -2
  17. package/src/compartment-map.d.ts.map +1 -1
  18. package/src/compartment-map.js +738 -254
  19. package/src/digest.d.ts +22 -2
  20. package/src/digest.d.ts.map +1 -1
  21. package/src/digest.js +180 -57
  22. package/src/generic-graph.d.ts +7 -25
  23. package/src/generic-graph.d.ts.map +1 -1
  24. package/src/generic-graph.js +83 -108
  25. package/src/guards.d.ts +18 -0
  26. package/src/guards.d.ts.map +1 -0
  27. package/src/guards.js +109 -0
  28. package/src/hooks.md +124 -0
  29. package/src/import-archive-lite.d.ts.map +1 -1
  30. package/src/import-archive-lite.js +15 -11
  31. package/src/import-archive.d.ts +5 -19
  32. package/src/import-archive.d.ts.map +1 -1
  33. package/src/import-archive.js +7 -27
  34. package/src/import-hook.d.ts +4 -3
  35. package/src/import-hook.d.ts.map +1 -1
  36. package/src/import-hook.js +140 -70
  37. package/src/import-lite.d.ts +6 -6
  38. package/src/import-lite.d.ts.map +1 -1
  39. package/src/import-lite.js +8 -5
  40. package/src/import.d.ts +3 -3
  41. package/src/import.d.ts.map +1 -1
  42. package/src/import.js +16 -6
  43. package/src/infer-exports.d.ts +4 -2
  44. package/src/infer-exports.d.ts.map +1 -1
  45. package/src/infer-exports.js +172 -23
  46. package/src/link.d.ts +4 -3
  47. package/src/link.d.ts.map +1 -1
  48. package/src/link.js +122 -52
  49. package/src/node-modules.d.ts +4 -3
  50. package/src/node-modules.d.ts.map +1 -1
  51. package/src/node-modules.js +513 -151
  52. package/src/parse-cjs-shared-export-wrapper.d.ts.map +1 -1
  53. package/src/parse-cjs-shared-export-wrapper.js +3 -1
  54. package/src/pattern-replacement.d.ts +6 -0
  55. package/src/pattern-replacement.d.ts.map +1 -0
  56. package/src/pattern-replacement.js +198 -0
  57. package/src/policy-format.d.ts +22 -5
  58. package/src/policy-format.d.ts.map +1 -1
  59. package/src/policy-format.js +342 -108
  60. package/src/policy.d.ts +13 -28
  61. package/src/policy.d.ts.map +1 -1
  62. package/src/policy.js +161 -106
  63. package/src/types/canonical-name.d.ts +97 -0
  64. package/src/types/canonical-name.d.ts.map +1 -0
  65. package/src/types/canonical-name.ts +151 -0
  66. package/src/types/compartment-map-schema.d.ts +121 -35
  67. package/src/types/compartment-map-schema.d.ts.map +1 -1
  68. package/src/types/compartment-map-schema.ts +211 -37
  69. package/src/types/external.d.ts +240 -76
  70. package/src/types/external.d.ts.map +1 -1
  71. package/src/types/external.ts +305 -74
  72. package/src/types/generic-graph.d.ts +8 -2
  73. package/src/types/generic-graph.d.ts.map +1 -1
  74. package/src/types/generic-graph.ts +7 -2
  75. package/src/types/internal.d.ts +31 -50
  76. package/src/types/internal.d.ts.map +1 -1
  77. package/src/types/internal.ts +60 -58
  78. package/src/types/node-modules.d.ts +112 -14
  79. package/src/types/node-modules.d.ts.map +1 -1
  80. package/src/types/node-modules.ts +152 -13
  81. package/src/types/pattern-replacement.d.ts +62 -0
  82. package/src/types/pattern-replacement.d.ts.map +1 -0
  83. package/src/types/pattern-replacement.ts +70 -0
  84. package/src/types/policy-schema.d.ts +26 -11
  85. package/src/types/policy-schema.d.ts.map +1 -1
  86. package/src/types/policy-schema.ts +29 -16
  87. package/src/types/policy.d.ts +6 -2
  88. package/src/types/policy.d.ts.map +1 -1
  89. package/src/types/policy.ts +7 -2
  90. package/src/types/powers.d.ts +11 -9
  91. package/src/types/powers.d.ts.map +1 -1
  92. package/src/types/powers.ts +11 -10
  93. package/src/types/typescript.d.ts +28 -0
  94. package/src/types/typescript.d.ts.map +1 -1
  95. package/src/types/typescript.ts +37 -1
@@ -2,38 +2,56 @@
2
2
  * Provides {@link GenericGraph} and {@link makeShortestPath}.
3
3
  *
4
4
  * Portions adapted from
5
- * {@link https://github.com/datavis-tech/graph-data-structure graph-data-structure},
5
+ * [graph data structure](https://github.com/datavis-tech/graph-data-structure),
6
6
  * which is Copyright (c) 2016 Curran Kelleher and licensed under the MIT
7
7
  * License.
8
8
  *
9
9
  * @module
10
10
  */
11
11
 
12
+ import { pathCompare } from '@endo/path-compare';
13
+
12
14
  /**
13
- * @import {TraversalContext} from './types/generic-graph.js';
15
+ * @import {GenericGraphNode, TraversalContext} from './types/generic-graph.js';
14
16
  */
15
17
 
16
- const { stringify: q } = JSON;
18
+ const { quote: q } = assert;
19
+
20
+ /**
21
+ * Returns `true` if the cost of path `a` is less than the cost of path `b`.
22
+ *
23
+ * @template {GenericGraphNode} [T=string]
24
+ * @param {T[]} [pathA]
25
+ * @param {T[]} [pathB]
26
+ * @returns {boolean}
27
+ */
28
+ const isLowerCost = (pathA, pathB) =>
29
+ pathCompare(pathA?.map(String), pathB?.map(String)) < 0;
17
30
 
18
31
  /**
19
32
  * Remove the node with the minimum weight from the priority queue.
20
33
  *
21
34
  * Performs linear search.
22
- * @template [T=string]
35
+ *
36
+ * @template {GenericGraphNode} [T=string]
23
37
  * @param {TraversalContext<T>} tracks
24
38
  * @returns {T|undefined}
25
39
  */
26
- const extractMin = ({ distances, queue }) => {
27
- let min = Infinity;
40
+ const extractMin = ({ paths, queue }) => {
41
+ /** @type {T[]|undefined} */
42
+ let minPath;
28
43
 
29
44
  /** @type {T|undefined} */
30
45
  let minNode;
31
46
 
32
47
  queue.forEach(node => {
33
- const nodeWeight = distances.get(node) ?? Infinity;
48
+ const path = paths.get(node);
49
+ if (!path) {
50
+ return;
51
+ }
34
52
 
35
- if (nodeWeight < min) {
36
- min = nodeWeight;
53
+ if (!minPath || isLowerCost(path, minPath)) {
54
+ minPath = path;
37
55
  minNode = node;
38
56
  }
39
57
  });
@@ -48,32 +66,23 @@ const extractMin = ({ distances, queue }) => {
48
66
  };
49
67
 
50
68
  /**
51
- * Update context with the new distance to the target node if the distance
52
- * through the source node is shorter than the current distance.
69
+ * Update context to include the current lowest-cost path to a target node
70
+ * reachable by a single edge from a source node.
53
71
  *
54
- * @template [T=string]
55
- * @param {GenericGraph<T>} graph
56
- * @param {TraversalContext<NoInfer<T>>} context
72
+ * @template {GenericGraphNode} [T=string]
73
+ * @param {TraversalContext<T>} context
57
74
  * @param {NoInfer<T>} source
58
75
  * @param {NoInfer<T>} target
59
76
  */
60
- const relax = (graph, { distances, predecessors }, source, target) => {
61
- const number = graph.getEdgeWeight(source, target);
77
+ const relax = ({ paths, predecessors }, source, target) => {
78
+ const pathSource = paths.get(source);
79
+ assert(pathSource, `Missing path to source ${q(source)}`);
62
80
 
63
- const distanceSource = distances.get(source);
64
- const distanceTarget = distances.get(target);
81
+ const pathTarget = paths.get(target);
82
+ const newPath = [...pathSource, target];
65
83
 
66
- assert(
67
- distanceSource !== undefined,
68
- `Missing distance for source ${q(source)}`,
69
- );
70
- assert(
71
- distanceTarget !== undefined,
72
- `Missing distance for target ${q(target)} target`,
73
- );
74
-
75
- if (distanceTarget > distanceSource + number) {
76
- distances.set(target, distanceSource + number);
84
+ if (!pathTarget || isLowerCost(newPath, pathTarget)) {
85
+ paths.set(target, newPath);
77
86
  predecessors.set(target, source);
78
87
  }
79
88
  };
@@ -82,7 +91,7 @@ const relax = (graph, { distances, predecessors }, source, target) => {
82
91
  * Assembles the shortest path by traversing the
83
92
  * predecessor subgraph from destination to source.
84
93
  *
85
- * @template [T=string]
94
+ * @template {GenericGraphNode} [T=string]
86
95
  * @param {TraversalContext<NoInfer<T>>} context Traversal context object
87
96
  * @param {NoInfer<T>} source Source node
88
97
  * @param {NoInfer<T>} target Destination node
@@ -94,6 +103,11 @@ const getPath = ({ predecessors }, source, target) => {
94
103
  /** @type {T[]} */
95
104
  const nodeList = [];
96
105
 
106
+ assert(
107
+ source !== target,
108
+ `Source ${q(source)} cannot be the same as target ${q(target)}`,
109
+ );
110
+
97
111
  let node = target;
98
112
 
99
113
  while (predecessors.has(node)) {
@@ -102,24 +116,27 @@ const getPath = ({ predecessors }, source, target) => {
102
116
  node = currentNode;
103
117
  }
104
118
 
105
- assert.equal(node, source, `No path found from ${q(source)} to ${q(target)}`);
119
+ assert.equal(
120
+ node,
121
+ source,
122
+ `No path found from ${q(String(source))} to ${q(String(target))}`,
123
+ );
106
124
 
107
125
  nodeList.push(node);
108
126
 
109
127
  assert(
110
128
  nodeList.length >= 2,
111
- `The path from ${source} to ${target} should have a least two nodes`,
129
+ `The path from ${source} to ${target} should have at least two nodes`,
112
130
  );
113
131
 
114
132
  return /** @type {[T, T, ...T[]]} */ (nodeList.reverse());
115
133
  };
116
134
 
117
135
  /**
118
- * @template [T=string] The type of nodes in the graph
119
- *
120
- * A generic graph implementation with edge weights.
136
+ * A generic graph implementation.
121
137
  *
122
- * Edge weights are assumed to be non-negative numbers (including `Infinity`)
138
+ * @template {GenericGraphNode} [T=string] The type of nodes in the graph. If
139
+ * `T` is not a string, relative paths will be compared by coercion to strings.
123
140
  */
124
141
  export class GenericGraph {
125
142
  /**
@@ -132,16 +149,10 @@ export class GenericGraph {
132
149
  */
133
150
  #edges;
134
151
 
135
- /**
136
- * @type {Map<T, Map<T, number>>}
137
- */
138
- #edgeWeights;
139
-
140
152
  /**
141
153
  * Initializes internal data structures.
142
154
  */
143
155
  constructor() {
144
- this.#edgeWeights = new Map();
145
156
  this.#edges = new Map();
146
157
  this.#nodes = new Set();
147
158
  }
@@ -194,42 +205,6 @@ export class GenericGraph {
194
205
  return this.#edges.get(node);
195
206
  }
196
207
 
197
- /**
198
- * Sets the weight of the given edge between `source` and `target`.
199
- *
200
- * @param {T} source Source node
201
- * @param {T} target Target node
202
- * @param {number} weight New edge weight
203
- * @returns {this}
204
- */
205
- setEdgeWeight(source, target, weight) {
206
- if (!this.#edgeWeights.has(source)) {
207
- this.#edgeWeights.set(source, new Map());
208
- }
209
- const weights = /** @type {Map<T, number>} */ (
210
- this.#edgeWeights.get(source)
211
- );
212
- weights.set(target, weight);
213
- return this;
214
- }
215
-
216
- /**
217
- * Gets the weight of the given edge between `source` and `target`.
218
- *
219
- * @param {T} source Source node
220
- * @param {T} target Target node
221
- * @returns {number} Edge weight from source to target
222
- */
223
- getEdgeWeight(source, target) {
224
- const weight = this.#edgeWeights.get(source)?.get(target);
225
- if (weight === undefined) {
226
- throw new ReferenceError(
227
- `Edge weight from ${q(source)} to ${q(target)} is not set`,
228
- );
229
- }
230
- return weight;
231
- }
232
-
233
208
  /**
234
209
  * Adds an edge from the `source` node to `target` node.
235
210
  *
@@ -240,17 +215,15 @@ export class GenericGraph {
240
215
  *
241
216
  * @param {T} source Source node
242
217
  * @param {T} target Target node
243
- * @param {number} weight Edge weight from source to target
244
218
  * @returns {this} This graph instance
245
219
  */
246
- addEdge(source, target, weight) {
220
+ addEdge(source, target) {
247
221
  this.addNode(source);
248
222
  this.addNode(target);
249
223
  const adjacentNodes = this.adjacent(source);
250
224
  assert(adjacentNodes, `Source ${q(source)} should have adjacent nodes`);
251
225
 
252
226
  adjacentNodes.add(target);
253
- this.setEdgeWeight(source, target, weight);
254
227
  return this;
255
228
  }
256
229
 
@@ -279,42 +252,32 @@ export class GenericGraph {
279
252
  }
280
253
 
281
254
  /**
282
- * Dijkstra's algorithm for shortest paths in a graph.
283
- * @template [T=string] The type of nodes in the graph
255
+ * Dijkstra's single-source shortest path algorithm.
256
+ *
257
+ * Computes shortest paths from `source` to **all** reachable nodes.
258
+ *
259
+ * @template {GenericGraphNode} [T=string] The type of nodes in the graph
284
260
  * @param {GenericGraph<T>} graph
285
261
  * @param {T} source
286
- * @param {T} target
287
262
  * @returns {TraversalContext<T>}
288
263
  */
289
- const dijkstra = (graph, source, target) => {
264
+ const dijkstra = (graph, source) => {
290
265
  const { nodes } = graph;
291
266
  /** @type {TraversalContext<T>} */
292
267
  const context = {
293
- distances: new Map(),
268
+ paths: new Map(),
294
269
  predecessors: new Map(),
295
- queue: new Set(),
270
+ queue: nodes,
296
271
  };
297
- const { queue, distances } = context;
298
-
299
- for (const node of nodes) {
300
- distances.set(node, Infinity);
301
- }
302
-
303
- assert(
304
- distances.get(source) === Infinity,
305
- `Source ${q(source)} is not in the graph`,
306
- );
307
- assert(
308
- distances.get(target) === Infinity,
309
- `Target ${q(target)} is not in the graph`,
310
- );
311
-
312
- distances.set(source, 0);
272
+ const { queue, paths } = context;
313
273
 
314
274
  for (const node of nodes) {
315
275
  queue.add(node);
316
276
  }
317
277
 
278
+ assert(queue.has(source), `Source ${q(source)} is not in the graph`);
279
+ paths.set(source, []);
280
+
318
281
  while (queue.size !== 0) {
319
282
  const node = extractMin(context);
320
283
  if (node === undefined) {
@@ -323,7 +286,7 @@ const dijkstra = (graph, source, target) => {
323
286
  const adjacent = graph.adjacent(node);
324
287
  if (adjacent) {
325
288
  for (const edge of adjacent) {
326
- relax(graph, context, node, edge);
289
+ relax(context, node, edge);
327
290
  }
328
291
  }
329
292
  }
@@ -331,20 +294,32 @@ const dijkstra = (graph, source, target) => {
331
294
  };
332
295
 
333
296
  /**
334
- * Returns a function which uses Dijkstra's shortest path algorithm to compute
335
- * the shortest path from `source` to `destination` in the given `graph`.
297
+ * Returns a function which computes the shortest path from `source` to
298
+ * `target` in the given `graph`.
336
299
  *
337
- * @template [T=string]
300
+ * Dijkstra's algorithm is a _single-source_ shortest path algorithm: one run
301
+ * produces shortest paths to every reachable node. The returned function
302
+ * caches the traversal context by source, so the first call for a given source
303
+ * pays O(V²) and every subsequent call with the same source is O(path length).
304
+ *
305
+ * @template {GenericGraphNode} [T=string]
338
306
  * @param {GenericGraph<T>} graph Graph to use
339
307
  */
340
308
  export const makeShortestPath = graph => {
309
+ /** @type {Map<T, TraversalContext<T>>} */
310
+ const contextCache = new Map();
311
+
341
312
  /**
342
313
  * @param {NoInfer<T>} source Source node
343
314
  * @param {NoInfer<T>} target Target node
344
315
  * @returns {[T, T, ...T[]]} Nodes from `source` to `target` inclusive (minimum of two nodes)
345
316
  */
346
317
  const shortestPath = (source, target) => {
347
- const context = dijkstra(graph, source, target);
318
+ let context = contextCache.get(source);
319
+ if (!context) {
320
+ context = dijkstra(graph, source);
321
+ contextCache.set(source, context);
322
+ }
348
323
  return getPath(context, source, target);
349
324
  };
350
325
  return shortestPath;
@@ -0,0 +1,18 @@
1
+ export function isErrorModuleConfiguration(value: ModuleConfiguration): value is ErrorModuleConfiguration;
2
+ export function isFileModuleConfiguration(value: ModuleConfiguration): value is FileModuleConfiguration;
3
+ export function isExitModuleConfiguration(value: ModuleConfiguration): value is ExitModuleConfiguration;
4
+ export function isCompartmentModuleConfiguration(value: ModuleConfiguration): value is CompartmentModuleConfiguration;
5
+ export function isErrorModuleSource(value: ModuleSource): value is ErrorModuleSource;
6
+ export function isExitModuleSource(value: ModuleSource): value is ExitModuleSource;
7
+ export function isLocalModuleSource(value: ModuleSource): value is LocalModuleSource;
8
+ export function isNonNullableObject(value: unknown): value is object;
9
+ import type { ModuleConfiguration } from './types.js';
10
+ import type { ErrorModuleConfiguration } from './types.js';
11
+ import type { FileModuleConfiguration } from './types.js';
12
+ import type { ExitModuleConfiguration } from './types.js';
13
+ import type { CompartmentModuleConfiguration } from './types.js';
14
+ import type { ModuleSource } from './types.js';
15
+ import type { ErrorModuleSource } from './types.js';
16
+ import type { ExitModuleSource } from './types.js';
17
+ import type { LocalModuleSource } from './types.js';
18
+ //# sourceMappingURL=guards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["guards.js"],"names":[],"mappings":"AA2BO,kDAHI,mBAAmB,GACjB,KAAK,IAAI,wBAAwB,CAIU;AAQjD,iDAHI,mBAAmB,GACjB,KAAK,IAAI,uBAAuB,CAKT;AAO7B,iDAHI,mBAAmB,GACjB,KAAK,IAAI,uBAAuB,CAKT;AAO7B,wDAHI,mBAAmB,GACjB,KAAK,IAAI,8BAA8B,CAOhB;AAO7B,2CAHI,YAAY,GACV,KAAK,IAAI,iBAAiB,CAIiB;AAQjD,0CAHI,YAAY,GACV,KAAK,IAAI,gBAAgB,CAKT;AAQtB,2CAHI,YAAY,GACV,KAAK,IAAI,iBAAiB,CAWV;AAQtB,2CAHI,OAAO,GACL,KAAK,IAAI,MAAM,CAGiB;yCAzFnC,YAAY;8CAAZ,YAAY;6CAAZ,YAAY;6CAAZ,YAAY;oDAAZ,YAAY;kCAAZ,YAAY;uCAAZ,YAAY;sCAAZ,YAAY;uCAAZ,YAAY"}
package/src/guards.js ADDED
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Common type guards.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ const { hasOwn } = Object;
8
+
9
+ /**
10
+ * @import {
11
+ * ModuleConfiguration,
12
+ * FileModuleConfiguration,
13
+ * ErrorModuleConfiguration,
14
+ * ModuleSource,
15
+ * ExitModuleSource,
16
+ * ErrorModuleSource,
17
+ * LocalModuleSource,
18
+ * ExitModuleConfiguration,
19
+ * CompartmentModuleConfiguration
20
+ * } from './types.js';
21
+ */
22
+
23
+ /**
24
+ * Type guard for an {@link ErrorModuleConfiguration}.
25
+ * @param {ModuleConfiguration} value
26
+ * @returns {value is ErrorModuleConfiguration}
27
+ */
28
+ export const isErrorModuleConfiguration = value =>
29
+ hasOwn(value, 'deferredError') &&
30
+ /** @type {any} */ (value).deferredError !== undefined;
31
+
32
+ /**
33
+ * Type guard for a {@link FileModuleConfiguration}.
34
+ *
35
+ * @param {ModuleConfiguration} value
36
+ * @returns {value is FileModuleConfiguration}
37
+ */
38
+ export const isFileModuleConfiguration = value =>
39
+ hasOwn(value, 'parser') &&
40
+ /** @type {any} */ (value).parser !== undefined &&
41
+ !isErrorModuleConfiguration(value);
42
+
43
+ /**
44
+ * Type guard for an {@link ExitModuleConfiguration}.
45
+ * @param {ModuleConfiguration} value
46
+ * @returns {value is ExitModuleConfiguration}
47
+ */
48
+ export const isExitModuleConfiguration = value =>
49
+ hasOwn(value, 'exit') &&
50
+ /** @type {any} */ (value).exit !== undefined &&
51
+ !isErrorModuleConfiguration(value);
52
+
53
+ /**
54
+ * Type guard for an {@link CompartmentModuleConfiguration}.
55
+ * @param {ModuleConfiguration} value
56
+ * @returns {value is CompartmentModuleConfiguration}
57
+ */
58
+ export const isCompartmentModuleConfiguration = value =>
59
+ hasOwn(value, 'compartment') &&
60
+ /** @type {any} */ (value).compartment !== undefined &&
61
+ hasOwn(value, 'module') &&
62
+ /** @type {any} */ (value).module !== undefined &&
63
+ !isErrorModuleConfiguration(value);
64
+ /**
65
+ * Type guard for an {@link ErrorModuleSource}.
66
+ *
67
+ * @param {ModuleSource} value
68
+ * @returns {value is ErrorModuleSource}
69
+ */
70
+ export const isErrorModuleSource = value =>
71
+ hasOwn(value, 'deferredError') &&
72
+ /** @type {any} */ (value).deferredError !== undefined;
73
+
74
+ /**
75
+ * Type guard for an {@link ExitModuleSource}.
76
+ *
77
+ * @param {ModuleSource} value
78
+ * @returns {value is ExitModuleSource}
79
+ */
80
+ export const isExitModuleSource = value =>
81
+ hasOwn(value, 'exit') &&
82
+ /** @type {any} */ (value).exit !== undefined &&
83
+ !isErrorModuleSource(value);
84
+
85
+ /**
86
+ * Type guard for an {@link LocalModuleSource}.
87
+ *
88
+ * @param {ModuleSource} value
89
+ * @returns {value is LocalModuleSource}
90
+ */
91
+ export const isLocalModuleSource = value =>
92
+ hasOwn(value, 'bytes') &&
93
+ /** @type {any} */ (value).bytes !== undefined &&
94
+ hasOwn(value, 'parser') &&
95
+ /** @type {any} */ (value).parser !== undefined &&
96
+ hasOwn(value, 'sourceDirname') &&
97
+ /** @type {any} */ (value).sourceDirname !== undefined &&
98
+ hasOwn(value, 'location') &&
99
+ /** @type {any} */ (value).location !== undefined &&
100
+ !isErrorModuleSource(value);
101
+
102
+ /**
103
+ * Type guard for a non-nullable object
104
+ *
105
+ * @param {unknown} value
106
+ * @returns {value is object}
107
+ */
108
+ export const isNonNullableObject = value =>
109
+ typeof value === 'object' && value !== null;
package/src/hooks.md ADDED
@@ -0,0 +1,124 @@
1
+ # Review of compartment-mapper hooks
2
+
3
+
4
+
5
+ | Hook Name | Description |
6
+ |-------------------------- | --- |
7
+ | `packageDataHook` | Receives all found package descriptors data before graph translation. |
8
+ | `packageDependenciesHook` | Allows dynamic mutation of dependencies during node_modules graph translation. |
9
+ | `unknownCanonicalNameHook`| Called when the policy references unknown canonical names, can suggest typos/similar names. |
10
+ | `moduleSourceHook` | Invoked when a module source is created. |
11
+ | `packageConnectionsHook` | Surfaces connections during digest. (ignored in archiving) |
12
+
13
+
14
+
15
+ [Type declarations for the hooks](./types/external.ts)
16
+
17
+ ```mermaid
18
+ graph TB
19
+
20
+ exports((public exports))
21
+ exports -.- compartmentMapForNodeModules
22
+ exports -.- mapNodeModules
23
+ exports -.- loadLocation
24
+ exports -.- importLocation
25
+ exports -.- captureFromMap
26
+ exports -.- importFromMap
27
+ exports -.- loadFromMap
28
+ exports -.- digestCompartmentMap
29
+ exports -.- makeFunctor
30
+ exports -.- makeScript
31
+
32
+
33
+ subgraph "import-hook.js"
34
+ moduleSourceHook{{moduleSourceHook}} -.- note5["for all module sources read"]
35
+ makeDeferError --> moduleSourceHook
36
+ makeImportHookMaker --> makeDeferError
37
+ makeImportNowHookMaker --> makeDeferError
38
+ makeImportNowHookMaker -- via importNowHook --> chooseModuleDescriptor --> executeLocalModuleSourceHook--> moduleSourceHook
39
+ makeImportHookMaker --via importHook exitModule logic--> moduleSourceHook
40
+ makeImportHookMaker --via importHook call--> chooseModuleDescriptor
41
+ end
42
+
43
+ subgraph "node-modules.js"
44
+ compartmentMapForNodeModules --> packageDataHook{{packageDataHook}} -.- note0["called once"]
45
+ compartmentMapForNodeModules --> unknownCanonicalNameHook{{unknownCanonicalNameHook}} -.- note1["for all issues from policy;<br>after defaultUnknownCanonicalNameHandler"]
46
+ compartmentMapForNodeModules --> translateGraph --> packageDependenciesHook{{packageDependenciesHook}} -.-note3["for all locatons in graph<br>after defaultPackageDependenciesFilter"]
47
+ mapNodeModules --> compartmentMapForNodeModules
48
+
49
+ end
50
+
51
+ subgraph "digest.js"
52
+ digestCompartmentMap --> translateCompartmentMap --> packageConnectionsHook{{packageConnectionsHook}} -.- note4["for all retained compartments"]
53
+ end
54
+
55
+ subgraph "bundle.js"
56
+ makeFunctor -- options:can include hooks ----------> mapNodeModules
57
+ makeScript -- options:can include hooks ----------> mapNodeModules
58
+ end
59
+
60
+
61
+ subgraph "import-lite.js"
62
+ importFromMap --> loadFromMap ---> makeImportHookMaker
63
+ loadFromMap ----> makeImportNowHookMaker
64
+ end
65
+
66
+
67
+ subgraph "capture-lite.js"
68
+ captureFromMap -----> makeImportHookMaker
69
+ captureFromMap --> captureCompartmentMap --> digestCompartmentMap
70
+ end
71
+
72
+
73
+ subgraph "import.js"
74
+ loadLocation ----------> mapNodeModules
75
+ importLocation --> loadLocation
76
+ loadLocation --> loadFromMap
77
+ end
78
+
79
+
80
+ %% STYLING
81
+ classDef note fill:#999, stroke:#ccb
82
+ class note0,note1,note2,note3,note4,note5 note
83
+
84
+ ```
85
+
86
+
87
+
88
+ <details>
89
+ <summary>Bundle and Archive bits of the diagram that don't use hooks</summary>
90
+
91
+ These are calling the functions accepting hooks but don't pass them
92
+
93
+ > [TODO]
94
+ > copy-paste this to the main diagram whenever the connections are made.
95
+
96
+ ```mermaid
97
+
98
+ graph TB
99
+
100
+ subgraph "bundle.js"
101
+ makeFunctor -- options:transparently ----------> mapNodeModules
102
+ makeScript -- options:transparently ----------> mapNodeModules
103
+ makeFunctorFromMap -- no moduleSourceHook ----x makeImportHookMaker
104
+ end
105
+
106
+ subgraph "bundle-lite.js"
107
+ makeFunctorFromMap2 --no moduleSourceHook -----x makeImportHookMaker
108
+ end
109
+
110
+ subgraph "archive-lite.js"
111
+ digestFromMap -- no moduleSourceHook -----x makeImportHookMaker
112
+ makeArchiveCompartmentMap --no packageConnectionsHook----x digestCompartmentMap
113
+ end
114
+
115
+ subgraph "archive.js"
116
+ archive(("multiple <br> methods")) --no hooks passed-----------x mapNodeModules
117
+ end
118
+
119
+ ```
120
+
121
+ </details>
122
+
123
+
124
+
@@ -1 +1 @@
1
- {"version":3,"file":"import-archive-lite.d.ts","sourceRoot":"","sources":["import-archive-lite.js"],"names":[],"mappings":"AAsPO,2CALI,UAAU,oBACV,MAAM,YACN,mBAAmB,GACjB,OAAO,CAAC,WAAW,CAAC,CAiKhC;AAQM,wCALI,MAAM,GAAG,UAAU,mBACnB,MAAM,YACN,kBAAkB,GAChB,OAAO,CAAC,WAAW,CAAC,CAwBhC;AAQM,0CALI,MAAM,GAAG,UAAU,mBACnB,MAAM,WACN,kBAAkB,GAChB,OAAO,CAAC,UAAU,CAAC,CAK/B;yCAxZS,YAAY;iCAAZ,YAAY;4BAAZ,YAAY;gCAAZ,YAAY;wCAAZ,YAAY;gCAAZ,YAAY"}
1
+ {"version":3,"file":"import-archive-lite.d.ts","sourceRoot":"","sources":["import-archive-lite.js"],"names":[],"mappings":"AA0PO,2CALI,UAAU,oBACV,MAAM,YACN,mBAAmB,GACjB,OAAO,CAAC,WAAW,CAAC,CAiKhC;AAQM,wCALI,MAAM,GAAG,UAAU,mBACnB,MAAM,YACN,kBAAkB,GAChB,OAAO,CAAC,WAAW,CAAC,CAwBhC;AAQM,0CALI,MAAM,GAAG,UAAU,mBACnB,MAAM,WACN,kBAAkB,GAChB,OAAO,CAAC,UAAU,CAAC,CAK/B;yCA5ZS,YAAY;iCAAZ,YAAY;4BAAZ,YAAY;gCAAZ,YAAY;wCAAZ,YAAY;gCAAZ,YAAY"}
@@ -23,7 +23,7 @@
23
23
  * } from 'ses';
24
24
  * @import {
25
25
  * Application,
26
- * CompartmentDescriptor,
26
+ * FileCompartmentDescriptor,
27
27
  * ComputeSourceLocationHook,
28
28
  * ComputeSourceMapLocationHook,
29
29
  * ExecuteFn,
@@ -44,9 +44,13 @@ import { link } from './link.js';
44
44
  import { parseLocatedJson } from './json.js';
45
45
  import { unpackReadPowers } from './powers.js';
46
46
  import { join } from './node-module-specifier.js';
47
- import { assertCompartmentMap } from './compartment-map.js';
47
+ import { assertFileCompartmentMap } from './compartment-map.js';
48
48
  import { exitModuleImportHookMaker } from './import-hook.js';
49
- import { attenuateModuleHook, enforceModulePolicy } from './policy.js';
49
+ import { attenuateModuleHook, enforcePolicyByModule } from './policy.js';
50
+ import {
51
+ isErrorModuleConfiguration,
52
+ isFileModuleConfiguration,
53
+ } from './guards.js';
50
54
 
51
55
  const { Fail, quote: q } = assert;
52
56
 
@@ -77,7 +81,7 @@ const postponeErrorToExecute = errorMessage => {
77
81
 
78
82
  /**
79
83
  * @param {(path: string) => Uint8Array} get
80
- * @param {Record<string, CompartmentDescriptor>} compartments
84
+ * @param {Record<string, FileCompartmentDescriptor>} compartments
81
85
  * @param {string} archiveLocation
82
86
  * @param {ParserForLanguage} parserForLanguage
83
87
  * @param {HashFn} [computeSha512]
@@ -118,7 +122,7 @@ const makeArchiveImportHookMaker = (
118
122
  // At this point in archive importing, if a module is not found and
119
123
  // exitModuleImportHook exists, the only possibility is that the
120
124
  // module is a "builtin" module and the policy needs to be enforced.
121
- enforceModulePolicy(moduleSpecifier, compartmentDescriptor, {
125
+ enforcePolicyByModule(moduleSpecifier, compartmentDescriptor, {
122
126
  exit: true,
123
127
  errorHint: `Blocked in loading. ${q(
124
128
  moduleSpecifier,
@@ -161,22 +165,22 @@ const makeArchiveImportHookMaker = (
161
165
  )} in archive ${q(archiveLocation)}`,
162
166
  );
163
167
  }
164
- if (module.deferredError !== undefined) {
168
+ if (isErrorModuleConfiguration(module)) {
165
169
  return postponeErrorToExecute(module.deferredError);
166
170
  }
167
- if (module.parser === undefined) {
171
+ if (!isFileModuleConfiguration(module)) {
168
172
  throw Error(
169
173
  `Cannot parse module ${q(moduleSpecifier)} in package ${q(
170
174
  packageLocation,
171
- )} in archive ${q(archiveLocation)}`,
175
+ )} in archive ${q(archiveLocation)}; missing parser`,
172
176
  );
173
177
  }
174
178
  const parser = parserForLanguage[module.parser];
175
179
  if (parser === undefined) {
176
180
  throw Error(
177
- `Cannot parse ${q(module.parser)} module ${q(
181
+ `Cannot parse module ${q(
178
182
  moduleSpecifier,
179
- )} in package ${q(packageLocation)} in archive ${q(archiveLocation)}`,
183
+ )} in package ${q(packageLocation)} in archive ${q(archiveLocation)}; unknown parser (${q(module.parser)})`,
180
184
  );
181
185
  }
182
186
  const { parse } = parser;
@@ -306,7 +310,7 @@ export const parseArchive = async (
306
310
  compartmentMapText,
307
311
  'compartment-map.json',
308
312
  );
309
- assertCompartmentMap(compartmentMap, archiveLocation);
313
+ assertFileCompartmentMap(compartmentMap, archiveLocation);
310
314
 
311
315
  const {
312
316
  compartments,