Skip to content

Commit 8b378b4

Browse files
committed
fix(compartment-mapper): policy, parser type fixes
- Fixes `CompartmentDescriptor` so that it is generic on the `PackagePolicy`; externally-defined `ParseFn`s can now refer to the specific contents of a custom `PackagePolicy` present in a `CompartmentDescriptor`. - Introduces `ParseSourceMapHook`; differentiated from `@endo/module-source`'s `SourceMapHook`. - Fixes type of `PolicyItem`; eliminates confusion between `void` (no extra union members) and `any` (`SomePackagePolicy`).
1 parent ce5b1cc commit 8b378b4

7 files changed

Lines changed: 162 additions & 54 deletions

File tree

.changeset/huge-mammals-give.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@endo/compartment-mapper': patch
3+
---
4+
5+
Fixes `CompartmentDescriptor` so that it is generic on the `PackagePolicy`; externally-defined `ParseFn`s can now refer to the specific contents of a custom `PackagePolicy` present in a `CompartmentDescriptor`.
6+
7+
Introduces `ParseSourceMapHook`; differentiated from `@endo/module-source`'s `SourceMapHook`.
8+
9+
Fixes type of `PolicyItem`; eliminates confusion between `void` (no extra union members) and `any` (`SomePackagePolicy`).

packages/compartment-mapper/src/parse-archive-mjs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export const parseArchiveMjs = (
2525
const record = new ModuleSource(source, {
2626
sourceMap,
2727
sourceMapUrl: sourceUrl,
28+
// @ts-expect-error: fixed in https://github.com/endojs/endo/pull/3218
2829
sourceMapHook,
2930
});
3031
const pre = textEncoder.encode(JSON.stringify(record));

packages/compartment-mapper/src/parse-mjs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const parseMjs = (
2020
sourceUrl: archiveOnly ? undefined : sourceUrl,
2121
sourceMap,
2222
sourceMapUrl: sourceUrl,
23+
// @ts-expect-error: fixed in https://github.com/endojs/endo/pull/3218
2324
sourceMapHook,
2425
});
2526
return {

packages/compartment-mapper/src/types/compartment-map-schema.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import type {
1414
} from '../policy-format.js';
1515
import type { CanonicalName } from './canonical-name.js';
1616
import type { FileUrlString } from './external.js';
17-
import type { SomePackagePolicy } from './policy-schema.js';
17+
import type { PackagePolicy, SomePackagePolicy } from './policy-schema.js';
1818
import type { PatternDescriptor } from './pattern-replacement.js';
1919
import type { LiteralUnion } from './typescript.js';
2020

@@ -120,23 +120,24 @@ export interface PackageCompartmentDescriptor extends CompartmentDescriptor<Comp
120120
* one for a given library or application `package.json`.
121121
*/
122122
export interface CompartmentDescriptor<
123-
T extends ModuleConfiguration = ModuleConfiguration,
124-
U extends string = string,
123+
TModuleConfiguration extends ModuleConfiguration = ModuleConfiguration,
124+
TCompartmentName extends string = string,
125+
TPackagePolicy extends SomePackagePolicy = SomePackagePolicy,
125126
> {
126-
label: CanonicalName<U>;
127+
label: CanonicalName<TCompartmentName>;
127128
/**
128129
* the name of the originating package suitable for constructing a sourceURL
129130
* prefix that will match it to files in a developer workspace.
130131
*/
131132
name: string;
132-
modules: Record<string, T>;
133+
modules: Record<string, TModuleConfiguration>;
133134
scopes?: Record<string, ScopeDescriptor>;
134135
/** language for extension */
135136
parsers?: LanguageForExtension;
136137
/** language for module specifier */
137138
types?: LanguageForModuleSpecifier;
138139
/** policy specific to compartment */
139-
policy?: SomePackagePolicy;
140+
policy?: TPackagePolicy;
140141

141142
location: string;
142143
/**
@@ -152,9 +153,32 @@ export interface CompartmentDescriptor<
152153
retained?: true;
153154
}
154155

156+
/**
157+
* Any {@link CompartmentDescriptor}
158+
*/
159+
export type SomeCompartmentDescriptor = CompartmentDescriptor<any, any, any>;
160+
161+
/**
162+
* Any {@link CompartmentDescriptor} with a non-nullish
163+
* {@link CompartmentDescriptor.policy} property
164+
*/
165+
export type SomeCompartmentDescriptorWithPolicy =
166+
CompartmentDescriptorWithPolicy<any, any, any>;
167+
168+
/**
169+
* A {@link CompartmentDescriptor} with a non-nullish
170+
* {@link CompartmentDescriptor.policy} property
171+
*/
155172
export type CompartmentDescriptorWithPolicy<
156-
T extends ModuleConfiguration = ModuleConfiguration,
157-
> = Omit<CompartmentDescriptor<T>, 'policy'> & { policy: SomePackagePolicy };
173+
TModuleConfiguration extends ModuleConfiguration = ModuleConfiguration,
174+
TCompartmentName extends string = string,
175+
TPackagePolicy extends SomePackagePolicy = SomePackagePolicy,
176+
> = Omit<
177+
CompartmentDescriptor<TModuleConfiguration, TCompartmentName, TPackagePolicy>,
178+
'policy'
179+
> & {
180+
policy: TPackagePolicy;
181+
};
158182

159183
/**
160184
* A compartment descriptor digested by `digestCompartmentMap()`

packages/compartment-mapper/src/types/external.ts

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import type {
1818
} from '../policy-format.js';
1919
import type { CanonicalName } from './canonical-name.js';
2020
import type {
21+
SomeCompartmentDescriptor,
2122
CompartmentDescriptor,
2223
CompartmentMapDescriptor,
2324
DigestedCompartmentMapDescriptor,
@@ -684,6 +685,15 @@ export type SourceMapHookDetails = {
684685
sha512: string;
685686
};
686687

688+
/**
689+
* Source map hook as received by {@link ParseFn}.
690+
*
691+
* The import hook wraps the public {@link SourceMapHook} into this shape; it
692+
* receives the raw source map object from the code generator, not a JSON
693+
* string.
694+
*/
695+
export type ParseSourceMapHook = (sourceMapObject: object) => void;
696+
687697
export type ModuleTransforms = Record<string, ModuleTransform>;
688698

689699
export type SyncModuleTransforms = Record<string, SyncModuleTransform>;
@@ -759,29 +769,51 @@ interface BaseParserImplementation {
759769
heuristicImports: boolean;
760770
}
761771

762-
export interface ParserImplementation extends BaseParserImplementation {
763-
parse: ParseFn;
772+
export interface ParserImplementation<
773+
TCompartmentDescriptor extends
774+
SomeCompartmentDescriptor = SomeCompartmentDescriptor,
775+
> extends BaseParserImplementation {
776+
parse: ParseFn<TCompartmentDescriptor>;
764777
synchronous: true;
765778
}
766779

767-
export interface AsyncParserImplementation extends BaseParserImplementation {
768-
parse: AsyncParseFn;
780+
export interface AsyncParserImplementation<
781+
TCompartmentDescriptor extends
782+
SomeCompartmentDescriptor = SomeCompartmentDescriptor,
783+
> extends BaseParserImplementation {
784+
parse: AsyncParseFn<TCompartmentDescriptor>;
769785
synchronous: false;
770786
}
771787

772-
type ParseArguments = [
788+
/**
789+
* Options bag for a {@link ParseFn} or {@link AsyncParseFn}.
790+
*
791+
* @template TCompartmentDescriptor The compartment descriptor to use for the parse
792+
*/
793+
export type ParseOptions<
794+
TCompartmentDescriptor extends
795+
SomeCompartmentDescriptor = SomeCompartmentDescriptor,
796+
> = Partial<{
797+
sourceMap: string | undefined;
798+
sourceMapHook: ParseSourceMapHook | undefined;
799+
sourceMapUrl: string | undefined;
800+
readPowers: ReadFn | ReadPowers | undefined;
801+
compartmentDescriptor: TCompartmentDescriptor | undefined;
802+
}> &
803+
ArchiveOnlyOption;
804+
805+
/**
806+
* Arguments for a {@link ParseFn} or {@link AsyncParseFn}.
807+
*/
808+
export type ParseArguments<
809+
TCompartmentDescriptor extends
810+
SomeCompartmentDescriptor = SomeCompartmentDescriptor,
811+
> = [
773812
bytes: Uint8Array,
774813
specifier: string,
775814
moduleLocation: string,
776815
packageLocation: string,
777-
options?: Partial<{
778-
sourceMap: string | undefined;
779-
sourceMapHook: SourceMapHook | undefined;
780-
sourceMapUrl: string | undefined;
781-
readPowers: ReadFn | ReadPowers | undefined;
782-
compartmentDescriptor: CompartmentDescriptor | undefined;
783-
}> &
784-
ArchiveOnlyOption,
816+
options?: ParseOptions<TCompartmentDescriptor>,
785817
];
786818

787819
/**
@@ -804,17 +836,17 @@ export type ParseResult = {
804836
* Because {@link ParseResult} contains {@link FinalStaticModuleType} from
805837
* `ses`, those types would want to be moved out of `ses` with it.
806838
*/
807-
export type ParseFn = { isSyncParser?: true } & ((
808-
...args: ParseArguments
809-
) => ParseResult);
839+
export interface ParseFn<
840+
TCompartmentDescriptor extends
841+
SomeCompartmentDescriptor = SomeCompartmentDescriptor,
842+
> {
843+
isSyncParser?: true;
844+
(...args: ParseArguments<TCompartmentDescriptor>): ParseResult;
845+
}
810846

811847
/**
812848
* An asynchronous module parsing function.
813849
*/
814-
export type AsyncParseFn = { isSyncParser?: false } & ((
815-
...args: ParseArguments
816-
) => Promise<ParseResult>);
817-
818850
/**
819851
* Mapping of `Language` to synchronous {@link ParserImplementation}s only.
820852
*
@@ -824,6 +856,13 @@ export type SyncParserForLanguage = Record<
824856
Language | string,
825857
ParserImplementation
826858
>;
859+
export interface AsyncParseFn<
860+
TCompartmentDescriptor extends
861+
SomeCompartmentDescriptor = SomeCompartmentDescriptor,
862+
> {
863+
isSyncParser?: false;
864+
(...args: ParseArguments<TCompartmentDescriptor>): Promise<ParseResult>;
865+
}
827866

828867
/**
829868
* Mapping of `Language` to {@link ParserImplementation

packages/compartment-mapper/src/types/policy-schema.ts

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import type { WILDCARD_POLICY_VALUE } from '../policy-format.js';
9+
import type { IsAny } from './typescript.js';
910

1011
/* eslint-disable no-use-before-define */
1112

@@ -31,9 +32,16 @@ export type ImplicitAttenuationDefinition = [any, ...any[]];
3132
export type AttenuationDefinition =
3233
| FullAttenuationDefinition
3334
| ImplicitAttenuationDefinition;
35+
36+
/**
37+
* Information about the attenuator implementation
38+
*/
3439
export type UnifiedAttenuationDefinition = {
40+
/** Name of the attenuator (for error messages) */
3541
displayName: string;
42+
/** The module specifier of the implementation */
3643
specifier: string | null;
44+
/** Parameters to pass to the attenuator at invocation */
3745
params?: any[] | undefined;
3846
};
3947

@@ -52,10 +60,24 @@ export type PropertyPolicy = Record<string, boolean>;
5260
* A type representing a policy item, which can be a {@link WildcardPolicy
5361
* wildcard policy}, a property policy, `undefined`, or defined by an
5462
* attenuator
63+
*
64+
* @remarks
65+
* The void-vs-custom `T` branch was originally `[T] extends [void] ? … : …`, but
66+
* the type `any` also makes that test succeed, so `PolicyItem<any>` used to
67+
* reduce to the same as `void` and
68+
* `PackagePolicy<any, any, any, any> = AnyPackagePolicy` was not a supertype of
69+
* policies with extra string literals (for example, LavaMoat's {@code "root"} on
70+
* package imports). A separate branch for a wide
71+
* `any` type parameter yields
72+
* `PolicyItem<any> = WildcardPolicy | PropertyPolicy | any` so
73+
* `AnyPackagePolicy` correctly accepts all package policy item shapes.
5574
*/
56-
export type PolicyItem<T = void> = [T] extends [void]
57-
? WildcardPolicy | PropertyPolicy
58-
: WildcardPolicy | PropertyPolicy | T;
75+
export type PolicyItem<T = void> =
76+
IsAny<T> extends true
77+
? WildcardPolicy | PropertyPolicy | T
78+
: [T] extends [void]
79+
? WildcardPolicy | PropertyPolicy
80+
: WildcardPolicy | PropertyPolicy | T;
5981

6082
/**
6183
* An object representing a nested attenuation definition.
@@ -69,10 +91,10 @@ export type NestedAttenuationDefinition = Record<
6991
* An object representing a base package policy.
7092
*/
7193
export type PackagePolicy<
72-
PackagePolicyItem = void,
73-
GlobalsPolicyItem = void,
74-
BuiltinsPolicyItem = void,
75-
ExtraOptions = unknown,
94+
PackagePolicyExtra = void,
95+
GlobalsPolicyExtra = void,
96+
BuiltinsPolicyExtra = void,
97+
Options = unknown,
7698
> = {
7799
/**
78100
* The default attenuator, if any.
@@ -81,17 +103,17 @@ export type PackagePolicy<
81103
/**
82104
* The policy item for packages.
83105
*/
84-
packages?: PolicyItem<PackagePolicyItem> | undefined;
106+
packages?: PolicyItem<PackagePolicyExtra> | undefined;
85107
/**
86108
* The policy item or full attenuation definition for globals.
87109
*/
88-
globals?: AttenuationDefinition | PolicyItem<GlobalsPolicyItem> | undefined;
110+
globals?: AttenuationDefinition | PolicyItem<GlobalsPolicyExtra> | undefined;
89111
/**
90112
* The policy item or nested attenuation definition for builtins.
91113
*/
92114
builtins?:
93115
| NestedAttenuationDefinition
94-
| PolicyItem<BuiltinsPolicyItem>
116+
| PolicyItem<BuiltinsPolicyExtra>
95117
| undefined;
96118
/**
97119
* Whether to disable global freeze.
@@ -104,43 +126,47 @@ export type PackagePolicy<
104126
/**
105127
* Any additional user-defined options can be added to the policy here
106128
*/
107-
options?: ExtraOptions | undefined;
129+
options?: Options | undefined;
108130
};
109131

110132
/**
111133
* An object representing a base policy.
112134
*/
113135
export type Policy<
114-
PackagePolicyItem = void,
115-
GlobalsPolicyItem = void,
116-
BuiltinsPolicyItem = void,
117-
ExtraOptions = unknown,
136+
PackagePolicyExtra = void,
137+
GlobalsPolicyExtra = void,
138+
BuiltinsPolicyExtra = void,
139+
Options = unknown,
118140
> = {
119141
/** The package policies for the resources. */
120142
resources: Record<
121143
string,
122144
PackagePolicy<
123-
PackagePolicyItem,
124-
GlobalsPolicyItem,
125-
BuiltinsPolicyItem,
126-
ExtraOptions
145+
PackagePolicyExtra,
146+
GlobalsPolicyExtra,
147+
BuiltinsPolicyExtra,
148+
Options
127149
>
128150
>;
129151
/** The default attenuator. */
130152
defaultAttenuator?: string | undefined;
131153
/** The package policy for the entry. */
132154
entry?:
133155
| PackagePolicy<
134-
PackagePolicyItem,
135-
GlobalsPolicyItem,
136-
BuiltinsPolicyItem,
137-
ExtraOptions
156+
PackagePolicyExtra,
157+
GlobalsPolicyExtra,
158+
BuiltinsPolicyExtra,
159+
Options
138160
>
139161
| undefined;
140162
};
141163

142-
/** Any {@link Policy} */
164+
/**
165+
* Any {@link Policy}
166+
*/
143167
export type SomePolicy = Policy<any, any, any, any>;
144168

145-
/** Any {@link PackagePolicy} */
146-
export type SomePackagePolicy = PackagePolicy<void, void, void, unknown>;
169+
/**
170+
* Any {@link PackagePolicy}
171+
*/
172+
export type SomePackagePolicy = PackagePolicy<any, any, any, any>;

packages/compartment-mapper/src/types/typescript.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,11 @@ export type UnionToIntersection<U> = (
7777
* Makes a nicer tooltip for `T` in IDEs (most of the time).
7878
*/
7979
export type Simplify<T> = { [K in keyof T]: T[K] } & {};
80+
81+
/**
82+
* `true` when the type parameter is a wide `any`
83+
*
84+
* `0 extends 1 & T` is true for `T = any` and false for `void` and specific literals
85+
* @see {@link https://github.com/microsoft/TypeScript/issues/30029}
86+
*/
87+
export type IsAny<T> = 0 extends 1 & T ? true : false;

0 commit comments

Comments
 (0)