@mearl/app-profiles 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 +1 -1
- package/dist/environment/app-device.d.ts +24 -0
- package/dist/environment/app-device.js +61 -0
- package/dist/environment/changes.d.ts +28 -0
- package/dist/environment/changes.js +53 -0
- package/dist/environment/document.d.ts +11 -0
- package/dist/environment/document.js +79 -0
- package/dist/environment/identity.d.ts +6 -0
- package/dist/environment/identity.js +12 -0
- package/dist/environment/relay.d.ts +5 -0
- package/dist/environment/relay.js +84 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/profiles.js +6 -8
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ export function compileInstalledApp(definition: AppProfile) {
|
|
|
14
14
|
|
|
15
15
|
resolveAppProfileUpdate、rebuildAppProfileConfig 和 updateAppProfileSetting 保持显式选项、设备外观及方法覆盖的优先级。preset 默认值由宿主传入,用户已有选择继续优先;能力包安装成功不等于默认配置已经应用。
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
App 行为由能力包声明并安装到受控页面;页面代码和宿主处理器通过 [能力包模型](../../docs/capability-model.md) 关联。
|
|
18
18
|
|
|
19
19
|
## Agent 能力发现
|
|
20
20
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { EnvironmentChange, EnvironmentSnapshot } from './changes.js';
|
|
2
|
+
type Component = 'device' | 'app';
|
|
3
|
+
type Values = {
|
|
4
|
+
device: unknown;
|
|
5
|
+
app: unknown;
|
|
6
|
+
};
|
|
7
|
+
export type AppDeviceChanges<T extends Values> = {
|
|
8
|
+
[K in Component]?: EnvironmentChange<T[K]>;
|
|
9
|
+
};
|
|
10
|
+
export type AppDeviceSnapshots<T extends Values> = {
|
|
11
|
+
[K in Component]: EnvironmentSnapshot<T[K]>;
|
|
12
|
+
};
|
|
13
|
+
export interface AppDeviceEnvironmentHost<T extends Values> {
|
|
14
|
+
/** Validate the composed physical environment as well as reading each component's intent. */
|
|
15
|
+
read(): Promise<AppDeviceSnapshots<T>>;
|
|
16
|
+
save(changes: AppDeviceChanges<T>): Promise<void>;
|
|
17
|
+
matchesRestored<K extends Component>(component: K, baseline: T[K], current: T[K]): boolean;
|
|
18
|
+
}
|
|
19
|
+
/** App and device share physical fields. A coordinated change keeps their original baselines. */
|
|
20
|
+
export declare function changeAppDeviceEnvironment<T extends Values, R>(owner: string, components: readonly Component[], changes: AppDeviceChanges<T>, host: AppDeviceEnvironmentHost<T>, effect: () => Promise<R>, restore?: boolean): Promise<{
|
|
21
|
+
result?: R;
|
|
22
|
+
changes: AppDeviceChanges<T>;
|
|
23
|
+
}>;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** App and device share physical fields. A coordinated change keeps their original baselines. */
|
|
2
|
+
export async function changeAppDeviceEnvironment(owner, components, changes, host, effect, restore = false) {
|
|
3
|
+
if (!owner)
|
|
4
|
+
throw new Error('Environment owner is required');
|
|
5
|
+
if (!components.length || new Set(components).size !== components.length || components.some(key => key !== 'device' && key !== 'app'))
|
|
6
|
+
throw new Error('Invalid App/device change components');
|
|
7
|
+
const selected = new Set(components.filter(key => !restore || changes[key]));
|
|
8
|
+
if (!selected.size)
|
|
9
|
+
return { changes };
|
|
10
|
+
const before = await host.read();
|
|
11
|
+
const next = structuredClone({ device: changes.device, app: changes.app });
|
|
12
|
+
for (const key of ['device', 'app']) {
|
|
13
|
+
const change = changes[key];
|
|
14
|
+
if (!change)
|
|
15
|
+
continue;
|
|
16
|
+
if (change.owner !== owner || change.component !== key)
|
|
17
|
+
throw new Error('ENVIRONMENT_OWNER_CONFLICT');
|
|
18
|
+
if (change.phase !== 'active' || !change.applied)
|
|
19
|
+
throw new Error('ENVIRONMENT_UNCONFIRMED');
|
|
20
|
+
if (change.applied.revision !== before[key].revision || change.applied.fingerprint !== before[key].fingerprint) {
|
|
21
|
+
next[key] = { ...change, phase: 'conflict' };
|
|
22
|
+
await host.save(next);
|
|
23
|
+
throw new Error(`ENVIRONMENT_STATE_CONFLICT: ${key} changed after this owner wrote it`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const isChanging = (key) => selected.has(key);
|
|
27
|
+
const stage = (key) => {
|
|
28
|
+
const previous = changes[key];
|
|
29
|
+
if (previous || (!restore && isChanging(key)))
|
|
30
|
+
next[key] = {
|
|
31
|
+
owner, component: key,
|
|
32
|
+
baseline: previous?.baseline ?? structuredClone(before[key]),
|
|
33
|
+
...(previous?.applied ? { applied: previous.applied } : {}),
|
|
34
|
+
phase: restore && isChanging(key) ? 'restoring' : 'pending',
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
stage('device');
|
|
38
|
+
stage('app');
|
|
39
|
+
// One host save covers every owned component affected by the shared UA/container transition.
|
|
40
|
+
await host.save(next);
|
|
41
|
+
const result = await effect();
|
|
42
|
+
const after = await host.read();
|
|
43
|
+
if (restore)
|
|
44
|
+
for (const key of selected) {
|
|
45
|
+
if (!host.matchesRestored(key, next[key].baseline.value, after[key].value))
|
|
46
|
+
throw new Error(`ENVIRONMENT_RESTORE_UNCONFIRMED: ${key}`);
|
|
47
|
+
}
|
|
48
|
+
const commit = (key) => {
|
|
49
|
+
const change = next[key];
|
|
50
|
+
if (!change)
|
|
51
|
+
return;
|
|
52
|
+
if (restore && isChanging(key))
|
|
53
|
+
delete next[key];
|
|
54
|
+
else
|
|
55
|
+
next[key] = { ...change, phase: 'active', applied: structuredClone(after[key]) };
|
|
56
|
+
};
|
|
57
|
+
commit('device');
|
|
58
|
+
commit('app');
|
|
59
|
+
await host.save(next);
|
|
60
|
+
return { result, changes: next };
|
|
61
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type EnvironmentComponent = 'device' | 'app' | 'timezone';
|
|
2
|
+
/** Hosts supply a revision for coordinated writes and an observation for external drift. */
|
|
3
|
+
export interface EnvironmentSnapshot<T> {
|
|
4
|
+
revision: number;
|
|
5
|
+
fingerprint: string;
|
|
6
|
+
value: T;
|
|
7
|
+
}
|
|
8
|
+
export interface EnvironmentChange<T> {
|
|
9
|
+
owner: string;
|
|
10
|
+
component: EnvironmentComponent;
|
|
11
|
+
baseline: EnvironmentSnapshot<T>;
|
|
12
|
+
applied?: EnvironmentSnapshot<T>;
|
|
13
|
+
phase: 'pending' | 'active' | 'restoring' | 'conflict' | 'restored';
|
|
14
|
+
}
|
|
15
|
+
/** Callers serialize this flow with their existing page/environment update queue. */
|
|
16
|
+
export interface EnvironmentChangeHost<T> {
|
|
17
|
+
read(): Promise<EnvironmentSnapshot<T>>;
|
|
18
|
+
save(change: EnvironmentChange<T>): Promise<void>;
|
|
19
|
+
restore(baseline: T): Promise<void>;
|
|
20
|
+
matchesRestored(baseline: EnvironmentSnapshot<T>, current: EnvironmentSnapshot<T>): boolean;
|
|
21
|
+
}
|
|
22
|
+
/** Persist recovery information before the first effect; failed/unknown effects remain pending. */
|
|
23
|
+
export declare function applyEnvironmentChange<T, R>(owner: string, component: EnvironmentComponent, previous: EnvironmentChange<T> | undefined, host: EnvironmentChangeHost<T>, apply: () => Promise<R>): Promise<R>;
|
|
24
|
+
/** Restore only this component and only while both coordinated and observed state still match. */
|
|
25
|
+
export declare function restoreEnvironmentChange<T>(owner: string, change: EnvironmentChange<T>, host: EnvironmentChangeHost<T>): Promise<{
|
|
26
|
+
restored: boolean;
|
|
27
|
+
reason?: 'changed' | 'unconfirmed';
|
|
28
|
+
}>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
function matches(expected, current) {
|
|
2
|
+
return expected.revision === current.revision && expected.fingerprint === current.fingerprint;
|
|
3
|
+
}
|
|
4
|
+
async function assertCurrent(change, host) {
|
|
5
|
+
if (change.phase !== 'active' || !change.applied) {
|
|
6
|
+
throw new Error('ENVIRONMENT_UNCONFIRMED: reconcile the previous change before writing again');
|
|
7
|
+
}
|
|
8
|
+
if (!matches(change.applied, await host.read())) {
|
|
9
|
+
await host.save({ ...change, phase: 'conflict' });
|
|
10
|
+
throw new Error('ENVIRONMENT_STATE_CONFLICT: the environment changed after this owner wrote it');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** Persist recovery information before the first effect; failed/unknown effects remain pending. */
|
|
14
|
+
export async function applyEnvironmentChange(owner, component, previous, host, apply) {
|
|
15
|
+
if (!owner)
|
|
16
|
+
throw new Error('Environment owner is required');
|
|
17
|
+
if (previous) {
|
|
18
|
+
if (previous.owner !== owner || previous.component !== component)
|
|
19
|
+
throw new Error('ENVIRONMENT_OWNER_CONFLICT');
|
|
20
|
+
await assertCurrent(previous, host);
|
|
21
|
+
}
|
|
22
|
+
const change = {
|
|
23
|
+
owner,
|
|
24
|
+
component,
|
|
25
|
+
baseline: previous?.baseline ?? structuredClone(await host.read()),
|
|
26
|
+
phase: 'pending',
|
|
27
|
+
};
|
|
28
|
+
await host.save(change);
|
|
29
|
+
const result = await apply();
|
|
30
|
+
const applied = structuredClone(await host.read());
|
|
31
|
+
await host.save({ ...change, phase: 'active', applied });
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
/** Restore only this component and only while both coordinated and observed state still match. */
|
|
35
|
+
export async function restoreEnvironmentChange(owner, change, host) {
|
|
36
|
+
if (change.owner !== owner)
|
|
37
|
+
throw new Error('ENVIRONMENT_OWNER_CONFLICT');
|
|
38
|
+
if (change.phase === 'restored')
|
|
39
|
+
return { restored: true };
|
|
40
|
+
if (change.phase !== 'active' || !change.applied)
|
|
41
|
+
return { restored: false, reason: 'unconfirmed' };
|
|
42
|
+
if (!matches(change.applied, await host.read())) {
|
|
43
|
+
await host.save({ ...change, phase: 'conflict' });
|
|
44
|
+
return { restored: false, reason: 'changed' };
|
|
45
|
+
}
|
|
46
|
+
const restoring = { ...change, phase: 'restoring' };
|
|
47
|
+
await host.save(restoring);
|
|
48
|
+
await host.restore(structuredClone(change.baseline.value));
|
|
49
|
+
if (!host.matchesRestored(change.baseline, await host.read()))
|
|
50
|
+
return { restored: false, reason: 'unconfirmed' };
|
|
51
|
+
await host.save({ ...restoring, phase: 'restored' });
|
|
52
|
+
return { restored: true };
|
|
53
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { InjectedAppProfileConfig } from '@mearl/browser-core';
|
|
2
|
+
export interface AppProfileDocumentEnvironmentOptions {
|
|
3
|
+
/** Some hosts prepare globals before the separately installed page runtime. */
|
|
4
|
+
globals: 'always' | 'runtime';
|
|
5
|
+
safeAreaVariables?: boolean;
|
|
6
|
+
restoreHintName?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Self-contained MAIN-world bootstrap; hosts own script registration and removal. */
|
|
9
|
+
export declare function installAppProfileDocumentEnvironment(config: InjectedAppProfileConfig, options: AppProfileDocumentEnvironmentOptions): void;
|
|
10
|
+
/** Self-contained document-start scheduling, including App containers that need a body. */
|
|
11
|
+
export declare function installAppProfileWhenDocumentReady(install: () => void, waitForBody?: boolean): void;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** Self-contained MAIN-world bootstrap; hosts own script registration and removal. */
|
|
2
|
+
export function installAppProfileDocumentEnvironment(config, options) {
|
|
3
|
+
const browser = globalThis;
|
|
4
|
+
const target = browser;
|
|
5
|
+
if (options.restoreHintName) {
|
|
6
|
+
try {
|
|
7
|
+
Object.defineProperty(target, options.restoreHintName, {
|
|
8
|
+
configurable: true, enumerable: false, writable: false, value: true,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
target[options.restoreHintName] = true;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const includeGlobals = options.globals === 'always' || Boolean(config.pageRuntime);
|
|
16
|
+
for (const [key, value] of Object.entries(includeGlobals ? config.globals : {})) {
|
|
17
|
+
try {
|
|
18
|
+
target[key] = value;
|
|
19
|
+
}
|
|
20
|
+
catch { /* Frozen page globals keep their original values. */ }
|
|
21
|
+
}
|
|
22
|
+
const insets = config.safeAreaInsets ?? { top: config.statusBarHeight, right: 0, bottom: 0, left: 0 };
|
|
23
|
+
if (includeGlobals) {
|
|
24
|
+
target.__STATUS_BAR_HEIGHT__ = config.statusBarHeight;
|
|
25
|
+
target.__SAFE_AREA_INSET_BOTTOM__ = insets.bottom;
|
|
26
|
+
}
|
|
27
|
+
for (const [key, value] of [
|
|
28
|
+
['userAgent', config.userAgent],
|
|
29
|
+
['platform', config.navigatorPlatform],
|
|
30
|
+
...(includeGlobals ? [['vendor', 'Apple Computer, Inc.']] : []),
|
|
31
|
+
['appVersion', config.userAgent.replace(/^Mozilla\//, '')],
|
|
32
|
+
['maxTouchPoints', config.maxTouchPoints],
|
|
33
|
+
...(includeGlobals && config.policies?.navigatorLanguage === true ? [['language', config.language]] : []),
|
|
34
|
+
]) {
|
|
35
|
+
try {
|
|
36
|
+
Object.defineProperty(browser.navigator, key, { configurable: true, get: () => value });
|
|
37
|
+
}
|
|
38
|
+
catch { /* Non-configurable native properties keep their original values. */ }
|
|
39
|
+
}
|
|
40
|
+
if (includeGlobals && config.policies?.navigatorLanguage === true) {
|
|
41
|
+
try {
|
|
42
|
+
Object.defineProperty(browser.navigator, 'languages', { configurable: true, get: () => [config.language] });
|
|
43
|
+
}
|
|
44
|
+
catch { /* Non-configurable native properties keep their original values. */ }
|
|
45
|
+
}
|
|
46
|
+
if (options.safeAreaVariables) {
|
|
47
|
+
const apply = () => {
|
|
48
|
+
const root = browser.document.documentElement;
|
|
49
|
+
if (!root)
|
|
50
|
+
return false;
|
|
51
|
+
root.style.setProperty('--safe-area-inset-top', `${insets.top}px`);
|
|
52
|
+
root.style.setProperty('--safe-area-inset-right', `${insets.right}px`);
|
|
53
|
+
root.style.setProperty('--safe-area-inset-bottom', `${insets.bottom}px`);
|
|
54
|
+
root.style.setProperty('--safe-area-inset-left', `${insets.left}px`);
|
|
55
|
+
return true;
|
|
56
|
+
};
|
|
57
|
+
if (!apply()) {
|
|
58
|
+
const observer = new browser.MutationObserver(() => { if (apply())
|
|
59
|
+
observer.disconnect(); });
|
|
60
|
+
observer.observe(browser.document, { childList: true });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Self-contained document-start scheduling, including App containers that need a body. */
|
|
65
|
+
export function installAppProfileWhenDocumentReady(install, waitForBody = false) {
|
|
66
|
+
const browser = globalThis;
|
|
67
|
+
const ready = () => Boolean(browser.document.documentElement && (!waitForBody || browser.document.body));
|
|
68
|
+
if (ready()) {
|
|
69
|
+
install();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const observer = new browser.MutationObserver(() => {
|
|
73
|
+
if (!ready())
|
|
74
|
+
return;
|
|
75
|
+
observer.disconnect();
|
|
76
|
+
install();
|
|
77
|
+
});
|
|
78
|
+
observer.observe(browser.document, { childList: true, subtree: waitForBody });
|
|
79
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type DeviceEnvironmentObservation, type ResolvedEmulationConfig } from '@mearl/browser-core';
|
|
2
|
+
/** App UA wins over device UA. Unspecified Client Hints retain the captured browser values. */
|
|
3
|
+
export declare function resolveAppDeviceUserAgent(baseline: Pick<DeviceEnvironmentObservation, 'userAgent' | 'userAgentMetadata'>, device?: ResolvedEmulationConfig | null, appUserAgent?: string): {
|
|
4
|
+
userAgent: string;
|
|
5
|
+
userAgentMetadata: import("@mearl/browser-core").DeviceUserAgentMetadata | null;
|
|
6
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { emptyDeviceUserAgentMetadata } from '@mearl/browser-core';
|
|
2
|
+
/** App UA wins over device UA. Unspecified Client Hints retain the captured browser values. */
|
|
3
|
+
export function resolveAppDeviceUserAgent(baseline, device, appUserAgent) {
|
|
4
|
+
const metadata = baseline.userAgentMetadata;
|
|
5
|
+
return {
|
|
6
|
+
userAgent: appUserAgent ?? device?.userAgent ?? baseline.userAgent,
|
|
7
|
+
userAgentMetadata: appUserAgent ? emptyDeviceUserAgentMetadata() : device && metadata ? {
|
|
8
|
+
...structuredClone(metadata), mobile: device.mobile, platform: device.platform,
|
|
9
|
+
platformVersion: device.platformVersion, architecture: device.architecture, model: device.model,
|
|
10
|
+
} : structuredClone(metadata),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/** Self-contained MAIN-world transport; host services remain outside the page. */
|
|
2
|
+
export function installAppProfileMessageRelay(bindingName, options = {}) {
|
|
3
|
+
const browser = globalThis;
|
|
4
|
+
const target = globalThis;
|
|
5
|
+
const markerName = options.markerName ?? '__mearlCdpAppProfileRelayInstalled__';
|
|
6
|
+
if (target[markerName])
|
|
7
|
+
return;
|
|
8
|
+
target[markerName] = true;
|
|
9
|
+
browser.addEventListener('message', event => {
|
|
10
|
+
if (event.source !== globalThis)
|
|
11
|
+
return;
|
|
12
|
+
const message = event.data;
|
|
13
|
+
if (!message || message.source !== 'mearl-app-bridge')
|
|
14
|
+
return;
|
|
15
|
+
const requestId = message.requestId;
|
|
16
|
+
if (typeof requestId !== 'number')
|
|
17
|
+
return;
|
|
18
|
+
if (message.type === 'settings' && globalThis.top !== globalThis)
|
|
19
|
+
return;
|
|
20
|
+
if (message.type === 'prepareWebView') {
|
|
21
|
+
browser.postMessage({
|
|
22
|
+
source: 'mearl-content',
|
|
23
|
+
type: 'APP_BRIDGE_PREPARE_WEBVIEW_RESPONSE',
|
|
24
|
+
requestId,
|
|
25
|
+
result: { success: true },
|
|
26
|
+
}, '*');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (message.type !== 'protocol' &&
|
|
30
|
+
message.type !== 'protocolStream' &&
|
|
31
|
+
message.type !== 'userInfo' &&
|
|
32
|
+
message.type !== 'authCode' &&
|
|
33
|
+
message.type !== 'authCodeCancel' &&
|
|
34
|
+
message.type !== 'settings' &&
|
|
35
|
+
message.type !== 'screenshot') {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const replyFailure = (reason) => {
|
|
39
|
+
browser.console?.warn('[Mearl] App bridge relay unavailable:', reason);
|
|
40
|
+
if (message.type === 'protocolStream') {
|
|
41
|
+
browser.postMessage({
|
|
42
|
+
source: 'mearl-content',
|
|
43
|
+
type: 'APP_BRIDGE_PROTOCOL_STREAM_EVENT',
|
|
44
|
+
requestId,
|
|
45
|
+
eventType: 'onError',
|
|
46
|
+
data: { error: reason },
|
|
47
|
+
}, '*');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const responseTypes = {
|
|
51
|
+
protocol: 'APP_BRIDGE_PROTOCOL_RESPONSE',
|
|
52
|
+
userInfo: 'APP_BRIDGE_USER_INFO_RESPONSE',
|
|
53
|
+
authCode: 'APP_BRIDGE_AUTH_CODE_RESPONSE',
|
|
54
|
+
screenshot: 'APP_BRIDGE_SCREENSHOT_RESPONSE',
|
|
55
|
+
settings: 'APP_BRIDGE_SETTINGS_RESPONSE',
|
|
56
|
+
};
|
|
57
|
+
browser.postMessage({
|
|
58
|
+
source: 'mearl-content',
|
|
59
|
+
type: responseTypes[String(message.type)],
|
|
60
|
+
requestId,
|
|
61
|
+
result: { success: false, error: reason },
|
|
62
|
+
}, '*');
|
|
63
|
+
};
|
|
64
|
+
if (options.supportedTypes && !options.supportedTypes.includes(String(message.type))) {
|
|
65
|
+
replyFailure('this App bridge operation is not supported by the selected backend');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const binding = target[bindingName];
|
|
69
|
+
if (typeof binding !== 'function') {
|
|
70
|
+
replyFailure('native relay binding is unavailable');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
binding(JSON.stringify({
|
|
75
|
+
type: message.type,
|
|
76
|
+
requestId,
|
|
77
|
+
payload: message.payload,
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
replyFailure(error instanceof Error ? error.message : 'native relay call failed');
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
export * from './profiles.js';
|
|
2
2
|
export * from './settings.js';
|
|
3
3
|
export * from './updates.js';
|
|
4
|
+
export * from './environment/changes.js';
|
|
5
|
+
export * from './environment/document.js';
|
|
6
|
+
export * from './environment/relay.js';
|
|
7
|
+
export * from './environment/app-device.js';
|
|
8
|
+
export * from './environment/identity.js';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
export * from './profiles.js';
|
|
2
2
|
export * from './settings.js';
|
|
3
3
|
export * from './updates.js';
|
|
4
|
+
export * from './environment/changes.js';
|
|
5
|
+
export * from './environment/document.js';
|
|
6
|
+
export * from './environment/relay.js';
|
|
7
|
+
export * from './environment/app-device.js';
|
|
8
|
+
export * from './environment/identity.js';
|
package/dist/profiles.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEVICE_PRESETS,
|
|
1
|
+
import { DEVICE_PRESETS, isAppBridgeBehavior, appProfileRevision, validateAppProfileDefinition, resolveEmulationConfig, } from '@mearl/browser-core';
|
|
2
2
|
function emptyActions() {
|
|
3
3
|
return {
|
|
4
4
|
open: [],
|
|
@@ -7,7 +7,7 @@ function emptyActions() {
|
|
|
7
7
|
navigationBar: [],
|
|
8
8
|
statusBar: [],
|
|
9
9
|
systemInfo: [],
|
|
10
|
-
|
|
10
|
+
protocol: [],
|
|
11
11
|
kvGet: [],
|
|
12
12
|
kvSet: [],
|
|
13
13
|
kvDelete: [],
|
|
@@ -27,7 +27,7 @@ function mergeActions(...maps) {
|
|
|
27
27
|
out.navigationBar.push(...(m.navigationBar ?? []));
|
|
28
28
|
out.statusBar.push(...(m.statusBar ?? []));
|
|
29
29
|
out.systemInfo.push(...m.systemInfo);
|
|
30
|
-
out.
|
|
30
|
+
out.protocol.push(...m.protocol);
|
|
31
31
|
out.kvGet.push(...(m.kvGet ?? []));
|
|
32
32
|
out.kvSet.push(...(m.kvSet ?? []));
|
|
33
33
|
out.kvDelete.push(...(m.kvDelete ?? []));
|
|
@@ -39,7 +39,6 @@ function mergeActions(...maps) {
|
|
|
39
39
|
}
|
|
40
40
|
return out;
|
|
41
41
|
}
|
|
42
|
-
const APP_BRIDGE_BEHAVIOR_SET = new Set(APP_BRIDGE_BEHAVIORS);
|
|
43
42
|
const BRIDGE_ACTION_ARRAY_KEYS = [
|
|
44
43
|
'open',
|
|
45
44
|
'close',
|
|
@@ -47,7 +46,7 @@ const BRIDGE_ACTION_ARRAY_KEYS = [
|
|
|
47
46
|
'navigationBar',
|
|
48
47
|
'statusBar',
|
|
49
48
|
'systemInfo',
|
|
50
|
-
'
|
|
49
|
+
'protocol',
|
|
51
50
|
'kvGet',
|
|
52
51
|
'kvSet',
|
|
53
52
|
'kvDelete',
|
|
@@ -110,7 +109,7 @@ export function validateAppBridgeConfig(value) {
|
|
|
110
109
|
if (Number(hasBehavior) + Number(hasResponse) + Number(hasNoop) !== 1) {
|
|
111
110
|
throw new Error(`bridgeConfig.methods.${method} must define exactly one of behavior, response, or noop=true`);
|
|
112
111
|
}
|
|
113
|
-
if (hasBehavior && !
|
|
112
|
+
if (hasBehavior && !isAppBridgeBehavior(methodConfig.behavior)) {
|
|
114
113
|
throw new Error(`bridgeConfig.methods.${method} has an unsupported behavior`);
|
|
115
114
|
}
|
|
116
115
|
if (hasResponse && !isJsonValue(methodConfig.response)) {
|
|
@@ -296,14 +295,13 @@ export function resolveAppProfileDefinition(definition, options = {}) {
|
|
|
296
295
|
schemes: profile.schemes,
|
|
297
296
|
...(profile.miniProgram && { miniProgram: profile.miniProgram }),
|
|
298
297
|
...(profile.imagePicker && { imagePicker: profile.imagePicker }),
|
|
299
|
-
...(profile.geolocation && { geolocation: profile.geolocation }),
|
|
300
298
|
...(profile.overlay && { overlay: profile.overlay }),
|
|
301
299
|
...(profile.popBeforeOpenParam && { popBeforeOpenParam: profile.popBeforeOpenParam }),
|
|
302
300
|
...(profile.messageEvent && { messageEvent: profile.messageEvent }),
|
|
303
301
|
...(profile.nativeBack && { nativeBack: profile.nativeBack }),
|
|
304
302
|
...(profile.layerUrlProxy && { layerUrlProxy: profile.layerUrlProxy }),
|
|
305
303
|
...(profile.urlSyncHostSuffixes && { urlSyncHostSuffixes: profile.urlSyncHostSuffixes }),
|
|
306
|
-
...(profile.
|
|
304
|
+
...(profile.runtimeOptions && { runtimeOptions: JSON.parse(JSON.stringify(profile.runtimeOptions)) }),
|
|
307
305
|
simulateSystemBars: false,
|
|
308
306
|
externalDeviceChrome: false,
|
|
309
307
|
rootContainer: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mearl/app-profiles",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.8",
|
|
4
4
|
"description": "Declarative App profile compilation for installed capabilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -24,14 +24,14 @@
|
|
|
24
24
|
],
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@mearl/browser-core": "3.0.0-beta.
|
|
27
|
+
"@mearl/browser-core": "3.0.0-beta.8"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"typescript": "^5.8.3"
|
|
31
31
|
},
|
|
32
32
|
"repository": {
|
|
33
33
|
"type": "git",
|
|
34
|
-
"url": "git+https://github.com/
|
|
34
|
+
"url": "git+https://github.com/mearlhq/mearl.git",
|
|
35
35
|
"directory": "packages/app-profiles"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|