@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
@@ -1,14 +1,43 @@
1
1
  /* Validates a compartment map against its schema. */
2
2
 
3
- import { assertPackagePolicy } from './policy-format.js';
3
+ import {
4
+ assertPackagePolicy,
5
+ ATTENUATORS_COMPARTMENT,
6
+ ENTRY_COMPARTMENT,
7
+ } from './policy-format.js';
4
8
 
5
- /** @import {CompartmentMapDescriptor} from './types.js' */
9
+ /**
10
+ * @import {
11
+ * FileCompartmentDescriptor,
12
+ * FileCompartmentMapDescriptor,
13
+ * FileModuleConfiguration,
14
+ * CompartmentMapDescriptor,
15
+ * EntryDescriptor,
16
+ * ModuleConfiguration,
17
+ * ExitModuleConfiguration,
18
+ * CompartmentModuleConfiguration,
19
+ * CompartmentDescriptor,
20
+ * ScopeDescriptor,
21
+ * BaseModuleConfiguration,
22
+ * DigestedCompartmentMapDescriptor,
23
+ * PackageCompartmentMapDescriptor,
24
+ * PackageCompartmentDescriptor,
25
+ * FileUrlString,
26
+ * LanguageForExtension,
27
+ * LanguageForModuleSpecifier,
28
+ * ModuleConfigurationKind,
29
+ * ModuleConfigurationKindToType,
30
+ * ErrorModuleConfiguration,
31
+ * DigestedCompartmentDescriptor} from './types.js'
32
+ */
6
33
 
7
34
  // TODO convert to the new `||` assert style.
8
35
  // Deferred because this file pervasively uses simple template strings rather than
9
36
  // template strings tagged with `assert.details` (aka `X`), and uses
10
37
  // this definition of `q` rather than `assert.quote`
11
38
  const q = JSON.stringify;
39
+ const { keys, entries } = Object;
40
+ const { isArray } = Array;
12
41
 
13
42
  /** @type {(a: string, b: string) => number} */
14
43
  // eslint-disable-next-line no-nested-ternary
@@ -26,432 +55,887 @@ function* enumerate(iterable) {
26
55
  }
27
56
  }
28
57
 
58
+ /**
59
+ * Type guard for a string value.
60
+ *
61
+ * @overload
62
+ * @param {unknown} value
63
+ * @param {string} keypath
64
+ * @param {string} url
65
+ * @returns {asserts value is string}
66
+ */
67
+
68
+ /**
69
+ * Type guard for a string value with a custom assertion failure message.
70
+ *
71
+ * @overload
72
+ * @param {unknown} value
73
+ * @param {string} message
74
+ * @returns {asserts value is string}
75
+ */
76
+
77
+ /**
78
+ * Type guard for a string value.
79
+ *
80
+ * @param {unknown} value
81
+ * @param {string} pathOrMessage
82
+ * @param {string} url
83
+ * @returns {asserts value is string}
84
+ */
85
+ const assertString = (value, pathOrMessage, url) => {
86
+ const keypath = pathOrMessage;
87
+ assert.typeof(
88
+ value,
89
+ 'string',
90
+ `${keypath} in ${q(url)} must be a string; got ${q(value)}`,
91
+ );
92
+ };
93
+
94
+ /**
95
+ * Asserts the `label` field valid
96
+ *
97
+ * @param {unknown} allegedLabel
98
+ * @param {string} keypath
99
+ * @param {string} url
100
+ * @returns {asserts alleged is string}
101
+ */
102
+ const assertLabel = (allegedLabel, keypath, url) => {
103
+ assertString(allegedLabel, keypath, url);
104
+ if (allegedLabel === ATTENUATORS_COMPARTMENT) {
105
+ return;
106
+ }
107
+ if (allegedLabel === ENTRY_COMPARTMENT) {
108
+ return;
109
+ }
110
+ assert(
111
+ /^(?:@[a-z][a-z0-9-.]*\/)?[a-z][a-z0-9-.]*(?:>(?:@[a-z][a-z0-9-.]*\/)?[a-z][a-z0-9-.]*)*$/.test(
112
+ allegedLabel,
113
+ ),
114
+ `${keypath} must be a canonical name in ${q(url)}; got ${q(allegedLabel)}`,
115
+ );
116
+ };
117
+
118
+ /**
119
+ * @param {unknown} allegedObject
120
+ * @param {string} keypath
121
+ * @param {string} url
122
+ * @returns {asserts allegedObject is Record<PropertyKey, unknown>}
123
+ */
124
+ const assertPlainObject = (allegedObject, keypath, url) => {
125
+ const object = Object(allegedObject);
126
+ assert(
127
+ object === allegedObject &&
128
+ !isArray(object) &&
129
+ !(typeof object === 'function'),
130
+ `${keypath} must be an object; got ${q(allegedObject)} of type ${q(typeof allegedObject)} in ${q(url)}`,
131
+ );
132
+ };
133
+
134
+ /**
135
+ *
136
+ * @param {unknown} value
137
+ * @param {string} keypath
138
+ * @param {string} url
139
+ * @returns {asserts value is boolean}
140
+ */
141
+ const assertBoolean = (value, keypath, url) => {
142
+ assert.typeof(
143
+ value,
144
+ 'boolean',
145
+ `${keypath} in ${q(url)} must be a boolean; got ${q(value)}`,
146
+ );
147
+ };
148
+
29
149
  /**
30
150
  * @param {Record<string, unknown>} object
31
151
  * @param {string} message
32
152
  */
33
153
  const assertEmptyObject = (object, message) => {
34
- assert(Object.keys(object).length === 0, message);
154
+ assert(keys(object).length === 0, message);
35
155
  };
36
156
 
37
157
  /**
38
158
  * @param {unknown} conditions
39
159
  * @param {string} url
160
+ * @returns {asserts conditions is CompartmentMapDescriptor['tags']}
40
161
  */
41
162
  const assertConditions = (conditions, url) => {
42
163
  if (conditions === undefined) return;
43
164
  assert(
44
- Array.isArray(conditions),
45
- `conditions must be an array, got ${conditions} in ${q(url)}`,
165
+ isArray(conditions),
166
+ `conditions must be an array; got ${conditions} in ${q(url)}`,
46
167
  );
47
168
  for (const [index, value] of enumerate(conditions)) {
48
- assert.typeof(
49
- value,
50
- 'string',
51
- `conditions[${index}] must be a string, got ${value} in ${q(url)}`,
52
- );
169
+ assertString(value, `conditions[${index}]`, url);
53
170
  }
54
171
  };
55
172
 
56
173
  /**
57
- * @param {Record<string, unknown>} allegedModule
58
- * @param {string} path
174
+ * @template {Partial<ModuleConfiguration>} T
175
+ * @param {T} allegedModule
176
+ * @returns {Omit<T, keyof BaseModuleConfiguration>}
177
+ */
178
+ const getModuleConfigurationSpecificProperties = allegedModule => {
179
+ const {
180
+ retained: _retained,
181
+ deferredError: _deferredError,
182
+ ...other
183
+ } = allegedModule;
184
+ return /** @type {Omit<T, keyof BaseModuleConfiguration>} */ (
185
+ Object.fromEntries(entries(other).filter(([key]) => !key.startsWith('_')))
186
+ );
187
+ };
188
+
189
+ /**
190
+ *
191
+ * @param {Record<PropertyKey, unknown>} allegedModule
192
+ * @param {string} keypath
59
193
  * @param {string} url
194
+ * @returns {asserts allegedModule is ModuleConfiguration}
60
195
  */
61
- const assertCompartmentModule = (allegedModule, path, url) => {
62
- const { compartment, module, retained, ...extra } = allegedModule;
196
+ const assertBaseModuleConfiguration = (allegedModule, keypath, url) => {
197
+ const { deferredError, retained, createdBy } = allegedModule;
198
+ if (deferredError !== undefined) {
199
+ assertString(deferredError, `${keypath}.deferredError`, url);
200
+ }
201
+ if (retained !== undefined) {
202
+ assertBoolean(retained, `${keypath}.retained`, url);
203
+ }
204
+ if (createdBy !== undefined) {
205
+ assertString(createdBy, `${keypath}.createdBy`, url);
206
+ }
207
+ };
208
+
209
+ /**
210
+ * @param {ModuleConfiguration} moduleDescriptor
211
+ * @param {string} keypath
212
+ * @param {string} url
213
+ * @returns {asserts allegedModule is CompartmentModuleConfiguration}
214
+ */
215
+ const assertCompartmentModuleConfiguration = (
216
+ moduleDescriptor,
217
+ keypath,
218
+ url,
219
+ ) => {
220
+ const { compartment, module, ...extra } =
221
+ getModuleConfigurationSpecificProperties(
222
+ /** @type {CompartmentModuleConfiguration} */ (moduleDescriptor),
223
+ );
63
224
  assertEmptyObject(
64
225
  extra,
65
- `${path} must not have extra properties, got ${q({
66
- extra,
67
- compartment,
68
- })} in ${q(url)}`,
226
+ `${keypath} must not have extra properties; got ${q(extra)} in ${q(url)}`,
69
227
  );
70
- assert.typeof(
71
- compartment,
72
- 'string',
73
- `${path}.compartment must be a string, got ${q(compartment)} in ${q(url)}`,
74
- );
75
- assert.typeof(
76
- module,
77
- 'string',
78
- `${path}.module must be a string, got ${q(module)} in ${q(url)}`,
79
- );
80
- if (retained !== undefined) {
81
- assert.typeof(
82
- retained,
83
- 'boolean',
84
- `${path}.retained must be a boolean, got ${q(retained)} in ${q(url)}`,
85
- );
86
- }
228
+
229
+ assertString(compartment, `${keypath}.compartment`, url);
230
+ assertString(module, `${keypath}.module`, url);
87
231
  };
88
232
 
89
233
  /**
90
- * @param {Record<string, unknown>} allegedModule
91
- * @param {string} path
234
+ * @param {ModuleConfiguration} moduleDescriptor
235
+ * @param {string} keypath
92
236
  * @param {string} url
237
+ * @returns {asserts allegedModule is FileModuleConfiguration}
93
238
  */
94
- const assertFileModule = (allegedModule, path, url) => {
95
- const { location, parser, sha512, ...extra } = allegedModule;
239
+ const assertFileModuleConfiguration = (moduleDescriptor, keypath, url) => {
240
+ const { location, parser, sha512, ...extra } =
241
+ getModuleConfigurationSpecificProperties(
242
+ /** @type {FileModuleConfiguration} */ (moduleDescriptor),
243
+ );
96
244
  assertEmptyObject(
97
245
  extra,
98
- `${path} must not have extra properties, got ${q(
99
- Object.keys(extra),
246
+ `${keypath} must not have extra properties; got ${q(
247
+ keys(extra),
100
248
  )} in ${q(url)}`,
101
249
  );
102
- assert.typeof(
103
- location,
104
- 'string',
105
- `${path}.location must be a string, got ${q(location)} in ${q(url)}`,
106
- );
107
- assert.typeof(
108
- parser,
109
- 'string',
110
- `${path}.parser must be a string, got ${q(parser)} in ${q(url)}`,
111
- );
250
+ if (location !== undefined) {
251
+ assertString(location, `${keypath}.location`, url);
252
+ }
253
+ assertString(parser, `${keypath}.parser`, url);
112
254
 
113
255
  if (sha512 !== undefined) {
114
- assert.typeof(
115
- sha512,
116
- 'string',
117
- `${path}.sha512 must be a string, got ${q(sha512)} in ${q(url)}`,
118
- );
256
+ assertString(sha512, `${keypath}.sha512`, url);
119
257
  }
120
258
  };
121
259
 
122
260
  /**
123
- * @param {Record<string, unknown>} allegedModule
124
- * @param {string} path
261
+ * @param {ModuleConfiguration} moduleDescriptor
262
+ * @param {string} keypath
125
263
  * @param {string} url
264
+ * @returns {asserts allegedModule is ExitModuleConfiguration}
126
265
  */
127
- const assertExitModule = (allegedModule, path, url) => {
128
- const { exit, ...extra } = allegedModule;
266
+ const assertExitModuleConfiguration = (moduleDescriptor, keypath, url) => {
267
+ const { exit, ...extra } = getModuleConfigurationSpecificProperties(
268
+ /** @type {ExitModuleConfiguration} */ (moduleDescriptor),
269
+ );
129
270
  assertEmptyObject(
130
271
  extra,
131
- `${path} must not have extra properties, got ${q(
132
- Object.keys(extra),
272
+ `${keypath} must not have extra properties; got ${q(
273
+ keys(extra),
133
274
  )} in ${q(url)}`,
134
275
  );
135
- assert.typeof(
136
- exit,
137
- 'string',
138
- `${path}.exit must be a string, got ${q(exit)} in ${q(url)}`,
139
- );
276
+ assertString(exit, `${keypath}.exit`, url);
140
277
  };
141
278
 
142
279
  /**
280
+ *
281
+ * @param {ModuleConfiguration} moduleDescriptor
282
+ * @param {string} keypath
283
+ * @param {string} url
284
+ * @returns {asserts moduleDescriptor is ErrorModuleConfiguration}
285
+ */
286
+ const assertErrorModuleConfiguration = (moduleDescriptor, keypath, url) => {
287
+ const { deferredError } = moduleDescriptor;
288
+ if (deferredError) {
289
+ assertString(deferredError, `${keypath}.deferredError`, url);
290
+ }
291
+ };
292
+
293
+ /**
294
+ * @template {ModuleConfigurationKind[]} Kinds
295
+ * @overload
143
296
  * @param {unknown} allegedModule
144
- * @param {string} path
297
+ * @param {string} keypath
145
298
  * @param {string} url
299
+ * @param {Kinds} kinds
300
+ * @returns {asserts allegedModule is ModuleConfigurationKindToType<Kinds>}
146
301
  */
147
- const assertModule = (allegedModule, path, url) => {
148
- const moduleDescriptor = Object(allegedModule);
302
+
303
+ /**
304
+ * @overload
305
+ * @param {unknown} allegedModule
306
+ * @param {string} keypath
307
+ * @param {string} url
308
+ * @returns {asserts allegedModule is ModuleConfiguration}
309
+ */
310
+
311
+ /**
312
+ * @param {unknown} allegedModule
313
+ * @param {string} keypath
314
+ * @param {string} url
315
+ * @param {ModuleConfigurationKind[]} kinds
316
+ */
317
+ function assertModuleConfiguration(allegedModule, keypath, url, kinds) {
318
+ assertPlainObject(allegedModule, keypath, url);
319
+ assertBaseModuleConfiguration(allegedModule, keypath, url);
320
+
321
+ const finalKinds =
322
+ kinds.length > 0
323
+ ? kinds
324
+ : /** @type {ModuleConfigurationKind[]} */ ([
325
+ 'compartment',
326
+ 'file',
327
+ 'exit',
328
+ 'error',
329
+ ]);
330
+ /** @type {Error[]} */
331
+ const errors = [];
332
+ for (const kind of finalKinds) {
333
+ switch (kind) {
334
+ case 'compartment': {
335
+ try {
336
+ assertCompartmentModuleConfiguration(allegedModule, keypath, url);
337
+ } catch (error) {
338
+ errors.push(error);
339
+ }
340
+ break;
341
+ }
342
+ case 'file': {
343
+ try {
344
+ assertFileModuleConfiguration(allegedModule, keypath, url);
345
+ } catch (error) {
346
+ errors.push(error);
347
+ }
348
+ break;
349
+ }
350
+ case 'exit': {
351
+ try {
352
+ assertExitModuleConfiguration(allegedModule, keypath, url);
353
+ } catch (error) {
354
+ errors.push(error);
355
+ }
356
+ break;
357
+ }
358
+ case 'error': {
359
+ try {
360
+ assertErrorModuleConfiguration(allegedModule, keypath, url);
361
+ } catch (error) {
362
+ errors.push(error);
363
+ }
364
+ break;
365
+ }
366
+ default:
367
+ throw new TypeError(
368
+ `Unknown module descriptor kind ${q(kind)} in ${q(url)}`,
369
+ );
370
+ }
371
+ }
372
+
149
373
  assert(
150
- allegedModule === moduleDescriptor && !Array.isArray(moduleDescriptor),
151
- `${path} must be an object, got ${allegedModule} in ${q(url)}`,
374
+ errors.length < finalKinds.length,
375
+ `invalid module descriptor in ${q(url)} at ${q(keypath)}; expected to match one of ${q(kinds)}: ${errors.map(err => err.message).join('; ')}`,
152
376
  );
377
+ }
153
378
 
154
- const { compartment, module, location, parser, exit, deferredError } =
155
- moduleDescriptor;
156
- if (compartment !== undefined || module !== undefined) {
157
- assertCompartmentModule(moduleDescriptor, path, url);
158
- } else if (location !== undefined || parser !== undefined) {
159
- assertFileModule(moduleDescriptor, path, url);
160
- } else if (exit !== undefined) {
161
- assertExitModule(moduleDescriptor, path, url);
162
- } else if (deferredError !== undefined) {
163
- assert.typeof(
164
- deferredError,
165
- 'string',
166
- `${path}.deferredError must be a string contaiing an error message`,
379
+ /**
380
+ * @param {unknown} allegedModules
381
+ * @param {string} keypath
382
+ * @param {string} url
383
+ * @returns {asserts allegedModules is Record<string, ModuleConfiguration>}
384
+ */
385
+ const assertModuleConfigurations = (allegedModules, keypath, url) => {
386
+ assertPlainObject(allegedModules, keypath, url);
387
+ for (const [key, value] of entries(allegedModules)) {
388
+ assertString(
389
+ key,
390
+ `all keys of ${keypath}.modules must be strings; got ${key} in ${q(url)}`,
167
391
  );
168
- } else {
169
- assert.fail(
170
- `${path} is not a valid module descriptor, got ${q(allegedModule)} in ${q(
171
- url,
172
- )}`,
392
+ assertModuleConfiguration(value, `${keypath}.modules[${q(key)}]`, url);
393
+ }
394
+ };
395
+
396
+ /**
397
+ * @param {unknown} allegedModules
398
+ * @param {string} keypath
399
+ * @param {string} url
400
+ * @returns {asserts allegedModules is Record<string, FileModuleConfiguration|CompartmentModuleConfiguration>}
401
+ */
402
+ const assertFileModuleConfigurations = (allegedModules, keypath, url) => {
403
+ assertPlainObject(allegedModules, keypath, url);
404
+ for (const [key, value] of entries(allegedModules)) {
405
+ assertString(
406
+ key,
407
+ `all keys of ${keypath}.modules must be strings; got ${key} in ${q(url)}`,
173
408
  );
409
+ assertModuleConfiguration(value, `${keypath}.modules[${q(key)}]`, url, [
410
+ 'file',
411
+ 'compartment',
412
+ 'error',
413
+ ]);
174
414
  }
175
415
  };
176
416
 
177
417
  /**
178
418
  * @param {unknown} allegedModules
179
- * @param {string} path
419
+ * @param {string} keypath
180
420
  * @param {string} url
421
+ * @returns {asserts allegedModules is Record<string, ModuleConfiguration>}
181
422
  */
182
- const assertModules = (allegedModules, path, url) => {
183
- const modules = Object(allegedModules);
184
- assert(
185
- allegedModules === modules || !Array.isArray(modules),
186
- `modules must be an object, got ${q(allegedModules)} in ${q(url)}`,
187
- );
188
- for (const [key, value] of Object.entries(modules)) {
189
- assertModule(value, `${path}.modules[${q(key)}]`, url);
423
+ const assertDigestedModuleConfigurations = (allegedModules, keypath, url) => {
424
+ assertPlainObject(allegedModules, keypath, url);
425
+ for (const [key, value] of entries(allegedModules)) {
426
+ assertString(
427
+ key,
428
+ `all keys of ${keypath}.modules must be strings; got ${key} in ${q(url)}`,
429
+ );
430
+ assertModuleConfiguration(value, `${keypath}.modules[${q(key)}]`, url, [
431
+ 'file',
432
+ 'exit',
433
+ 'error',
434
+ ]);
190
435
  }
191
436
  };
192
437
 
193
438
  /**
194
439
  * @param {unknown} allegedParsers
195
- * @param {string} path
440
+ * @param {string} keypath
196
441
  * @param {string} url
442
+ * @returns {asserts allegedParsers is LanguageForExtension}
197
443
  */
198
- const assertParsers = (allegedParsers, path, url) => {
199
- if (allegedParsers === undefined) {
200
- return;
201
- }
202
- const parsers = Object(allegedParsers);
203
- assert(
204
- allegedParsers === parsers && !Array.isArray(parsers),
205
- `${path}.parsers must be an object, got ${allegedParsers} in ${q(url)}`,
206
- );
444
+ const assertParsers = (allegedParsers, keypath, url) => {
445
+ assertPlainObject(allegedParsers, `${keypath}.parsers`, url);
207
446
 
208
- for (const [key, value] of Object.entries(parsers)) {
209
- assert.typeof(
447
+ for (const [key, value] of entries(allegedParsers)) {
448
+ assertString(
210
449
  key,
211
- 'string',
212
- `all keys of ${path}.parsers must be strings, got ${key} in ${q(url)}`,
213
- );
214
- assert.typeof(
215
- value,
216
- 'string',
217
- `${path}.parsers[${q(key)}] must be a string, got ${value} in ${q(url)}`,
450
+ `all keys of ${keypath}.parsers must be strings; got ${key} in ${q(url)}`,
218
451
  );
452
+ assertString(value, `${keypath}.parsers[${q(key)}]`, url);
219
453
  }
220
454
  };
221
455
 
222
456
  /**
223
- * @param {unknown} allegedScope
224
- * @param {string} path
457
+ * @overload
458
+ * @param {unknown} allegedTruthyValue
459
+ * @param {string} keypath
225
460
  * @param {string} url
461
+ * @returns {asserts allegedTruthyValue is NonNullable<unknown>}
226
462
  */
227
- const assertScope = (allegedScope, path, url) => {
228
- const scope = Object(allegedScope);
463
+
464
+ /**
465
+ *
466
+ * @overload
467
+ * @param {unknown} allegedTruthyValue
468
+ * @param {string} message
469
+ * @returns {asserts allegedTruthyValue is NonNullable<unknown>}
470
+ */
471
+
472
+ /**
473
+ *
474
+ * @param {unknown} allegedTruthyValue
475
+ * @param {string} keypath
476
+ * @param {string} [url]
477
+ * @returns {asserts allegedTruthyValue is NonNullable<unknown>}
478
+ */
479
+ const assertTruthy = (allegedTruthyValue, keypath, url) => {
229
480
  assert(
230
- allegedScope === scope && !Array.isArray(scope),
231
- `${path} must be an object, got ${allegedScope} in ${q(url)}`,
481
+ allegedTruthyValue,
482
+ url
483
+ ? `${keypath} in ${q(url)} must be truthy; got ${q(allegedTruthyValue)}`
484
+ : url,
232
485
  );
486
+ };
487
+
488
+ /**
489
+ * @template [T=string]
490
+ * @typedef {(value: unknown, keypath: string, url: string) => void} AssertFn
491
+ */
233
492
 
234
- const { compartment, ...extra } = scope;
493
+ /**
494
+ * @template [T=string]
495
+ * @param {unknown} allegedScope
496
+ * @param {string} keypath
497
+ * @param {string} url
498
+ * @param {AssertFn<T>} [assertCompartmentValue]
499
+ * @returns {asserts allegedScope is ScopeDescriptor<T>}
500
+ */
501
+ const assertScope = (allegedScope, keypath, url, assertCompartmentValue) => {
502
+ assertPlainObject(allegedScope, keypath, url);
503
+
504
+ const { compartment, ...extra } = allegedScope;
235
505
  assertEmptyObject(
236
506
  extra,
237
- `${path} must not have extra properties, got ${q(
238
- Object.keys(extra),
507
+ `${keypath} must not have extra properties; got ${q(
508
+ keys(extra),
239
509
  )} in ${q(url)}`,
240
510
  );
241
511
 
242
- assert.typeof(
243
- compartment,
244
- 'string',
245
- `${path}.compartment must be a string, got ${q(compartment)} in ${q(url)}`,
246
- );
512
+ if (assertCompartmentValue) {
513
+ assertCompartmentValue(compartment, `${keypath}.compartment`, url);
514
+ } else {
515
+ assertString(compartment, `${keypath}.compartment`, url);
516
+ }
247
517
  };
248
518
 
249
519
  /**
520
+ * @template [T=string]
250
521
  * @param {unknown} allegedScopes
251
- * @param {string} path
522
+ * @param {string} keypath
252
523
  * @param {string} url
524
+ * @param {AssertFn<T>} [assertCompartmentValue]
525
+ * @returns {asserts allegedScopes is Record<string, ScopeDescriptor<T>>}
253
526
  */
254
- const assertScopes = (allegedScopes, path, url) => {
255
- if (allegedScopes === undefined) {
256
- return;
257
- }
258
- const scopes = Object(allegedScopes);
259
- assert(
260
- allegedScopes === scopes && !Array.isArray(scopes),
261
- `${path}.scopes must be an object, got ${q(allegedScopes)} in ${q(url)}`,
262
- );
527
+ const assertScopes = (
528
+ allegedScopes,
529
+ keypath,
530
+ url,
531
+ assertCompartmentValue = assertString,
532
+ ) => {
533
+ assertPlainObject(allegedScopes, keypath, url);
263
534
 
264
- for (const [key, value] of Object.entries(scopes)) {
265
- assert.typeof(
535
+ for (const [key, value] of entries(allegedScopes)) {
536
+ assertString(
266
537
  key,
267
- 'string',
268
- `all keys of ${path}.scopes must be strings, got ${key} in ${q(url)}`,
538
+ `all keys of ${keypath}.scopes must be strings; got ${key} in ${q(url)}`,
539
+ );
540
+ assertScope(
541
+ value,
542
+ `${keypath}.scopes[${q(key)}]`,
543
+ url,
544
+ assertCompartmentValue,
269
545
  );
270
- assertScope(value, `${path}.scopes[${q(key)}]`, url);
271
546
  }
272
547
  };
273
548
 
274
549
  /**
275
550
  * @param {unknown} allegedTypes
276
- * @param {string} path
551
+ * @param {string} keypath
277
552
  * @param {string} url
553
+ * @returns {asserts allegedTypes is LanguageForModuleSpecifier}
278
554
  */
279
- const assertTypes = (allegedTypes, path, url) => {
280
- if (allegedTypes === undefined) {
281
- return;
555
+ const assertTypes = (allegedTypes, keypath, url) => {
556
+ assertPlainObject(allegedTypes, `${keypath}.types`, url);
557
+
558
+ for (const [key, value] of entries(allegedTypes)) {
559
+ assertString(
560
+ key,
561
+ `all keys of ${keypath}.types must be strings; got ${key} in ${q(url)}`,
562
+ );
563
+ assertString(value, `${keypath}.types[${q(key)}]`, url);
564
+ }
565
+ };
566
+
567
+ /**
568
+ * @template {Record<string, ModuleConfiguration>} [M=Record<string, ModuleConfiguration>]
569
+ * @param {unknown} allegedCompartment
570
+ * @param {string} keypath
571
+ * @param {string} url
572
+ * @param {AssertFn<M>} [moduleConfigurationAssertionFn]
573
+ * @returns {asserts allegedCompartment is CompartmentDescriptor}
574
+ */
575
+ const assertCompartmentDescriptor = (
576
+ allegedCompartment,
577
+ keypath,
578
+ url,
579
+ moduleConfigurationAssertionFn = assertModuleConfigurations,
580
+ ) => {
581
+ assertPlainObject(allegedCompartment, keypath, url);
582
+
583
+ const {
584
+ location,
585
+ name,
586
+ parsers,
587
+ types,
588
+ scopes,
589
+ modules,
590
+ policy,
591
+ sourceDirname,
592
+ retained,
593
+ } = allegedCompartment;
594
+
595
+ assertString(location, `${keypath}.location`, url);
596
+ assertString(name, `${keypath}.name`, url);
597
+
598
+ // TODO: It may be prudent to assert that there exists some module referring
599
+ // to its own compartment
600
+
601
+ moduleConfigurationAssertionFn(modules, keypath, url);
602
+
603
+ if (parsers !== undefined) {
604
+ assertParsers(parsers, keypath, url);
605
+ }
606
+ if (scopes !== undefined) {
607
+ assertScopes(scopes, keypath, url);
608
+ }
609
+ if (types !== undefined) {
610
+ assertTypes(types, keypath, url);
282
611
  }
283
- const types = Object(allegedTypes);
612
+ if (policy !== undefined) {
613
+ assertPackagePolicy(policy, keypath, url);
614
+ }
615
+ if (sourceDirname !== undefined) {
616
+ assertString(sourceDirname, `${keypath}.sourceDirname`, url);
617
+ }
618
+ if (retained !== undefined) {
619
+ assertBoolean(retained, `${keypath}.retained`, url);
620
+ }
621
+ };
622
+
623
+ /**
624
+ * Ensures a string is a file URL (a {@link FileUrlString})
625
+ *
626
+ * @param {unknown} allegedFileUrlString - a package location to assert
627
+ * @param {string} keypath
628
+ * @param {string} url
629
+ * @returns {asserts allegedFileUrlString is FileUrlString}
630
+ */
631
+ const assertFileUrlString = (allegedFileUrlString, keypath, url) => {
632
+ assertString(allegedFileUrlString, keypath, url);
633
+ assert(
634
+ allegedFileUrlString.startsWith('file://'),
635
+ `${keypath} must be a file URL in ${q(url)}; got ${q(allegedFileUrlString)}`,
636
+ );
284
637
  assert(
285
- allegedTypes === types && !Array.isArray(types),
286
- `${path}.types must be an object, got ${allegedTypes} in ${q(url)}`,
638
+ allegedFileUrlString.length > 7,
639
+ `${keypath} must contain a non-empty path in ${q(url)}; got ${q(allegedFileUrlString)}`,
287
640
  );
641
+ };
288
642
 
289
- for (const [key, value] of Object.entries(types)) {
290
- assert.typeof(
643
+ /**
644
+ * @param {unknown} allegedModules
645
+ * @param {string} keypath
646
+ * @param {string} url
647
+ * @returns {asserts allegedModules is Record<string, CompartmentModuleConfiguration>}
648
+ */
649
+ const assertPackageModuleConfigurations = (allegedModules, keypath, url) => {
650
+ assertPlainObject(allegedModules, keypath, url);
651
+ for (const [key, value] of entries(allegedModules)) {
652
+ assertString(
291
653
  key,
292
- 'string',
293
- `all keys of ${path}.types must be strings, got ${key} in ${q(url)}`,
294
- );
295
- assert.typeof(
296
- value,
297
- 'string',
298
- `${path}.types[${q(key)}] must be a string, got ${value} in ${q(url)}`,
654
+ `all keys of ${keypath}.modules must be strings; got ${key} in ${q(url)}`,
299
655
  );
656
+ assertModuleConfiguration(value, `${keypath}.modules[${q(key)}]`, url, [
657
+ 'compartment',
658
+ ]);
300
659
  }
301
660
  };
302
661
 
303
662
  /**
304
- * @param {unknown} allegedPolicy
305
- * @param {string} path
306
- * @param {string} [url]
663
+ *
664
+ * @param {unknown} allegedLocation
665
+ * @param {string} keypath
666
+ * @param {string} url
667
+ * @returns {asserts allegedLocation is PackageCompartmentDescriptor['location']}
307
668
  */
308
-
309
- const assertPolicy = (
310
- allegedPolicy,
311
- path,
312
- url = '<unknown-compartment-map.json>',
313
- ) => {
314
- assertPackagePolicy(allegedPolicy, `${path}.policy`, url);
669
+ const assertPackageLocation = (allegedLocation, keypath, url) => {
670
+ if (allegedLocation === ATTENUATORS_COMPARTMENT) {
671
+ return;
672
+ }
673
+ assertFileUrlString(allegedLocation, keypath, url);
315
674
  };
316
675
 
317
676
  /**
318
677
  * @param {unknown} allegedCompartment
319
- * @param {string} path
678
+ * @param {string} keypath
320
679
  * @param {string} url
680
+ * @returns {asserts allegedCompartment is PackageCompartmentDescriptor}
321
681
  */
322
- const assertCompartment = (allegedCompartment, path, url) => {
323
- const compartment = Object(allegedCompartment);
324
- assert(
325
- allegedCompartment === compartment && !Array.isArray(compartment),
326
- `${path} must be an object, got ${allegedCompartment} in ${q(url)}`,
682
+ const assertPackageCompartmentDescriptor = (
683
+ allegedCompartment,
684
+ keypath,
685
+ url,
686
+ ) => {
687
+ assertCompartmentDescriptor(
688
+ allegedCompartment,
689
+ keypath,
690
+ url,
691
+ assertPackageModuleConfigurations,
327
692
  );
328
693
 
329
694
  const {
330
695
  location,
331
- name,
332
- label,
333
- parsers,
334
- types,
335
696
  scopes,
336
- modules,
337
- policy,
697
+ label,
698
+ // these unused vars already validated by assertPackageModuleConfigurations
699
+ name: _name,
700
+ sourceDirname: _sourceDirname,
701
+ modules: _modules,
702
+ parsers: _parsers,
703
+ types: _types,
704
+ policy: _policy,
705
+ version: _version,
338
706
  ...extra
339
- } = compartment;
707
+ } = /** @type {PackageCompartmentDescriptor} */ (allegedCompartment);
340
708
 
341
709
  assertEmptyObject(
342
710
  extra,
343
- `${path} must not have extra properties, got ${q(
344
- Object.keys(extra),
711
+ `${keypath} must not have extra properties; got ${q(
712
+ keys(extra),
345
713
  )} in ${q(url)}`,
346
714
  );
347
715
 
348
- assert.typeof(
349
- location,
350
- 'string',
351
- `${path}.location in ${q(url)} must be string, got ${q(location)}`,
716
+ assertPackageLocation(location, `${keypath}.location`, url);
717
+ assertLabel(label, `${keypath}.label`, url);
718
+ assertScopes(scopes, `${keypath}.scopes`, url, assertFileUrlString);
719
+ };
720
+
721
+ /**
722
+ *
723
+ * @param {unknown} allegedCompartment
724
+ * @param {string} keypath
725
+ * @param {string} url
726
+ * @returns {asserts allegedCompartment is DigestedCompartmentDescriptor}
727
+ */
728
+ const assertDigestedCompartmentDescriptor = (
729
+ allegedCompartment,
730
+ keypath,
731
+ url,
732
+ ) => {
733
+ assertCompartmentDescriptor(
734
+ allegedCompartment,
735
+ keypath,
736
+ url,
737
+ assertDigestedModuleConfigurations,
352
738
  );
353
- assert.typeof(
354
- name,
355
- 'string',
356
- `${path}.name in ${q(url)} must be string, got ${q(name)}`,
739
+
740
+ const {
741
+ name: _name,
742
+ label: _label,
743
+ modules: _modules,
744
+ policy: _policy,
745
+ location: _location,
746
+ ...extra
747
+ } = allegedCompartment;
748
+
749
+ assertEmptyObject(
750
+ extra,
751
+ `${keypath} must not have extra properties; got ${q(
752
+ keys(extra),
753
+ )} in ${q(url)}`,
357
754
  );
358
- assert.typeof(
755
+ };
756
+
757
+ /**
758
+ * @param {unknown} allegedCompartment
759
+ * @param {string} keypath
760
+ * @param {string} url
761
+ * @returns {asserts allegedCompartment is FileCompartmentDescriptor}
762
+ */
763
+ const assertFileCompartmentDescriptor = (allegedCompartment, keypath, url) => {
764
+ assertCompartmentDescriptor(
765
+ allegedCompartment,
766
+ keypath,
767
+ url,
768
+ assertFileModuleConfigurations,
769
+ );
770
+
771
+ const {
772
+ location: _location,
773
+ name: _name,
359
774
  label,
360
- 'string',
361
- `${path}.label in ${q(url)} must be string, got ${q(label)}`,
775
+ modules: _modules,
776
+ policy: _policy,
777
+ ...extra
778
+ } = /** @type {FileCompartmentDescriptor} */ (allegedCompartment);
779
+
780
+ assertEmptyObject(
781
+ extra,
782
+ `${keypath} must not have extra properties; got ${q(
783
+ keys(extra),
784
+ )} in ${q(url)}`,
362
785
  );
363
786
 
364
- assertModules(modules, path, url);
365
- assertParsers(parsers, path, url);
366
- assertScopes(scopes, path, url);
367
- assertTypes(types, path, url);
368
- assertPolicy(policy, path, url);
787
+ assertString(label, `${keypath}.label`, url);
369
788
  };
370
789
 
371
790
  /**
372
791
  * @param {unknown} allegedCompartments
373
792
  * @param {string} url
793
+ * @returns {asserts allegedCompartments is Record<string, unknown>}
374
794
  */
375
- const assertCompartments = (allegedCompartments, url) => {
376
- const compartments = Object(allegedCompartments);
795
+ const assertCompartmentDescriptors = (allegedCompartments, url) => {
796
+ assertPlainObject(allegedCompartments, 'compartments', url);
797
+ const compartmentNames = keys(allegedCompartments);
377
798
  assert(
378
- allegedCompartments === compartments || !Array.isArray(compartments),
379
- `compartments must be an object, got ${q(allegedCompartments)} in ${q(
380
- url,
381
- )}`,
799
+ compartmentNames.length > 0,
800
+ `compartments must not be empty in ${q(url)}`,
382
801
  );
383
- for (const [key, value] of Object.entries(compartments)) {
384
- assertCompartment(value, `compartments[${q(key)}]`, url);
802
+ for (const key of keys(allegedCompartments)) {
803
+ assertString(
804
+ key,
805
+ `all keys of compartments must be strings; got ${key} in ${q(url)}`,
806
+ );
807
+ }
808
+ assert(
809
+ compartmentNames.every(name => typeof name === 'string'),
810
+ `all keys of compartments must be strings; got ${q(compartmentNames)} in ${q(url)}`,
811
+ );
812
+ };
813
+
814
+ /**
815
+ * @param {unknown} allegedCompartments
816
+ * @param {string} url
817
+ * @returns {asserts allegedCompartments is Record<string, FileCompartmentDescriptor>}
818
+ */
819
+ const assertFileCompartmentDescriptors = (allegedCompartments, url) => {
820
+ assertCompartmentDescriptors(allegedCompartments, url);
821
+ for (const [key, value] of entries(allegedCompartments)) {
822
+ assertFileCompartmentDescriptor(value, `compartments[${q(key)}]`, url);
385
823
  }
386
824
  };
387
825
 
826
+ /**
827
+ * @param {unknown} allegedCompartments
828
+ * @param {string} url
829
+ * @returns {asserts allegedCompartments is Record<string, PackageCompartmentDescriptor>}
830
+ */
831
+ const assertPackageCompartmentDescriptors = (allegedCompartments, url) => {
832
+ assertCompartmentDescriptors(allegedCompartments, url);
833
+ for (const [key, value] of entries(allegedCompartments)) {
834
+ assertPackageCompartmentDescriptor(value, `compartments[${q(key)}]`, url);
835
+ }
836
+ };
388
837
  /**
389
838
  * @param {unknown} allegedEntry
390
839
  * @param {string} url
840
+ * @returns {asserts allegedEntry is EntryDescriptor}
391
841
  */
392
842
  const assertEntry = (allegedEntry, url) => {
393
- const entry = Object(allegedEntry);
394
- assert(
395
- allegedEntry === entry && !Array.isArray(entry),
396
- `"entry" must be an object in compartment map, got ${allegedEntry} in ${q(
397
- url,
398
- )}`,
399
- );
400
- const { compartment, module, ...extra } = entry;
843
+ assertPlainObject(allegedEntry, 'entry', url);
844
+ const { compartment, module, ...extra } = allegedEntry;
401
845
  assertEmptyObject(
402
846
  extra,
403
- `"entry" must not have extra properties in compartment map, got ${q(
404
- Object.keys(extra),
847
+ `"entry" must not have extra properties in compartment map; got ${q(
848
+ keys(extra),
405
849
  )} in ${q(url)}`,
406
850
  );
407
- assert.typeof(
408
- compartment,
409
- 'string',
410
- `entry.compartment must be a string in compartment map, got ${compartment} in ${q(
411
- url,
412
- )}`,
413
- );
414
- assert.typeof(
415
- module,
416
- 'string',
417
- `entry.module must be a string in compartment map, got ${module} in ${q(
418
- url,
419
- )}`,
420
- );
851
+ assertString(compartment, 'entry.compartment', url);
852
+ assertString(module, 'entry.module', url);
421
853
  };
422
854
 
423
855
  /**
424
856
  * @param {unknown} allegedCompartmentMap
425
- * @param {string} [url]
857
+ * @param {string} url
426
858
  * @returns {asserts allegedCompartmentMap is CompartmentMapDescriptor}
427
859
  */
428
-
429
- export const assertCompartmentMap = (
430
- allegedCompartmentMap,
431
- url = '<unknown-compartment-map.json>',
432
- ) => {
433
- const compartmentMap = Object(allegedCompartmentMap);
434
- assert(
435
- allegedCompartmentMap === compartmentMap && !Array.isArray(compartmentMap),
436
- `Compartment map must be an object, got ${allegedCompartmentMap} in ${q(
437
- url,
438
- )}`,
439
- );
860
+ const assertCompartmentMap = (allegedCompartmentMap, url) => {
861
+ assertPlainObject(allegedCompartmentMap, 'compartment map', url);
440
862
  const {
441
863
  // TODO migrate tags to conditions
442
864
  // https://github.com/endojs/endo/issues/2388
443
865
  tags: conditions,
444
866
  entry,
445
- compartments,
867
+ compartments: _compartments,
446
868
  ...extra
447
- } = Object(compartmentMap);
869
+ } = allegedCompartmentMap;
448
870
  assertEmptyObject(
449
871
  extra,
450
- `Compartment map must not have extra properties, got ${q(
451
- Object.keys(extra),
872
+ `Compartment map must not have extra properties; got ${q(
873
+ keys(extra),
452
874
  )} in ${q(url)}`,
453
875
  );
454
876
  assertConditions(conditions, url);
455
877
  assertEntry(entry, url);
456
- assertCompartments(compartments, url);
878
+ assertTruthy(
879
+ allegedCompartmentMap.compartments?.[entry.compartment],
880
+ `compartments must contain entry compartment "${entry.compartment}" in ${q(url)}`,
881
+ );
882
+ };
883
+
884
+ /**
885
+ * @param {unknown} allegedCompartmentMap
886
+ * @param {string} [url]
887
+ * @returns {asserts allegedCompartmentMap is FileCompartmentMapDescriptor}
888
+ */
889
+ export const assertFileCompartmentMap = (
890
+ allegedCompartmentMap,
891
+ url = '<unknown-compartment-map.json>',
892
+ ) => {
893
+ assertCompartmentMap(allegedCompartmentMap, url);
894
+ const { compartments } = allegedCompartmentMap;
895
+ assertFileCompartmentDescriptors(compartments, url);
896
+ };
897
+
898
+ /**
899
+ *
900
+ * @param {unknown} allegedCompartments
901
+ * @param {string} url
902
+ * @returns {asserts allegedCompartments is Record<string, DigestedCompartmentDescriptor>}
903
+ */
904
+ export const assertDigestedCompartmentDescriptors = (
905
+ allegedCompartments,
906
+ url = '<unknown-compartment-map.json>',
907
+ ) => {
908
+ assertCompartmentDescriptors(allegedCompartments, url);
909
+ for (const [key, value] of entries(allegedCompartments)) {
910
+ assertDigestedCompartmentDescriptor(value, `compartments[${q(key)}]`, url);
911
+ }
912
+ };
913
+
914
+ /**
915
+ *
916
+ * @param {unknown} allegedCompartmentMap
917
+ * @param {string} [url]
918
+ * @returns {asserts allegedCompartmentMap is DigestedCompartmentMapDescriptor}
919
+ */
920
+ export const assertDigestedCompartmentMap = (
921
+ allegedCompartmentMap,
922
+ url = '<unknown-compartment-map.json>',
923
+ ) => {
924
+ assertCompartmentMap(allegedCompartmentMap, url);
925
+ const { compartments } = allegedCompartmentMap;
926
+ assertDigestedCompartmentDescriptors(compartments, url);
927
+ };
928
+
929
+ /**
930
+ * @param {unknown} allegedCompartmentMap
931
+ * @param {string} [url]
932
+ * @returns {asserts allegedCompartmentMap is PackageCompartmentMapDescriptor}
933
+ */
934
+ export const assertPackageCompartmentMap = (
935
+ allegedCompartmentMap,
936
+ url = '<unknown-compartment-map.json>',
937
+ ) => {
938
+ assertCompartmentMap(allegedCompartmentMap, url);
939
+ const { compartments } = allegedCompartmentMap;
940
+ assertPackageCompartmentDescriptors(compartments, url);
457
941
  };