@endo/compartment-mapper 2.0.0 → 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 (43) hide show
  1. package/package.json +23 -9
  2. package/src/archive-lite.js +3 -3
  3. package/src/capture-lite.d.ts.map +1 -1
  4. package/src/capture-lite.js +29 -3
  5. package/src/compartment-map.d.ts.map +1 -1
  6. package/src/compartment-map.js +3 -2
  7. package/src/digest.js +1 -1
  8. package/src/generic-graph.d.ts +7 -25
  9. package/src/generic-graph.d.ts.map +1 -1
  10. package/src/generic-graph.js +75 -105
  11. package/src/import-hook.d.ts.map +1 -1
  12. package/src/import-hook.js +4 -3
  13. package/src/infer-exports.d.ts +4 -2
  14. package/src/infer-exports.d.ts.map +1 -1
  15. package/src/infer-exports.js +158 -19
  16. package/src/link.d.ts.map +1 -1
  17. package/src/link.js +61 -3
  18. package/src/node-modules.d.ts.map +1 -1
  19. package/src/node-modules.js +33 -39
  20. package/src/pattern-replacement.d.ts +6 -0
  21. package/src/pattern-replacement.d.ts.map +1 -0
  22. package/src/pattern-replacement.js +198 -0
  23. package/src/types/compartment-map-schema.d.ts +8 -1
  24. package/src/types/compartment-map-schema.d.ts.map +1 -1
  25. package/src/types/compartment-map-schema.ts +9 -0
  26. package/src/types/external.d.ts +79 -55
  27. package/src/types/external.d.ts.map +1 -1
  28. package/src/types/external.ts +104 -62
  29. package/src/types/generic-graph.d.ts +8 -2
  30. package/src/types/generic-graph.d.ts.map +1 -1
  31. package/src/types/generic-graph.ts +7 -2
  32. package/src/types/internal.d.ts +8 -8
  33. package/src/types/internal.d.ts.map +1 -1
  34. package/src/types/internal.ts +9 -8
  35. package/src/types/node-modules.d.ts +45 -8
  36. package/src/types/node-modules.d.ts.map +1 -1
  37. package/src/types/node-modules.ts +56 -15
  38. package/src/types/pattern-replacement.d.ts +62 -0
  39. package/src/types/pattern-replacement.d.ts.map +1 -0
  40. package/src/types/pattern-replacement.ts +70 -0
  41. package/src/types/powers.d.ts +11 -9
  42. package/src/types/powers.d.ts.map +1 -1
  43. package/src/types/powers.ts +11 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@endo/compartment-mapper",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "The compartment mapper assembles Node applications in a sandbox",
5
5
  "keywords": [
6
6
  "node",
@@ -49,31 +49,41 @@
49
49
  },
50
50
  "scripts": {
51
51
  "build": "exit 0",
52
- "prepack": "tsc --build tsconfig.build.json",
53
- "postpack": "git clean -fX \"*.d.ts*\" \"*.d.cts*\" \"*.d.mts*\" \"*.tsbuildinfo\"",
52
+ "prepack": "git clean -fX -e node_modules/ && tsc --build tsconfig.build.json",
53
+ "postpack": "git clean -fX -e node_modules/",
54
54
  "cover": "c8 ava --config test/_ava-ses.config.js",
55
55
  "lint": "yarn lint:types && yarn lint:eslint",
56
56
  "lint-fix": "eslint --fix .",
57
57
  "lint:eslint": "eslint .",
58
58
  "lint:types": "tsc",
59
59
  "prettier-fixtures": "prettier --write --with-node-modules './test/fixtures-*/**/*.*js'",
60
- "test": "ava"
60
+ "test": "ses-ava"
61
61
  },
62
62
  "dependencies": {
63
63
  "@endo/cjs-module-analyzer": "^1.0.11",
64
- "@endo/module-source": "^1.4.0",
64
+ "@endo/module-source": "^1.4.1",
65
65
  "@endo/path-compare": "^1.1.0",
66
66
  "@endo/trampoline": "^1.0.5",
67
67
  "@endo/zip": "^1.1.0",
68
- "ses": "^1.15.0"
68
+ "ses": "^2.0.0"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@endo/env-options": "^1.1.11",
72
+ "@endo/evasive-transform": "^2.1.0",
73
+ "@endo/eventual-send": "^1.5.0",
72
74
  "@endo/init": "^1.1.13",
75
+ "@endo/ses-ava": "^1.4.1",
73
76
  "ava": "catalog:dev",
77
+ "babel-eslint": "^10.1.0",
74
78
  "c8": "catalog:dev",
75
79
  "eslint": "catalog:dev",
76
- "typescript": "~5.9.2"
80
+ "eslint-config-airbnb-base": "^15.0.0",
81
+ "eslint-config-prettier": "^9.1.0",
82
+ "eslint-plugin-eslint-comments": "^3.2.0",
83
+ "eslint-plugin-import": "^2.31.0",
84
+ "prettier": "^3.5.3",
85
+ "ses": "workspace:^",
86
+ "typescript": "catalog:dev"
77
87
  },
78
88
  "files": [
79
89
  "./*.d.ts",
@@ -108,12 +118,16 @@
108
118
  },
109
119
  "ava": {
110
120
  "files": [
111
- "test/**/*.test.*"
121
+ "test/**/*.test.*",
122
+ "!test/**/*.node-condition.test.*"
112
123
  ],
113
124
  "timeout": "2m"
114
125
  },
126
+ "sesAvaConfigs": {
127
+ "node-condition": "test/_ava-node-condition.config.js"
128
+ },
115
129
  "typeCoverage": {
116
130
  "atLeast": 86.14
117
131
  },
118
- "gitHead": "f91329e8616a19f131d009356a5f11ef11c839cc"
132
+ "gitHead": "e74ed050e9d1122d692b00ee14971299c6afbf30"
119
133
  }
@@ -255,7 +255,7 @@ const digestFromMap = async (powers, compartmentMap, options = {}) => {
255
255
 
256
256
  const archiveCompartmentMapText = JSON.stringify(
257
257
  archiveCompartmentMap,
258
- null,
258
+ (key, value) => (key.startsWith('_') ? undefined : value),
259
259
  2,
260
260
  );
261
261
  const archiveCompartmentMapBytes = textEncoder.encode(
@@ -274,7 +274,7 @@ const digestFromMap = async (powers, compartmentMap, options = {}) => {
274
274
  return {
275
275
  compartmentMapBytes: archiveCompartmentMapBytes,
276
276
  sources: archiveSources,
277
- sha512: archiveSha512,
277
+ ...(archiveSha512 !== undefined && { sha512: archiveSha512 }),
278
278
  };
279
279
  };
280
280
 
@@ -300,7 +300,7 @@ export const makeAndHashArchiveFromMap = async (
300
300
  await addSourcesToArchive(archive, sources);
301
301
  const bytes = await archive.snapshot();
302
302
 
303
- return { bytes, sha512 };
303
+ return { bytes, ...(sha512 !== undefined && { sha512 }) };
304
304
  };
305
305
 
306
306
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"capture-lite.d.ts","sourceRoot":"","sources":["capture-lite.js"],"names":[],"mappings":"AAmQO,2CALI,MAAM,GAAG,UAAU,kBACnB,+BAA+B,YAC/B,kBAAkB,GAChB,OAAO,CAAC,aAAa,CAAC,CAsFlC;4BA1SS,YAAY;gCAAZ,YAAY;qDAAZ,YAAY;wCAAZ,YAAY;mCAAZ,YAAY"}
1
+ {"version":3,"file":"capture-lite.d.ts","sourceRoot":"","sources":["capture-lite.js"],"names":[],"mappings":"AA2RO,2CALI,MAAM,GAAG,UAAU,kBACnB,+BAA+B,YAC/B,kBAAkB,GAChB,OAAO,CAAC,aAAa,CAAC,CAwFlC;4BApUS,YAAY;gCAAZ,YAAY;qDAAZ,YAAY;wCAAZ,YAAY;mCAAZ,YAAY"}
@@ -112,7 +112,12 @@ const captureCompartmentMap = (
112
112
  const makePreloader = (
113
113
  compartmentMap,
114
114
  sources,
115
- { log = noop, policy, _preload: preload = [] } = {},
115
+ {
116
+ log = noop,
117
+ policy,
118
+ _preload: preload = [],
119
+ _redundantPreloadHook: redundantPreloadHook = undefined,
120
+ } = {},
116
121
  ) => {
117
122
  const {
118
123
  entry: { module: entryModuleSpecifier },
@@ -173,10 +178,29 @@ const makePreloader = (
173
178
 
174
179
  const compartmentSources = sources[compartmentName];
175
180
 
176
- if (keys(compartmentSources).length) {
181
+ // The default preload entry is the entry module as defined by the
182
+ // package itself. This corresponds to the `ModuleConfiguration` of `.`
183
+ // in the `CompartmentDescriptor`'s `modules` object. Since the key in
184
+ // `compartmentSources` is presumably a resolved relative path, we don't
185
+ // actually know which key to look for! Thus, we are assuming that the
186
+ // `Compartment`'s entry module has been loaded if _any_ sources are
187
+ // present.
188
+ const entryIsLoaded =
189
+ entry === DEFAULT_PRELOAD_ENTRY
190
+ ? keys(compartmentSources).length > 0
191
+ : entry in compartmentSources;
192
+
193
+ if (entryIsLoaded) {
177
194
  log(
178
- `Refusing to preload Compartment ${q(canonicalName)}; already loaded`,
195
+ `Refusing to preload Compartment ${q(canonicalName)} entry ${q(entry)}; already loaded`,
179
196
  );
197
+ if (redundantPreloadHook) {
198
+ redundantPreloadHook({
199
+ canonicalName,
200
+ entry,
201
+ log,
202
+ });
203
+ }
180
204
  } else {
181
205
  const compartment = compartments[compartmentName];
182
206
  if (!compartment) {
@@ -274,6 +298,7 @@ export const captureFromMap = async (
274
298
  Compartment: CompartmentOption = DefaultCompartment,
275
299
  log = noop,
276
300
  _preload: preload = [],
301
+ _redundantPreloadHook: redundantPreloadHook = undefined,
277
302
  packageConnectionsHook,
278
303
  moduleSourceHook,
279
304
  } = options;
@@ -295,6 +320,7 @@ export const captureFromMap = async (
295
320
  log,
296
321
  policy,
297
322
  _preload: preload,
323
+ _redundantPreloadHook: redundantPreloadHook,
298
324
  });
299
325
 
300
326
  const consolidatedExitModuleImportHook = exitModuleImportHookMaker({
@@ -1 +1 @@
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"}
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;AA60B/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;kDA3c1B,YAAY;mDAAZ,YAAY;sDAAZ,YAAY;qDAAZ,YAAY"}
@@ -177,12 +177,13 @@ const assertConditions = (conditions, url) => {
177
177
  */
178
178
  const getModuleConfigurationSpecificProperties = allegedModule => {
179
179
  const {
180
- __createdBy: _createdBy,
181
180
  retained: _retained,
182
181
  deferredError: _deferredError,
183
182
  ...other
184
183
  } = allegedModule;
185
- return other;
184
+ return /** @type {Omit<T, keyof BaseModuleConfiguration>} */ (
185
+ Object.fromEntries(entries(other).filter(([key]) => !key.startsWith('_')))
186
+ );
186
187
  };
187
188
 
188
189
  /**
package/src/digest.js CHANGED
@@ -178,7 +178,7 @@ const translateCompartmentMap = (
178
178
  modules[name] = {
179
179
  location,
180
180
  parser,
181
- sha512,
181
+ ...(sha512 !== undefined && { sha512 }),
182
182
  __createdBy: 'digest',
183
183
  };
184
184
  } else if (isExitModuleSource(source)) {
@@ -1,11 +1,10 @@
1
1
  /**
2
- * @template [T=string] The type of nodes in the graph
2
+ * A generic graph implementation.
3
3
  *
4
- * A generic graph implementation with edge weights.
5
- *
6
- * Edge weights are assumed to be non-negative numbers (including `Infinity`)
4
+ * @template {GenericGraphNode} [T=string] The type of nodes in the graph. If
5
+ * `T` is not a string, relative paths will be compared by coercion to strings.
7
6
  */
8
- export class GenericGraph<T = string> {
7
+ export class GenericGraph<T extends GenericGraphNode = string> {
9
8
  /**
10
9
  * Returns a shallow copy of the `Set` of nodes in the graph.
11
10
  */
@@ -31,23 +30,6 @@ export class GenericGraph<T = string> {
31
30
  * @returns {Set<T>|undefined}
32
31
  */
33
32
  adjacent(node: T): Set<T> | undefined;
34
- /**
35
- * Sets the weight of the given edge between `source` and `target`.
36
- *
37
- * @param {T} source Source node
38
- * @param {T} target Target node
39
- * @param {number} weight New edge weight
40
- * @returns {this}
41
- */
42
- setEdgeWeight(source: T, target: T, weight: number): this;
43
- /**
44
- * Gets the weight of the given edge between `source` and `target`.
45
- *
46
- * @param {T} source Source node
47
- * @param {T} target Target node
48
- * @returns {number} Edge weight from source to target
49
- */
50
- getEdgeWeight(source: T, target: T): number;
51
33
  /**
52
34
  * Adds an edge from the `source` node to `target` node.
53
35
  *
@@ -58,10 +40,9 @@ export class GenericGraph<T = string> {
58
40
  *
59
41
  * @param {T} source Source node
60
42
  * @param {T} target Target node
61
- * @param {number} weight Edge weight from source to target
62
43
  * @returns {this} This graph instance
63
44
  */
64
- addEdge(source: T, target: T, weight: number): this;
45
+ addEdge(source: T, target: T): this;
65
46
  /**
66
47
  * Removes the edge from the `source` node to `target` node.
67
48
  * Does not remove the nodes themselves.
@@ -80,5 +61,6 @@ export class GenericGraph<T = string> {
80
61
  hasEdge(source: T, target: T): boolean;
81
62
  #private;
82
63
  }
83
- export function makeShortestPath<T = string>(graph: GenericGraph<T>): (source: NoInfer<T>, target: NoInfer<T>) => [T, T, ...T[]];
64
+ export function makeShortestPath<T extends GenericGraphNode = string>(graph: GenericGraph<T>): (source: NoInfer<T>, target: NoInfer<T>) => [T, T, ...T[]];
65
+ import type { GenericGraphNode } from './types/generic-graph.js';
84
66
  //# sourceMappingURL=generic-graph.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generic-graph.d.ts","sourceRoot":"","sources":["generic-graph.js"],"names":[],"mappings":"AAyHA;;;;;;GAMG;AACH,0BANc,CAAC;IA+Bb;;OAEG;IACH,oBAEC;IAED;;;;;;OAMG;IACH,cAHW,CAAC,GACC,IAAI,CAUhB;IAED;;;;;OAKG;IACH,iBAHW,CAAC,GACC,IAAI,CAShB;IAED;;;;OAIG;IACH,eAHW,CAAC,GACC,GAAG,CAAC,CAAC,CAAC,GAAC,SAAS,CAI5B;IAED;;;;;;;OAOG;IACH,sBALW,CAAC,UACD,CAAC,UACD,MAAM,GACJ,IAAI,CAWhB;IAED;;;;;;OAMG;IACH,sBAJW,CAAC,UACD,CAAC,GACC,MAAM,CAUlB;IAED;;;;;;;;;;;;OAYG;IACH,gBALW,CAAC,UACD,CAAC,UACD,MAAM,GACJ,IAAI,CAWhB;IAED;;;;;;;OAOG;IACH,mBAJW,CAAC,UACD,CAAC,GACC,IAAI,CAKhB;IAED;;;;;OAKG;IACH,gBAJW,CAAC,UACD,CAAC,GACC,OAAO,CAInB;;CACF;AA6DM,iCAHO,CAAC,kBACJ,YAAY,CAAC,CAAC,CAAC,YAIb,OAAO,CAAC,CAAC,CAAC,UACV,OAAO,CAAC,CAAC,CAAC,KACR,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAO5B"}
1
+ {"version":3,"file":"generic-graph.d.ts","sourceRoot":"","sources":["generic-graph.js"],"names":[],"mappings":"AAsIA;;;;;GAKG;AACH,0BAHiC,CAAC,SAArB,gBAAkB;IAsB7B;;OAEG;IACH,oBAEC;IAED;;;;;;OAMG;IACH,cAHW,CAAC,GACC,IAAI,CAUhB;IAED;;;;;OAKG;IACH,iBAHW,CAAC,GACC,IAAI,CAShB;IAED;;;;OAIG;IACH,eAHW,CAAC,GACC,GAAG,CAAC,CAAC,CAAC,GAAC,SAAS,CAI5B;IAED;;;;;;;;;;;OAWG;IACH,gBAJW,CAAC,UACD,CAAC,GACC,IAAI,CAUhB;IAED;;;;;;;OAOG;IACH,mBAJW,CAAC,UACD,CAAC,GACC,IAAI,CAKhB;IAED;;;;;OAKG;IACH,gBAJW,CAAC,UACD,CAAC,GACC,OAAO,CAInB;;CACF;AAwDM,iCAH0B,CAAC,SAArB,gBAAkB,kBACpB,YAAY,CAAC,CAAC,CAAC,YAOb,OAAO,CAAC,CAAC,CAAC,UACV,OAAO,CAAC,CAAC,CAAC,KACR,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAW5B;sCAvToD,0BAA0B"}
@@ -9,31 +9,49 @@
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
18
  const { quote: q } = assert;
17
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;
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);
62
-
63
- const distanceSource = distances.get(source);
64
- const distanceTarget = distances.get(target);
77
+ const relax = ({ paths, predecessors }, source, target) => {
78
+ const pathSource = paths.get(source);
79
+ assert(pathSource, `Missing path to source ${q(source)}`);
65
80
 
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
- );
81
+ const pathTarget = paths.get(target);
82
+ const newPath = [...pathSource, target];
74
83
 
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
@@ -107,7 +116,11 @@ const getPath = ({ predecessors }, source, target) => {
107
116
  node = currentNode;
108
117
  }
109
118
 
110
- 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
+ );
111
124
 
112
125
  nodeList.push(node);
113
126
 
@@ -120,11 +133,10 @@ const getPath = ({ predecessors }, source, target) => {
120
133
  };
121
134
 
122
135
  /**
123
- * @template [T=string] The type of nodes in the graph
124
- *
125
- * A generic graph implementation with edge weights.
136
+ * A generic graph implementation.
126
137
  *
127
- * 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.
128
140
  */
129
141
  export class GenericGraph {
130
142
  /**
@@ -137,16 +149,10 @@ export class GenericGraph {
137
149
  */
138
150
  #edges;
139
151
 
140
- /**
141
- * @type {Map<T, Map<T, number>>}
142
- */
143
- #edgeWeights;
144
-
145
152
  /**
146
153
  * Initializes internal data structures.
147
154
  */
148
155
  constructor() {
149
- this.#edgeWeights = new Map();
150
156
  this.#edges = new Map();
151
157
  this.#nodes = new Set();
152
158
  }
@@ -199,42 +205,6 @@ export class GenericGraph {
199
205
  return this.#edges.get(node);
200
206
  }
201
207
 
202
- /**
203
- * Sets the weight of the given edge between `source` and `target`.
204
- *
205
- * @param {T} source Source node
206
- * @param {T} target Target node
207
- * @param {number} weight New edge weight
208
- * @returns {this}
209
- */
210
- setEdgeWeight(source, target, weight) {
211
- if (!this.#edgeWeights.has(source)) {
212
- this.#edgeWeights.set(source, new Map());
213
- }
214
- const weights = /** @type {Map<T, number>} */ (
215
- this.#edgeWeights.get(source)
216
- );
217
- weights.set(target, weight);
218
- return this;
219
- }
220
-
221
- /**
222
- * Gets the weight of the given edge between `source` and `target`.
223
- *
224
- * @param {T} source Source node
225
- * @param {T} target Target node
226
- * @returns {number} Edge weight from source to target
227
- */
228
- getEdgeWeight(source, target) {
229
- const weight = this.#edgeWeights.get(source)?.get(target);
230
- if (weight === undefined) {
231
- throw new ReferenceError(
232
- `Edge weight from ${q(source)} to ${q(target)} is not set`,
233
- );
234
- }
235
- return weight;
236
- }
237
-
238
208
  /**
239
209
  * Adds an edge from the `source` node to `target` node.
240
210
  *
@@ -245,17 +215,15 @@ export class GenericGraph {
245
215
  *
246
216
  * @param {T} source Source node
247
217
  * @param {T} target Target node
248
- * @param {number} weight Edge weight from source to target
249
218
  * @returns {this} This graph instance
250
219
  */
251
- addEdge(source, target, weight) {
220
+ addEdge(source, target) {
252
221
  this.addNode(source);
253
222
  this.addNode(target);
254
223
  const adjacentNodes = this.adjacent(source);
255
224
  assert(adjacentNodes, `Source ${q(source)} should have adjacent nodes`);
256
225
 
257
226
  adjacentNodes.add(target);
258
- this.setEdgeWeight(source, target, weight);
259
227
  return this;
260
228
  }
261
229
 
@@ -284,42 +252,32 @@ export class GenericGraph {
284
252
  }
285
253
 
286
254
  /**
287
- * Dijkstra's algorithm for shortest paths in a graph.
288
- * @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
289
260
  * @param {GenericGraph<T>} graph
290
261
  * @param {T} source
291
- * @param {T} target
292
262
  * @returns {TraversalContext<T>}
293
263
  */
294
- const dijkstra = (graph, source, target) => {
264
+ const dijkstra = (graph, source) => {
295
265
  const { nodes } = graph;
296
266
  /** @type {TraversalContext<T>} */
297
267
  const context = {
298
- distances: new Map(),
268
+ paths: new Map(),
299
269
  predecessors: new Map(),
300
- queue: new Set(),
270
+ queue: nodes,
301
271
  };
302
- const { queue, distances } = context;
303
-
304
- for (const node of nodes) {
305
- distances.set(node, Infinity);
306
- }
307
-
308
- assert(
309
- distances.get(source) === Infinity,
310
- `Source ${q(source)} is not in the graph`,
311
- );
312
- assert(
313
- distances.get(target) === Infinity,
314
- `Target ${q(target)} is not in the graph`,
315
- );
316
-
317
- distances.set(source, 0);
272
+ const { queue, paths } = context;
318
273
 
319
274
  for (const node of nodes) {
320
275
  queue.add(node);
321
276
  }
322
277
 
278
+ assert(queue.has(source), `Source ${q(source)} is not in the graph`);
279
+ paths.set(source, []);
280
+
323
281
  while (queue.size !== 0) {
324
282
  const node = extractMin(context);
325
283
  if (node === undefined) {
@@ -328,7 +286,7 @@ const dijkstra = (graph, source, target) => {
328
286
  const adjacent = graph.adjacent(node);
329
287
  if (adjacent) {
330
288
  for (const edge of adjacent) {
331
- relax(graph, context, node, edge);
289
+ relax(context, node, edge);
332
290
  }
333
291
  }
334
292
  }
@@ -336,20 +294,32 @@ const dijkstra = (graph, source, target) => {
336
294
  };
337
295
 
338
296
  /**
339
- * Returns a function which uses Dijkstra's shortest path algorithm to compute
340
- * 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`.
341
299
  *
342
- * @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]
343
306
  * @param {GenericGraph<T>} graph Graph to use
344
307
  */
345
308
  export const makeShortestPath = graph => {
309
+ /** @type {Map<T, TraversalContext<T>>} */
310
+ const contextCache = new Map();
311
+
346
312
  /**
347
313
  * @param {NoInfer<T>} source Source node
348
314
  * @param {NoInfer<T>} target Target node
349
315
  * @returns {[T, T, ...T[]]} Nodes from `source` to `target` inclusive (minimum of two nodes)
350
316
  */
351
317
  const shortestPath = (source, target) => {
352
- 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
+ }
353
323
  return getPath(context, source, target);
354
324
  };
355
325
  return shortestPath;
@@ -1 +1 @@
1
- {"version":3,"file":"import-hook.d.ts","sourceRoot":"","sources":["import-hook.js"],"names":[],"mappings":"AAouBA;;;;;;;GAOG;AACH,mDALW,aAAa,gBACb,aAAa,kKACb,6BAA6B,GAC3B,kBAAkB,CAqM9B;AA9tBM,oGALJ;IAAqC,OAAO;IACN,oBAAoB;IACnC,oBAAoB,EAAnC,MAAM;CACd,GAAU,oBAAoB,GAAC,SAAS,CA8B1C;AA0UM,gDALI,MAAM,GAAC,UAAU,gBACjB,aAAa,wMACb,0BAA0B,GACxB,eAAe,CA6K3B;mCAtrBS,YAAY;mCAAZ,YAAY;mDAAZ,YAAY;wCAAZ,YAAY;0CAAZ,YAAY;4BAAZ,YAAY;gCAAZ,YAAY;gDAAZ,YAAY;qCAAZ,YAAY"}
1
+ {"version":3,"file":"import-hook.d.ts","sourceRoot":"","sources":["import-hook.js"],"names":[],"mappings":"AAquBA;;;;;;;GAOG;AACH,mDALW,aAAa,gBACb,aAAa,kKACb,6BAA6B,GAC3B,kBAAkB,CAqM9B;AA9tBM,oGALJ;IAAqC,OAAO;IACN,oBAAoB;IACnC,oBAAoB,EAAnC,MAAM;CACd,GAAU,oBAAoB,GAAC,SAAS,CA8B1C;AA0UM,gDALI,MAAM,GAAC,UAAU,gBACjB,aAAa,wMACb,0BAA0B,GACxB,eAAe,CA6K3B;mCAtrBS,YAAY;mCAAZ,YAAY;mDAAZ,YAAY;wCAAZ,YAAY;0CAAZ,YAAY;4BAAZ,YAAY;gCAAZ,YAAY;gDAAZ,YAAY;qCAAZ,YAAY"}
@@ -41,7 +41,8 @@
41
41
  * CompartmentModuleConfiguration,
42
42
  * LogOptions,
43
43
  * CanonicalName,
44
- * LocalModuleSource
44
+ * LocalModuleSource,
45
+ * ModuleSourceHook,
45
46
  * } from './types.js'
46
47
  */
47
48
 
@@ -262,7 +263,7 @@ const nominateCandidates = (moduleSpecifier, searchSuffixes) => {
262
263
  *
263
264
  * Preprocesses the fields for the hook.
264
265
  *
265
- * @param {import('./types.js').ModuleSourceHook | undefined} moduleSourceHook Hook function
266
+ * @param {ModuleSourceHook | undefined} moduleSourceHook Hook function
266
267
  * @param {LocalModuleSource} moduleSource Original `LocalModuleSource` object
267
268
  * @param {CanonicalName} canonicalName Canonical name of the compartment/package
268
269
  * @param {LogOptions} options Options
@@ -485,7 +486,7 @@ function* chooseModuleDescriptor(
485
486
  parser,
486
487
  bytes: transformedBytes,
487
488
  record: concreteRecord,
488
- sha512,
489
+ ...(sha512 !== undefined && { sha512 }),
489
490
  };
490
491
 
491
492
  packageSources[candidateSpecifier] = localModuleSource;
@@ -1,7 +1,9 @@
1
- export function inferExportsEntries({ main, module, exports }: PackageDescriptor, conditions: Set<string>, types: LanguageForExtension): Generator<string[], void, any>;
1
+ export function inferExportsEntries({ main, module, exports }: PackageDescriptor, conditions: Set<string>, types: LanguageForExtension): Generator<string[] | [string, string | null], void, any>;
2
2
  export function inferExports(descriptor: PackageDescriptor, conditions: Set<string>, types: LanguageForExtension): Record<string, string>;
3
- export function inferExportsAndAliases(descriptor: PackageDescriptor, externalAliases: Node["externalAliases"], internalAliases: Node["internalAliases"], conditions: Set<string>, types: Record<string, string>): void;
3
+ export function inferExportsAliasesAndPatterns(descriptor: PackageDescriptor, externalAliases: Node["externalAliases"], internalAliases: Node["internalAliases"], patterns: PatternDescriptor[], conditions: Set<string>, types: Record<string, string>, log: LogFn): void;
4
4
  import type { PackageDescriptor } from './types.js';
5
5
  import type { LanguageForExtension } from './types.js';
6
6
  import type { Node } from './types/node-modules.js';
7
+ import type { PatternDescriptor } from './types/pattern-replacement.js';
8
+ import type { LogFn } from './types/external.js';
7
9
  //# sourceMappingURL=infer-exports.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"infer-exports.d.ts","sourceRoot":"","sources":["infer-exports.js"],"names":[],"mappings":"AAsIO,+DAPI,iBAAiB,cACjB,GAAG,CAAC,MAAM,CAAC,SAEX,oBAAoB,kCA2B9B;AAkBM,yCAPI,iBAAiB,cACjB,GAAG,CAAC,MAAM,CAAC,SAEX,oBAAoB,GAElB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAG8B;AAU1D,mDANI,iBAAiB,mBACjB,IAAI,CAAC,iBAAiB,CAAC,mBACvB,IAAI,CAAC,iBAAiB,CAAC,cACvB,GAAG,CAAC,MAAM,CAAC,SACX,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,QA6ChC;uCA1NyD,YAAY;0CAAZ,YAAY;0BAC/C,yBAAyB"}
1
+ {"version":3,"file":"infer-exports.d.ts","sourceRoot":"","sources":["infer-exports.js"],"names":[],"mappings":"AAyLO,+DAPI,iBAAiB,cACjB,GAAG,CAAC,MAAM,CAAC,SAEX,oBAAoB,4DA2B9B;AAkBM,yCAPI,iBAAiB,cACjB,GAAG,CAAC,MAAM,CAAC,SAEX,oBAAoB,GAElB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAG8B;AAyD1D,2DARI,iBAAiB,mBACjB,IAAI,CAAC,iBAAiB,CAAC,mBACvB,IAAI,CAAC,iBAAiB,CAAC,YACvB,iBAAiB,EAAE,cACnB,GAAG,CAAC,MAAM,CAAC,SACX,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,OACtB,KAAK,QAsFf;uCArWyD,YAAY;0CAAZ,YAAY;0BAE7B,yBAAyB;uCAC9B,gCAAgC;2BAF5C,qBAAqB"}