@mearl/preset 3.0.0-beta.5 → 3.0.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -2
- package/dist/browserControls.d.ts +29 -0
- package/dist/browserControls.js +54 -0
- package/dist/browserPreset.js +8 -3
- package/dist/browserViews.d.ts +1 -1
- package/dist/browserViews.js +5 -5
- package/dist/bundle.d.ts +4 -1
- package/dist/bundle.js +24 -5
- package/dist/bundleStore.js +80 -75
- package/dist/bundles.d.ts +7 -1
- package/dist/bundles.js +4 -1
- package/dist/{nodeSnapshot.d.ts → capabilitySnapshot.d.ts} +11 -11
- package/dist/{nodeSnapshot.js → capabilitySnapshot.js} +36 -35
- package/dist/extensionContributions.d.ts +29 -0
- package/dist/extensionContributions.js +57 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -1
- package/dist/manifest.js +5 -5
- package/dist/networkRoutes.d.ts +12 -0
- package/dist/networkRoutes.js +32 -0
- package/dist/prepare.d.ts +6 -0
- package/dist/prepare.js +81 -16
- package/dist/preset.js +3 -1
- package/dist/requestUi.d.ts +3 -1
- package/dist/requestUi.js +2 -2
- package/dist/resolve.js +4 -0
- package/dist/toolboxCatalogs.d.ts +8 -0
- package/dist/toolboxCatalogs.js +34 -0
- package/dist/types.d.ts +2 -1
- package/installation-site.json +1 -1
- package/package.json +7 -3
package/dist/bundles.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { BundleError, MAX_BUNDLE_BYTES, MAX_BUNDLE_MANIFEST_BYTES, MAX_BROWSER_STORAGE_BYTES, MAX_PAGE_RUNTIME_BYTES, MAX_REQUEST_UI_BYTES, bundleDigest, bundleSourceUrl, prepareBundle, readBundleJson, readBundleText, validateBundleManifest, verifyBundle, } from './bundle.js';
|
|
2
2
|
export type { BundleManifest, PreparedBundle, BundleLoader, BundleLoadOptions, PrepareBundleOptions, CapabilityRuntimeDeclaration } from './bundle.js';
|
|
3
|
+
export { selectExtensionContributions, validateExtensionContributions } from './extensionContributions.js';
|
|
4
|
+
export type { ExtensionContributions } from './extensionContributions.js';
|
|
3
5
|
export { createBundleFetchLoader } from './bundleFetch.js';
|
|
4
6
|
export { BrowserBundleStore } from './bundleStore.js';
|
|
5
7
|
export type { BrowserPresetInstallation } from './bundleStore.js';
|
|
@@ -12,7 +14,7 @@ export { installPageRuntimeHost, buildPageRuntimeRegistrationSource, buildPageRu
|
|
|
12
14
|
export type { PageRuntimeBinding, PageRuntimeConfig } from './pageRuntime.js';
|
|
13
15
|
export { receiveBrowserPreset, readPresetTransferResource, PRESET_TRANSFER_CHUNK_BYTES, MAX_PRESET_TRANSFER_HEADER_BYTES } from './browserTransfer.js';
|
|
14
16
|
export type { BrowserPresetTransferHeader, PresetTransferRequest, PresetTransferTransport } from './browserTransfer.js';
|
|
15
|
-
export { prepareBrowserBundleSnapshot } from './
|
|
17
|
+
export { prepareBrowserBundleSnapshot } from './capabilitySnapshot.js';
|
|
16
18
|
export { buildBrowserCapabilityPage, buildBrowserPureCapabilityPage } from './browserSandbox.js';
|
|
17
19
|
export type { BrowserCapabilityHost, BrowserCapabilityInvocation, BrowserCapabilityRequest, BrowserCapabilityResponse } from './browserSandbox.js';
|
|
18
20
|
export { validateBrowserRequestUi, selectBrowserRequestUi, browserUiText, browserRequestUiLink, browserRequestLoginError, validateBrowserRequestDetail } from './requestUi.js';
|
|
@@ -20,3 +22,7 @@ export type { BrowserUiText, BrowserRequestUi, BrowserRequestDetail, SelectedBro
|
|
|
20
22
|
export { validateBrowserView, selectBrowserView, buildBrowserViewPage } from './browserViews.js';
|
|
21
23
|
export type { BrowserViewSlot, BrowserViewDefinition, BrowserViewAction, BrowserViewTransport, SelectedBrowserView, BrowserViewBootstrap } from './browserViews.js';
|
|
22
24
|
export { browserTransportUrl, browserTransportPolicies } from './browserTransport.js';
|
|
25
|
+
export { validateBrowserControl, selectBrowserControl } from './browserControls.js';
|
|
26
|
+
export type { BrowserSegmentedControl, SelectedBrowserControl } from './browserControls.js';
|
|
27
|
+
export { parseNetworkRoutePlan, encodeNetworkRoutePlan, decodeNetworkRoutePlan } from './networkRoutes.js';
|
|
28
|
+
export type { NetworkHostRoute, NetworkRoutePlan } from './networkRoutes.js';
|
package/dist/bundles.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
export { BundleError, MAX_BUNDLE_BYTES, MAX_BUNDLE_MANIFEST_BYTES, MAX_BROWSER_STORAGE_BYTES, MAX_PAGE_RUNTIME_BYTES, MAX_REQUEST_UI_BYTES, bundleDigest, bundleSourceUrl, prepareBundle, readBundleJson, readBundleText, validateBundleManifest, verifyBundle, } from './bundle.js';
|
|
2
|
+
export { selectExtensionContributions, validateExtensionContributions } from './extensionContributions.js';
|
|
2
3
|
export { createBundleFetchLoader } from './bundleFetch.js';
|
|
3
4
|
export { BrowserBundleStore } from './bundleStore.js';
|
|
4
5
|
export { prepareBrowserPreset, presetRequiresBrowser, verifyBrowserPreset, verifyPreparedBrowserPreset } from './browserPreset.js';
|
|
5
6
|
export { preparePreset, verifyPreset, verifyPreparedPreset } from './preset.js';
|
|
6
7
|
export { installPageRuntimeHost, buildPageRuntimeRegistrationSource, buildPageRuntimeCallSource } from './pageRuntime.js';
|
|
7
8
|
export { receiveBrowserPreset, readPresetTransferResource, PRESET_TRANSFER_CHUNK_BYTES, MAX_PRESET_TRANSFER_HEADER_BYTES } from './browserTransfer.js';
|
|
8
|
-
export { prepareBrowserBundleSnapshot } from './
|
|
9
|
+
export { prepareBrowserBundleSnapshot } from './capabilitySnapshot.js';
|
|
9
10
|
export { buildBrowserCapabilityPage, buildBrowserPureCapabilityPage } from './browserSandbox.js';
|
|
10
11
|
export { validateBrowserRequestUi, selectBrowserRequestUi, browserUiText, browserRequestUiLink, browserRequestLoginError, validateBrowserRequestDetail } from './requestUi.js';
|
|
11
12
|
export { validateBrowserView, selectBrowserView, buildBrowserViewPage } from './browserViews.js';
|
|
12
13
|
export { browserTransportUrl, browserTransportPolicies } from './browserTransport.js';
|
|
14
|
+
export { validateBrowserControl, selectBrowserControl } from './browserControls.js';
|
|
15
|
+
export { parseNetworkRoutePlan, encodeNetworkRoutePlan, decodeNetworkRoutePlan } from './networkRoutes.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { JsonObject } from './types.js';
|
|
2
|
-
export interface
|
|
2
|
+
export interface CapabilitySelection {
|
|
3
3
|
schemaVersion: 1;
|
|
4
4
|
modules: Record<string, {
|
|
5
5
|
sha256: string;
|
|
@@ -11,28 +11,28 @@ export interface NodeCapabilitySelection {
|
|
|
11
11
|
handler: string;
|
|
12
12
|
}>;
|
|
13
13
|
}
|
|
14
|
-
export interface
|
|
14
|
+
export interface CapabilitySnapshot extends CapabilitySelection {
|
|
15
15
|
revision: string;
|
|
16
16
|
}
|
|
17
|
-
export interface
|
|
17
|
+
export interface CapabilityActivation extends CapabilitySnapshot {
|
|
18
18
|
installationRevision?: string;
|
|
19
19
|
}
|
|
20
|
-
export declare const
|
|
20
|
+
export declare const MAX_CAPABILITY_SELECTION_BYTES = 262144;
|
|
21
21
|
/** Explicit module configuration survives periods where a preset disables its module. */
|
|
22
|
-
export declare function
|
|
22
|
+
export declare function validateCapabilityConfig(value: Record<string, JsonObject>): Record<string, JsonObject>;
|
|
23
23
|
/** Preset defaults are inherited; explicit host configuration stays a separate override. */
|
|
24
|
-
export declare function
|
|
24
|
+
export declare function resolveCapabilityConfig(defaults: JsonObject, overrides?: Record<string, JsonObject>): Record<string, JsonObject>;
|
|
25
25
|
/** Exact content and bindings are selected together; filesystem and code loading belong to hosts. */
|
|
26
|
-
export declare function
|
|
27
|
-
export declare function
|
|
26
|
+
export declare function prepareCapabilitySnapshot(input: CapabilitySelection): Promise<CapabilitySnapshot>;
|
|
27
|
+
export declare function verifyCapabilitySnapshot(input: CapabilitySnapshot): Promise<CapabilitySnapshot>;
|
|
28
28
|
/** The installer receipt and Node selection share one atomic filesystem pointer. */
|
|
29
|
-
export declare function
|
|
29
|
+
export declare function verifyCapabilityActivation(input: CapabilityActivation): Promise<CapabilityActivation>;
|
|
30
30
|
export declare function prepareNodeBundleSnapshot(bundles: import('./bundle.js').PreparedBundle[], host: string, mearlApiVersion: string, config?: Record<string, JsonObject>): Promise<{
|
|
31
|
-
snapshot:
|
|
31
|
+
snapshot: CapabilitySnapshot;
|
|
32
32
|
resources: Record<string, Uint8Array>;
|
|
33
33
|
}>;
|
|
34
34
|
/** Browser factories use the same exact module/configuration selection without a Node loader. */
|
|
35
35
|
export declare function prepareBrowserBundleSnapshot(bundles: import('./bundle.js').PreparedBundle[], host: string, mearlApiVersion: string, config?: Record<string, JsonObject>): Promise<{
|
|
36
|
-
snapshot:
|
|
36
|
+
snapshot: CapabilitySnapshot;
|
|
37
37
|
resources: Record<string, Uint8Array>;
|
|
38
38
|
}>;
|
|
@@ -1,95 +1,95 @@
|
|
|
1
1
|
import { BundleError, MAX_BROWSER_STORAGE_BYTES, MAX_BUNDLE_BYTES, bundleDigest, validateBundleManifest } from './bundle.js';
|
|
2
2
|
import { clonePresetDefaults, isJsonObject } from './manifest.js';
|
|
3
3
|
import { mergePresetDefaults } from './resolve.js';
|
|
4
|
-
export const
|
|
4
|
+
export const MAX_CAPABILITY_SELECTION_BYTES = 262_144;
|
|
5
5
|
const identifier = /^[a-z][a-z0-9-]{0,79}$/;
|
|
6
6
|
const hookName = /^[a-z][a-zA-Z0-9_.-]{0,127}$/;
|
|
7
7
|
const digest = /^[a-f0-9]{64}$/;
|
|
8
8
|
function invalid(message) { throw new BundleError('BUNDLE_INVALID', message); }
|
|
9
9
|
function fields(value, allowed) {
|
|
10
10
|
if (!isJsonObject(value) || Object.keys(value).some(key => !allowed.includes(key)))
|
|
11
|
-
invalid('Invalid
|
|
11
|
+
invalid('Invalid Capability selection structure');
|
|
12
12
|
}
|
|
13
13
|
/** Explicit module configuration survives periods where a preset disables its module. */
|
|
14
|
-
export function
|
|
14
|
+
export function validateCapabilityConfig(value) {
|
|
15
15
|
const configs = clonePresetDefaults(value);
|
|
16
|
-
if (Object.keys(configs).length > 64 || new TextEncoder().encode(JSON.stringify(configs)).byteLength >
|
|
17
|
-
throw new BundleError('BUNDLE_QUOTA', '
|
|
16
|
+
if (Object.keys(configs).length > 64 || new TextEncoder().encode(JSON.stringify(configs)).byteLength > MAX_CAPABILITY_SELECTION_BYTES)
|
|
17
|
+
throw new BundleError('BUNDLE_QUOTA', 'Capability module configuration exceeds its limit');
|
|
18
18
|
for (const [id, config] of Object.entries(configs))
|
|
19
19
|
if (!identifier.test(id) || !isJsonObject(config))
|
|
20
|
-
invalid('
|
|
20
|
+
invalid('Capability module configuration must map IDs to JSON objects');
|
|
21
21
|
return configs;
|
|
22
22
|
}
|
|
23
23
|
/** Preset defaults are inherited; explicit host configuration stays a separate override. */
|
|
24
|
-
export function
|
|
24
|
+
export function resolveCapabilityConfig(defaults, overrides = {}) {
|
|
25
25
|
const value = defaults.capabilities === undefined ? {} : defaults.capabilities;
|
|
26
26
|
if (!isJsonObject(value))
|
|
27
27
|
invalid('Capability defaults must map module IDs to configuration objects');
|
|
28
|
-
return
|
|
28
|
+
return validateCapabilityConfig(mergePresetDefaults(validateCapabilityConfig(value), validateCapabilityConfig(overrides)));
|
|
29
29
|
}
|
|
30
30
|
/** Exact content and bindings are selected together; filesystem and code loading belong to hosts. */
|
|
31
|
-
export async function
|
|
31
|
+
export async function prepareCapabilitySnapshot(input) {
|
|
32
32
|
const value = clonePresetDefaults(input);
|
|
33
33
|
fields(value, ['schemaVersion', 'modules', 'bindings']);
|
|
34
34
|
if (value.schemaVersion !== 1 || !isJsonObject(value.modules) || !isJsonObject(value.bindings))
|
|
35
|
-
invalid('Invalid
|
|
35
|
+
invalid('Invalid Capability selection');
|
|
36
36
|
if (Object.keys(value.modules).length > 64 || Object.keys(value.bindings).length > 256)
|
|
37
|
-
throw new BundleError('BUNDLE_QUOTA', '
|
|
37
|
+
throw new BundleError('BUNDLE_QUOTA', 'Capability selection exceeds the entry limit');
|
|
38
38
|
const modules = {};
|
|
39
39
|
let total = 0;
|
|
40
40
|
for (const id of Object.keys(value.modules).sort()) {
|
|
41
41
|
if (!identifier.test(id))
|
|
42
|
-
invalid('Invalid
|
|
42
|
+
invalid('Invalid Capability module ID');
|
|
43
43
|
const entry = value.modules[id];
|
|
44
44
|
fields(entry, ['sha256', 'byteLength', 'config']);
|
|
45
45
|
if (typeof entry.sha256 !== 'string' || !digest.test(entry.sha256) || typeof entry.byteLength !== 'number' || !Number.isSafeInteger(entry.byteLength) || entry.byteLength <= 0 || entry.byteLength > MAX_BUNDLE_BYTES)
|
|
46
|
-
invalid('Invalid
|
|
46
|
+
invalid('Invalid Capability module resource');
|
|
47
47
|
if (entry.config !== undefined && !isJsonObject(entry.config))
|
|
48
|
-
invalid('
|
|
48
|
+
invalid('Capability module configuration must be a JSON object');
|
|
49
49
|
total += entry.byteLength;
|
|
50
50
|
if (total > MAX_BROWSER_STORAGE_BYTES)
|
|
51
|
-
throw new BundleError('BUNDLE_QUOTA', '
|
|
51
|
+
throw new BundleError('BUNDLE_QUOTA', 'Capability resources exceed 64 MiB');
|
|
52
52
|
modules[id] = { sha256: entry.sha256, byteLength: entry.byteLength, ...(entry.config === undefined ? {} : { config: clonePresetDefaults(entry.config) }) };
|
|
53
53
|
}
|
|
54
54
|
const bindings = {};
|
|
55
55
|
for (const name of Object.keys(value.bindings).sort()) {
|
|
56
56
|
if (!hookName.test(name))
|
|
57
|
-
invalid('Invalid
|
|
57
|
+
invalid('Invalid Capability hook name');
|
|
58
58
|
const binding = value.bindings[name];
|
|
59
59
|
fields(binding, ['module', 'handler']);
|
|
60
60
|
if (typeof binding.module !== 'string' || !Object.hasOwn(modules, binding.module) || typeof binding.handler !== 'string' || !hookName.test(binding.handler))
|
|
61
|
-
invalid('
|
|
61
|
+
invalid('Capability hook must bind a declared module handler');
|
|
62
62
|
bindings[name] = { module: binding.module, handler: binding.handler };
|
|
63
63
|
}
|
|
64
64
|
const selection = { schemaVersion: 1, modules, bindings };
|
|
65
|
-
if (new TextEncoder().encode(JSON.stringify(selection)).byteLength + 80 >
|
|
66
|
-
throw new BundleError('BUNDLE_QUOTA', '
|
|
65
|
+
if (new TextEncoder().encode(JSON.stringify(selection)).byteLength + 80 > MAX_CAPABILITY_SELECTION_BYTES)
|
|
66
|
+
throw new BundleError('BUNDLE_QUOTA', 'Capability selection metadata exceeds 256 KiB');
|
|
67
67
|
return { ...selection, revision: await bundleDigest(new TextEncoder().encode(JSON.stringify(selection))) };
|
|
68
68
|
}
|
|
69
|
-
export async function
|
|
69
|
+
export async function verifyCapabilitySnapshot(input) {
|
|
70
70
|
const value = clonePresetDefaults(input);
|
|
71
71
|
fields(value, ['schemaVersion', 'modules', 'bindings', 'revision']);
|
|
72
72
|
if (typeof value.revision !== 'string' || !digest.test(value.revision))
|
|
73
|
-
invalid('Invalid
|
|
73
|
+
invalid('Invalid Capability revision');
|
|
74
74
|
const { revision, ...selection } = value;
|
|
75
|
-
const prepared = await
|
|
75
|
+
const prepared = await prepareCapabilitySnapshot(selection);
|
|
76
76
|
if (prepared.revision !== revision)
|
|
77
|
-
throw new BundleError('BUNDLE_INTEGRITY', '
|
|
77
|
+
throw new BundleError('BUNDLE_INTEGRITY', 'Capability selection revision does not match');
|
|
78
78
|
return prepared;
|
|
79
79
|
}
|
|
80
80
|
/** The installer receipt and Node selection share one atomic filesystem pointer. */
|
|
81
|
-
export async function
|
|
81
|
+
export async function verifyCapabilityActivation(input) {
|
|
82
82
|
const value = clonePresetDefaults(input);
|
|
83
83
|
const { installationRevision, ...selection } = value;
|
|
84
84
|
if (installationRevision !== undefined && (typeof installationRevision !== 'string' || !digest.test(installationRevision)))
|
|
85
85
|
invalid('Invalid capability installation revision');
|
|
86
|
-
const snapshot = await
|
|
86
|
+
const snapshot = await verifyCapabilitySnapshot(selection);
|
|
87
87
|
return { ...snapshot, ...(installationRevision === undefined ? {} : { installationRevision: installationRevision }) };
|
|
88
88
|
}
|
|
89
89
|
async function prepareRuntimeBundleSnapshot(bundles, kind, host, mearlApiVersion, config) {
|
|
90
90
|
if (!identifier.test(host))
|
|
91
|
-
invalid('Invalid
|
|
92
|
-
const configs =
|
|
91
|
+
invalid('Invalid capability runtime host');
|
|
92
|
+
const configs = validateCapabilityConfig(config);
|
|
93
93
|
const modules = {};
|
|
94
94
|
const bindings = {};
|
|
95
95
|
const resources = {};
|
|
@@ -101,20 +101,21 @@ async function prepareRuntimeBundleSnapshot(bundles, kind, host, mearlApiVersion
|
|
|
101
101
|
if (!runtime)
|
|
102
102
|
continue;
|
|
103
103
|
if (Object.hasOwn(modules, manifest.id))
|
|
104
|
-
invalid('Duplicate
|
|
104
|
+
invalid('Duplicate Capability module ID');
|
|
105
105
|
const metadata = manifest.resources[runtime.entry];
|
|
106
106
|
if (!Object.hasOwn(bundle.resources, runtime.entry))
|
|
107
|
-
invalid('
|
|
107
|
+
invalid('Capability module resource is missing');
|
|
108
108
|
total += metadata.byteLength;
|
|
109
109
|
if (pending.length >= 64 || total > MAX_BROWSER_STORAGE_BYTES)
|
|
110
|
-
throw new BundleError('BUNDLE_QUOTA', '
|
|
110
|
+
throw new BundleError('BUNDLE_QUOTA', 'Capability resources exceed the selection limit');
|
|
111
111
|
pending.push({ id: manifest.id, bytes: bundle.resources[runtime.entry], sha256: metadata.sha256 });
|
|
112
112
|
if (pending[pending.length - 1].bytes.byteLength !== metadata.byteLength)
|
|
113
|
-
invalid('
|
|
114
|
-
|
|
113
|
+
invalid('Capability module length does not match');
|
|
114
|
+
const effectiveConfig = mergePresetDefaults(manifest.defaults ?? {}, configs[manifest.id] ?? {});
|
|
115
|
+
modules[manifest.id] = { ...metadata, ...(Object.keys(effectiveConfig).length ? { config: effectiveConfig } : {}) };
|
|
115
116
|
for (const [name, handler] of Object.entries(runtime.bindings)) {
|
|
116
117
|
if (Object.hasOwn(bindings, name))
|
|
117
|
-
invalid('
|
|
118
|
+
invalid('Capability hook has multiple providers');
|
|
118
119
|
bindings[name] = { module: manifest.id, handler };
|
|
119
120
|
}
|
|
120
121
|
}
|
|
@@ -122,10 +123,10 @@ async function prepareRuntimeBundleSnapshot(bundles, kind, host, mearlApiVersion
|
|
|
122
123
|
const captured = pending.map(resource => ({ ...resource, bytes: Uint8Array.from(resource.bytes) }));
|
|
123
124
|
for (const resource of captured) {
|
|
124
125
|
if (await bundleDigest(resource.bytes) !== resource.sha256)
|
|
125
|
-
throw new BundleError('BUNDLE_INTEGRITY', `
|
|
126
|
+
throw new BundleError('BUNDLE_INTEGRITY', `Capability module digest does not match: ${resource.id}`);
|
|
126
127
|
resources[resource.sha256] = resource.bytes;
|
|
127
128
|
}
|
|
128
|
-
return { snapshot: await
|
|
129
|
+
return { snapshot: await prepareCapabilitySnapshot({ schemaVersion: 1, modules, bindings }), resources };
|
|
129
130
|
}
|
|
130
131
|
export function prepareNodeBundleSnapshot(bundles, host, mearlApiVersion, config = {}) {
|
|
131
132
|
return prepareRuntimeBundleSnapshot(bundles, 'nodeRuntimes', host, mearlApiVersion, config);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type PreparedBundle } from './bundle.js';
|
|
2
|
+
import { type BrowserUiText } from './requestUi.js';
|
|
3
|
+
export interface ExtensionContributions {
|
|
4
|
+
schemaVersion: 1;
|
|
5
|
+
operations?: Record<string, {
|
|
6
|
+
hook: string;
|
|
7
|
+
setting?: string;
|
|
8
|
+
}>;
|
|
9
|
+
logins?: Record<string, {
|
|
10
|
+
url: string;
|
|
11
|
+
title: BrowserUiText;
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
export declare function validateExtensionContributions(input: unknown): ExtensionContributions;
|
|
15
|
+
/** Definitions belong to packages; preset defaults cannot redirect their handlers. */
|
|
16
|
+
export declare function selectExtensionContributions(bundles: PreparedBundle[]): {
|
|
17
|
+
operations: Record<string, {
|
|
18
|
+
hook: string;
|
|
19
|
+
setting?: string;
|
|
20
|
+
}>;
|
|
21
|
+
logins: Record<string, {
|
|
22
|
+
url: string;
|
|
23
|
+
title: BrowserUiText;
|
|
24
|
+
}>;
|
|
25
|
+
owners: Record<string, {
|
|
26
|
+
bundleId: string;
|
|
27
|
+
revision: string;
|
|
28
|
+
}>;
|
|
29
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { BundleError, readBundleJson } from './bundle.js';
|
|
2
|
+
import { clonePresetDefaults, isJsonObject } from './manifest.js';
|
|
3
|
+
import { browserUiHttpUrl, validateBrowserUiText } from './requestUi.js';
|
|
4
|
+
export function validateExtensionContributions(input) {
|
|
5
|
+
const value = clonePresetDefaults(input);
|
|
6
|
+
if (value.schemaVersion !== 1 || Object.keys(value).some(key => !['schemaVersion', 'operations', 'logins'].includes(key)))
|
|
7
|
+
throw new BundleError('BUNDLE_INVALID', 'Invalid extension contributions');
|
|
8
|
+
for (const kind of ['operations', 'logins']) {
|
|
9
|
+
const entries = value[kind];
|
|
10
|
+
if (entries === undefined)
|
|
11
|
+
continue;
|
|
12
|
+
if (!isJsonObject(entries) || Object.keys(entries).length > 64)
|
|
13
|
+
throw new BundleError('BUNDLE_QUOTA', 'Extension contributions exceed 64 entries');
|
|
14
|
+
for (const [name, entry] of Object.entries(entries)) {
|
|
15
|
+
if (!/^[a-z][a-zA-Z0-9_.-]{0,95}$/.test(name) || !isJsonObject(entry))
|
|
16
|
+
throw new BundleError('BUNDLE_INVALID', 'Invalid extension contribution name');
|
|
17
|
+
if (kind === 'operations') {
|
|
18
|
+
if (Object.keys(entry).some(key => !['hook', 'setting'].includes(key)) || typeof entry.hook !== 'string' || !/^[a-z][a-zA-Z0-9_.-]{0,127}$/.test(entry.hook) || entry.setting !== undefined && (typeof entry.setting !== 'string' || !/^[a-z][a-zA-Z0-9]{0,95}Enabled$/.test(entry.setting) || ['agentEnabled', 'sseMonitoringEnabled', 'agentFloatingButtonEnabled'].includes(entry.setting)))
|
|
19
|
+
throw new BundleError('BUNDLE_INVALID', 'Invalid extension operation');
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
if (Object.keys(entry).some(key => !['url', 'title'].includes(key)))
|
|
23
|
+
throw new BundleError('BUNDLE_INVALID', 'Invalid extension login');
|
|
24
|
+
entry.url = browserUiHttpUrl(entry.url, { allowFragment: true });
|
|
25
|
+
validateBrowserUiText(entry.title);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
/** Definitions belong to packages; preset defaults cannot redirect their handlers. */
|
|
32
|
+
export function selectExtensionContributions(bundles) {
|
|
33
|
+
const operations = {};
|
|
34
|
+
const logins = {};
|
|
35
|
+
const owners = {};
|
|
36
|
+
for (const bundle of bundles) {
|
|
37
|
+
const path = bundle.manifest.contributions?.extension;
|
|
38
|
+
if (!path)
|
|
39
|
+
continue;
|
|
40
|
+
const definition = validateExtensionContributions(readBundleJson(bundle, path));
|
|
41
|
+
const bindings = bundle.manifest.contributions?.browserRuntimes?.extension?.bindings ?? {};
|
|
42
|
+
for (const [name, operation] of Object.entries(definition.operations ?? {})) {
|
|
43
|
+
if (Object.hasOwn(operations, name))
|
|
44
|
+
throw new BundleError('BUNDLE_CONFLICT', `Multiple providers for extension operation: ${name}`);
|
|
45
|
+
if (!Object.hasOwn(bindings, operation.hook))
|
|
46
|
+
throw new BundleError('BUNDLE_INVALID', 'Extension operation must use its own runtime');
|
|
47
|
+
operations[name] = operation;
|
|
48
|
+
owners[name] = { bundleId: bundle.manifest.id, revision: bundle.revision };
|
|
49
|
+
}
|
|
50
|
+
for (const [name, login] of Object.entries(definition.logins ?? {})) {
|
|
51
|
+
if (Object.hasOwn(logins, name))
|
|
52
|
+
throw new BundleError('BUNDLE_CONFLICT', `Multiple providers for extension login: ${name}`);
|
|
53
|
+
logins[name] = login;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { operations, logins, owners };
|
|
57
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { PresetError, validatePreset, clonePresetDefaults } from './manifest.js';
|
|
2
2
|
export { mergePresetDefaults, resolvePreset } from './resolve.js';
|
|
3
3
|
export type * from './types.js';
|
|
4
|
-
export { prepareNodeBundleSnapshot,
|
|
5
|
-
export type {
|
|
4
|
+
export { prepareNodeBundleSnapshot, prepareCapabilitySnapshot, verifyCapabilitySnapshot, verifyCapabilityActivation, validateCapabilityConfig, resolveCapabilityConfig, MAX_CAPABILITY_SELECTION_BYTES } from './capabilitySnapshot.js';
|
|
5
|
+
export type { CapabilitySelection, CapabilitySnapshot, CapabilityActivation } from './capabilitySnapshot.js';
|
|
6
|
+
export { parseToolboxCatalogs } from './toolboxCatalogs.js';
|
|
7
|
+
export type { ToolboxCatalogConfig, ToolboxCatalogConfigs } from './toolboxCatalogs.js';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { PresetError, validatePreset, clonePresetDefaults } from './manifest.js';
|
|
2
2
|
export { mergePresetDefaults, resolvePreset } from './resolve.js';
|
|
3
|
-
export { prepareNodeBundleSnapshot,
|
|
3
|
+
export { prepareNodeBundleSnapshot, prepareCapabilitySnapshot, verifyCapabilitySnapshot, verifyCapabilityActivation, validateCapabilityConfig, resolveCapabilityConfig, MAX_CAPABILITY_SELECTION_BYTES } from './capabilitySnapshot.js';
|
|
4
|
+
export { parseToolboxCatalogs } from './toolboxCatalogs.js';
|
package/dist/manifest.js
CHANGED
|
@@ -72,7 +72,7 @@ export function clonePresetDefaults(value) {
|
|
|
72
72
|
return json(value, 'defaults');
|
|
73
73
|
}
|
|
74
74
|
function component(value, path, kind) {
|
|
75
|
-
fields(value, path, ['source', 'version', 'enabled', '
|
|
75
|
+
fields(value, path, ['source', 'version', 'enabled', 'targets', 'dependsOn', 'migrateFrom']);
|
|
76
76
|
if ('source' in value)
|
|
77
77
|
text(value.source, `${path}.source`);
|
|
78
78
|
if ('version' in value)
|
|
@@ -86,10 +86,10 @@ function component(value, path, kind) {
|
|
|
86
86
|
}
|
|
87
87
|
if ('enabled' in value && typeof value.enabled !== 'boolean')
|
|
88
88
|
invalid(`${path}.enabled`, 'expected a boolean');
|
|
89
|
-
if ('
|
|
90
|
-
stringArray(value.
|
|
91
|
-
if (value.
|
|
92
|
-
invalid(`${path}.
|
|
89
|
+
if ('targets' in value) {
|
|
90
|
+
stringArray(value.targets, `${path}.targets`);
|
|
91
|
+
if (value.targets.length === 0 || value.targets.some(item => item !== 'browser' && item !== 'node')) {
|
|
92
|
+
invalid(`${path}.targets`, 'expected browser and/or node');
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
if ('dependsOn' in value) {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface NetworkHostRoute {
|
|
2
|
+
id?: string;
|
|
3
|
+
fromHost: string;
|
|
4
|
+
toHost: string;
|
|
5
|
+
}
|
|
6
|
+
export interface NetworkRoutePlan {
|
|
7
|
+
routes: NetworkHostRoute[];
|
|
8
|
+
}
|
|
9
|
+
/** Shared by Node, the extension host and the self-contained document-start runtime. */
|
|
10
|
+
export declare function parseNetworkRoutePlan(input: unknown): NetworkRoutePlan;
|
|
11
|
+
export declare function encodeNetworkRoutePlan(input: NetworkRoutePlan): string;
|
|
12
|
+
export declare function decodeNetworkRoutePlan(value: string): NetworkRoutePlan;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Shared by Node, the extension host and the self-contained document-start runtime. */
|
|
2
|
+
export function parseNetworkRoutePlan(input) {
|
|
3
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).join(',') !== 'routes')
|
|
4
|
+
throw new Error('Invalid network route plan');
|
|
5
|
+
const routes = input.routes;
|
|
6
|
+
if (!Array.isArray(routes) || routes.length > 32)
|
|
7
|
+
throw new Error('Invalid network routes');
|
|
8
|
+
const sources = new Set();
|
|
9
|
+
const ids = new Set();
|
|
10
|
+
const hostname = (value) => typeof value === 'string' && value.length <= 253 && value.includes('.') && value.split('.').every(label => /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(label));
|
|
11
|
+
for (const route of routes) {
|
|
12
|
+
if (!route || typeof route !== 'object' || Array.isArray(route) || Object.keys(route).some(key => !['id', 'fromHost', 'toHost'].includes(key)) || route.id !== undefined && (typeof route.id !== 'string' || !/^[a-z][a-zA-Z0-9_.-]{0,79}$/.test(route.id)) || !hostname(route.fromHost) || !hostname(route.toHost) || route.fromHost === route.toHost || sources.has(route.fromHost) || route.id !== undefined && ids.has(route.id))
|
|
13
|
+
throw new Error('Invalid or ambiguous network route');
|
|
14
|
+
sources.add(route.fromHost);
|
|
15
|
+
if (route.id)
|
|
16
|
+
ids.add(route.id);
|
|
17
|
+
}
|
|
18
|
+
if (routes.reduce((size, route) => size + route.toHost.length, 0) > 1024)
|
|
19
|
+
throw new Error('Network route plan is too large');
|
|
20
|
+
const plan = { routes: routes.map(({ id, fromHost, toHost }) => ({ ...(id ? { id } : {}), fromHost, toHost })) };
|
|
21
|
+
if (JSON.stringify(plan).length > 12288)
|
|
22
|
+
throw new Error('Network route header is too large');
|
|
23
|
+
return plan;
|
|
24
|
+
}
|
|
25
|
+
export function encodeNetworkRoutePlan(input) {
|
|
26
|
+
return btoa(JSON.stringify(parseNetworkRoutePlan(input)));
|
|
27
|
+
}
|
|
28
|
+
export function decodeNetworkRoutePlan(value) {
|
|
29
|
+
if (value.length > 16384)
|
|
30
|
+
throw new Error('Network route header is too large');
|
|
31
|
+
return parseNetworkRoutePlan(JSON.parse(atob(value)));
|
|
32
|
+
}
|
package/dist/prepare.d.ts
CHANGED
|
@@ -11,17 +11,23 @@ export interface PresetGraphSnapshot {
|
|
|
11
11
|
source: string;
|
|
12
12
|
manifests: Record<string, PresetManifest>;
|
|
13
13
|
bundleRevisions: Record<string, string>;
|
|
14
|
+
packageTargets: Record<string, PresetRuntime[]>;
|
|
14
15
|
revision: string;
|
|
15
16
|
}
|
|
16
17
|
export declare function presetSnapshotRevision(value: object): Promise<string>;
|
|
17
18
|
/** Each execution location must be able to satisfy its own package dependencies. */
|
|
18
19
|
export declare function presetPackages(preset: ResolvedPreset, runtimes: PresetRuntime[]): Array<[string, ResolvedComponent]>;
|
|
20
|
+
export declare function bundleTargets(bundle: Pick<PreparedBundle, 'manifest'>): PresetRuntime[];
|
|
21
|
+
/** Resolved activation choices are derived data; source manifests remain unchanged. */
|
|
22
|
+
export declare function applyPackageTargets(preset: ResolvedPreset, targets: Record<string, PresetRuntime[]>): ResolvedPreset;
|
|
23
|
+
export declare function verifyPackageTargets(preset: ResolvedPreset, bundles: PreparedBundle[]): void;
|
|
19
24
|
/** Shared preparation performs no installation, code execution or permission changes. */
|
|
20
25
|
export declare function preparePresetPackages(source: string, options: PreparePresetOptions, runtimes: PresetRuntime[]): Promise<{
|
|
21
26
|
source: string;
|
|
22
27
|
manifests: Record<string, PresetManifest>;
|
|
23
28
|
preset: ResolvedPreset;
|
|
24
29
|
bundles: PreparedBundle[];
|
|
30
|
+
packageTargets: Record<string, PresetRuntime[]>;
|
|
25
31
|
}>;
|
|
26
32
|
/** The exact graph is checked offline; installed resources are verified by the target store. */
|
|
27
33
|
export declare function verifyPresetGraph(value: PresetGraphSnapshot & {
|
package/dist/prepare.js
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import { BundleError, MAX_BROWSER_STORAGE_BYTES, bundleDigest, bundleSourceUrl, prepareBundle } from './bundle.js';
|
|
1
|
+
import { BundleError, MAX_BROWSER_STORAGE_BYTES, MAX_BUNDLE_MANIFEST_BYTES, bundleDigest, bundleSourceUrl, prepareBundle, validateBundleManifest } from './bundle.js';
|
|
2
|
+
import { satisfies } from 'semver';
|
|
2
3
|
import { clonePresetDefaults, validatePreset } from './manifest.js';
|
|
3
4
|
import { resolvePreset } from './resolve.js';
|
|
4
5
|
export async function presetSnapshotRevision(value) {
|
|
5
6
|
return bundleDigest(new TextEncoder().encode(JSON.stringify(value)));
|
|
6
7
|
}
|
|
8
|
+
/** Select candidates without assuming that inferred locations have been discovered. */
|
|
9
|
+
function selectPresetPackages(preset, runtimes) {
|
|
10
|
+
const entries = Object.entries(preset.packages).filter(([, entry]) => entry.enabled !== false && (entry.targets ?? ['browser', 'node']).some(runtime => runtimes.includes(runtime)));
|
|
11
|
+
if (entries.length > 64)
|
|
12
|
+
throw new BundleError('BUNDLE_QUOTA', 'A preset can activate at most 64 packages');
|
|
13
|
+
return entries;
|
|
14
|
+
}
|
|
7
15
|
/** Each execution location must be able to satisfy its own package dependencies. */
|
|
8
16
|
export function presetPackages(preset, runtimes) {
|
|
9
|
-
if (runtimes.includes('node') && runtimes.includes('browser') && Object.values(preset.packages).some(entry => entry.enabled !== false && !entry.runtimes?.length)) {
|
|
10
|
-
throw new BundleError('BUNDLE_INCOMPATIBLE', 'Every enabled package must declare its browser and/or Node runtime');
|
|
11
|
-
}
|
|
12
17
|
for (const runtime of runtimes) {
|
|
13
|
-
const entries = Object.entries(preset.packages).filter(([, entry]) => entry.enabled !== false && entry.
|
|
18
|
+
const entries = Object.entries(preset.packages).filter(([, entry]) => entry.enabled !== false && (entry.targets ?? ['browser', 'node']).includes(runtime));
|
|
14
19
|
const ids = new Set(entries.map(([id]) => id));
|
|
15
20
|
for (const [id, entry] of entries) {
|
|
16
21
|
if (entry.dependsOn?.some(dependency => !ids.has(dependency))) {
|
|
@@ -25,10 +30,37 @@ export function presetPackages(preset, runtimes) {
|
|
|
25
30
|
}
|
|
26
31
|
}
|
|
27
32
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
return selectPresetPackages(preset, runtimes);
|
|
34
|
+
}
|
|
35
|
+
export function bundleTargets(bundle) {
|
|
36
|
+
const contributions = bundle.manifest.contributions;
|
|
37
|
+
const targets = [];
|
|
38
|
+
if (contributions && (Object.keys(contributions.browserRuntimes ?? {}).length || Object.keys(contributions.pageRuntimes ?? {}).length || contributions.appProfiles?.length || contributions.extension || contributions.requestUi || contributions.browserViews || contributions.browserControls))
|
|
39
|
+
targets.push('browser');
|
|
40
|
+
if (Object.keys(contributions?.nodeRuntimes ?? {}).length)
|
|
41
|
+
targets.push('node');
|
|
42
|
+
return targets.length ? targets : ['browser', 'node'];
|
|
43
|
+
}
|
|
44
|
+
/** Resolved activation choices are derived data; source manifests remain unchanged. */
|
|
45
|
+
export function applyPackageTargets(preset, targets) {
|
|
46
|
+
const enabled = Object.entries(preset.packages).filter(([, entry]) => entry.enabled !== false);
|
|
47
|
+
if (enabled.length !== Object.keys(targets).length)
|
|
48
|
+
throw new BundleError('BUNDLE_INTEGRITY', 'Package target selection does not match the preset');
|
|
49
|
+
const packages = { ...preset.packages };
|
|
50
|
+
for (const [id, entry] of enabled) {
|
|
51
|
+
const selected = targets[id];
|
|
52
|
+
if (!Array.isArray(selected) || !selected.length || selected.length > 2 || new Set(selected).size !== selected.length || selected.some(target => target !== 'browser' && target !== 'node') || entry.targets && JSON.stringify(entry.targets) !== JSON.stringify(selected))
|
|
53
|
+
throw new BundleError('BUNDLE_INTEGRITY', 'Invalid package target selection');
|
|
54
|
+
packages[id] = { ...entry, targets: [...selected] };
|
|
55
|
+
}
|
|
56
|
+
return { ...preset, packages };
|
|
57
|
+
}
|
|
58
|
+
export function verifyPackageTargets(preset, bundles) {
|
|
59
|
+
for (const bundle of bundles) {
|
|
60
|
+
const available = bundleTargets(bundle);
|
|
61
|
+
if (preset.packages[bundle.manifest.id]?.targets?.some(target => !available.includes(target)))
|
|
62
|
+
throw new BundleError('BUNDLE_INCOMPATIBLE', 'Package does not support the selected target');
|
|
63
|
+
}
|
|
32
64
|
}
|
|
33
65
|
/** Shared preparation performs no installation, code execution or permission changes. */
|
|
34
66
|
export async function preparePresetPackages(source, options, runtimes) {
|
|
@@ -47,7 +79,7 @@ export async function preparePresetPackages(source, options, runtimes) {
|
|
|
47
79
|
return bytes;
|
|
48
80
|
};
|
|
49
81
|
const manifests = {};
|
|
50
|
-
|
|
82
|
+
let preset = await resolvePreset(source, {
|
|
51
83
|
mearlApiVersion: options.mearlApiVersion,
|
|
52
84
|
signal: options.signal,
|
|
53
85
|
load: async (url) => {
|
|
@@ -63,18 +95,49 @@ export async function preparePresetPackages(source, options, runtimes) {
|
|
|
63
95
|
return manifest;
|
|
64
96
|
},
|
|
65
97
|
});
|
|
66
|
-
const
|
|
98
|
+
const targetsKnown = Object.values(preset.packages).every(entry => entry.enabled === false || Boolean(entry.targets?.length));
|
|
99
|
+
const entries = targetsKnown ? presetPackages(preset, runtimes) : selectPresetPackages(preset, runtimes);
|
|
67
100
|
const bundles = [];
|
|
101
|
+
const pending = [];
|
|
102
|
+
const packageTargets = Object.fromEntries(Object.entries(preset.packages).filter(([, entry]) => entry.enabled !== false).map(([id, entry]) => [id, entry.targets ?? []]));
|
|
68
103
|
for (const [id, entry] of entries) {
|
|
69
|
-
|
|
104
|
+
const manifestSource = bundleSourceUrl(entry.source);
|
|
105
|
+
const bytes = Uint8Array.from(await load(manifestSource, { maxBytes: MAX_BUNDLE_MANIFEST_BYTES, signal: options.signal }));
|
|
106
|
+
let manifest;
|
|
107
|
+
try {
|
|
108
|
+
manifest = validateBundleManifest(JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes)), options.mearlApiVersion);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (error instanceof BundleError)
|
|
112
|
+
throw error;
|
|
113
|
+
throw new BundleError('BUNDLE_INVALID', error instanceof Error ? error.message : 'Invalid package manifest');
|
|
114
|
+
}
|
|
115
|
+
if (manifest.id !== id)
|
|
116
|
+
throw new BundleError('BUNDLE_INVALID', 'Package identity does not match the preset');
|
|
117
|
+
if (entry.version && !satisfies(manifest.version, entry.version))
|
|
118
|
+
throw new BundleError('BUNDLE_INCOMPATIBLE', 'Package version does not satisfy the preset');
|
|
119
|
+
const available = bundleTargets({ manifest });
|
|
120
|
+
const targets = entry.targets ?? available;
|
|
121
|
+
if (targets.some(target => !available.includes(target)))
|
|
122
|
+
throw new BundleError('BUNDLE_INCOMPATIBLE', 'Package does not support the selected target');
|
|
123
|
+
packageTargets[id] = targets;
|
|
124
|
+
pending.push({ id, entry, source: manifestSource, bytes });
|
|
125
|
+
}
|
|
126
|
+
preset = applyPackageTargets(preset, packageTargets);
|
|
127
|
+
const selected = new Set(presetPackages(preset, runtimes).map(([id]) => id));
|
|
128
|
+
for (const candidate of pending) {
|
|
129
|
+
if (!selected.has(candidate.id))
|
|
130
|
+
continue;
|
|
131
|
+
bundles.push(await prepareBundle(candidate.source, { ...options, load: (url, request) => url === candidate.source ? Promise.resolve(candidate.bytes) : load(url, request), expectedId: candidate.id, version: candidate.entry.version }));
|
|
70
132
|
}
|
|
133
|
+
verifyPackageTargets(preset, bundles);
|
|
71
134
|
options.signal?.throwIfAborted();
|
|
72
|
-
return { source, manifests, preset, bundles };
|
|
135
|
+
return { source, manifests, preset, bundles: bundles.filter(bundle => selected.has(bundle.manifest.id)), packageTargets };
|
|
73
136
|
}
|
|
74
137
|
/** The exact graph is checked offline; installed resources are verified by the target store. */
|
|
75
138
|
export async function verifyPresetGraph(value, mearlApiVersion, runtimes, full) {
|
|
76
139
|
const json = clonePresetDefaults(value);
|
|
77
|
-
const allowed = ['source', 'manifests', 'bundleRevisions', 'revision', ...(full ? ['schemaVersion'] : [])];
|
|
140
|
+
const allowed = ['source', 'manifests', 'bundleRevisions', 'packageTargets', 'revision', ...(full ? ['schemaVersion'] : [])];
|
|
78
141
|
if (Object.keys(json).some(key => !allowed.includes(key)) || (full && json.schemaVersion !== 1) ||
|
|
79
142
|
typeof json.source !== 'string' || typeof json.revision !== 'string' || !/^[a-f0-9]{64}$/.test(json.revision)) {
|
|
80
143
|
throw new BundleError('BUNDLE_INTEGRITY', 'Invalid stored preset identity');
|
|
@@ -93,10 +156,11 @@ export async function verifyPresetGraph(value, mearlApiVersion, runtimes, full)
|
|
|
93
156
|
throw new BundleError('BUNDLE_INTEGRITY', 'Invalid stored preset source');
|
|
94
157
|
validated[url] = validatePreset(manifest);
|
|
95
158
|
}
|
|
96
|
-
const
|
|
159
|
+
const packageTargets = clonePresetDefaults(json.packageTargets);
|
|
160
|
+
const data = { ...(full ? { schemaVersion: 1 } : {}), source, manifests: validated, bundleRevisions, packageTargets };
|
|
97
161
|
if (await presetSnapshotRevision(data) !== json.revision)
|
|
98
162
|
throw new BundleError('BUNDLE_INTEGRITY', 'Stored preset revision does not match');
|
|
99
|
-
|
|
163
|
+
let preset = await resolvePreset(source, {
|
|
100
164
|
mearlApiVersion,
|
|
101
165
|
load: async (url) => {
|
|
102
166
|
if (!Object.hasOwn(validated, url))
|
|
@@ -104,6 +168,7 @@ export async function verifyPresetGraph(value, mearlApiVersion, runtimes, full)
|
|
|
104
168
|
return validated[url];
|
|
105
169
|
},
|
|
106
170
|
});
|
|
171
|
+
preset = applyPackageTargets(preset, packageTargets);
|
|
107
172
|
const entries = presetPackages(preset, runtimes);
|
|
108
173
|
if (preset.chain.length !== Object.keys(validated).length || entries.length !== Object.keys(bundleRevisions).length || entries.some(([id]) => !Object.hasOwn(bundleRevisions, id))) {
|
|
109
174
|
throw new BundleError('BUNDLE_INTEGRITY', 'Stored preset package graph does not match');
|