@ceraph/react-native-mcp 0.2.2 → 0.3.2
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/LICENSE +116 -15
- package/README.md +79 -77
- package/assets/default.png +0 -0
- package/dist/app-lifecycle.d.ts +50 -0
- package/dist/app-lifecycle.js +487 -0
- package/dist/camera-image-writer.d.ts +43 -0
- package/dist/camera-image-writer.js +280 -0
- package/dist/camera-registry-sync.d.ts +18 -0
- package/dist/camera-registry-sync.js +117 -0
- package/dist/cli.d.ts +0 -7
- package/dist/cli.js +41 -9
- package/dist/device-autonomy.d.ts +30 -0
- package/dist/device-autonomy.js +117 -0
- package/dist/error-parser.d.ts +6 -26
- package/dist/error-parser.js +4 -74
- package/dist/expo-manager.d.ts +2 -74
- package/dist/expo-manager.js +11 -125
- package/dist/index.d.ts +0 -7
- package/dist/index.js +1266 -56
- package/dist/init/ast-camera.d.ts +29 -0
- package/dist/init/ast-camera.js +267 -0
- package/dist/init/ast-layout.d.ts +15 -0
- package/dist/init/ast-layout.js +167 -0
- package/dist/init/claude-hook-constants.d.ts +9 -0
- package/dist/init/claude-hook-constants.js +91 -0
- package/dist/init/lan-ip.d.ts +11 -0
- package/dist/init/lan-ip.js +51 -0
- package/dist/init/monorepo.d.ts +13 -0
- package/dist/init/monorepo.js +185 -0
- package/dist/init/oauth.d.ts +52 -0
- package/dist/init/oauth.js +220 -0
- package/dist/init/package-manager.d.ts +11 -0
- package/dist/init/package-manager.js +60 -0
- package/dist/init/prompt.d.ts +12 -0
- package/dist/init/prompt.js +68 -0
- package/dist/init/shell-profile.d.ts +22 -0
- package/dist/init/shell-profile.js +85 -0
- package/dist/init/steps.d.ts +135 -0
- package/dist/init/steps.js +399 -0
- package/dist/init/url-scheme.d.ts +42 -0
- package/dist/init/url-scheme.js +187 -0
- package/dist/init/walkthrough.d.ts +76 -0
- package/dist/init/walkthrough.js +340 -0
- package/dist/init.d.ts +7 -7
- package/dist/init.js +280 -120
- package/dist/iproxy-manager.d.ts +32 -0
- package/dist/iproxy-manager.js +216 -0
- package/dist/mac-caffeinate.d.ts +10 -0
- package/dist/mac-caffeinate.js +56 -0
- package/dist/permission-interceptor.d.ts +29 -0
- package/dist/permission-interceptor.js +185 -0
- package/dist/prebuild-detector.d.ts +0 -30
- package/dist/prebuild-detector.js +1 -42
- package/dist/preflight.d.ts +34 -0
- package/dist/preflight.js +847 -0
- package/dist/screen.d.ts +132 -43
- package/dist/screen.js +668 -94
- package/dist/shim/boot.d.ts +41 -0
- package/dist/shim/boot.js +141 -0
- package/dist/shim/camera.d.ts +22 -0
- package/dist/shim/camera.js +62 -0
- package/dist/shim/config.d.ts +6 -0
- package/dist/shim/config.js +56 -0
- package/dist/shim/deep-link.d.ts +1 -0
- package/dist/shim/deep-link.js +25 -0
- package/dist/shim/dev-guard.d.ts +1 -0
- package/dist/shim/dev-guard.js +3 -0
- package/dist/shim/error-handler.d.ts +20 -0
- package/dist/shim/error-handler.js +66 -0
- package/dist/shim/fetch-interceptor.d.ts +13 -0
- package/dist/shim/fetch-interceptor.js +93 -0
- package/dist/shim/index.d.ts +6 -0
- package/dist/shim/index.js +6 -0
- package/dist/shim/keep-awake.d.ts +13 -0
- package/dist/shim/keep-awake.js +118 -0
- package/dist/shim/reload.d.ts +23 -0
- package/dist/shim/reload.js +76 -0
- package/dist/shim/signal-capture.d.ts +11 -0
- package/dist/shim/signal-capture.js +15 -0
- package/dist/shim/signal-transport.d.ts +17 -0
- package/dist/shim/signal-transport.js +43 -0
- package/dist/signal-listener.d.ts +27 -0
- package/dist/signal-listener.js +135 -0
- package/dist/simulator-boot.d.ts +52 -0
- package/dist/simulator-boot.js +227 -0
- package/dist/target.d.ts +48 -0
- package/dist/target.js +267 -0
- package/dist/uninstall/cli-runner.d.ts +32 -0
- package/dist/uninstall/cli-runner.js +223 -0
- package/dist/uninstall/footprint.d.ts +40 -0
- package/dist/uninstall/footprint.js +288 -0
- package/dist/uninstall/mcp-tools.d.ts +14 -0
- package/dist/uninstall/mcp-tools.js +175 -0
- package/dist/uninstall/revert-auth.d.ts +22 -0
- package/dist/uninstall/revert-auth.js +31 -0
- package/dist/uninstall/revert-boot.d.ts +24 -0
- package/dist/uninstall/revert-boot.js +242 -0
- package/dist/uninstall/revert-camera.d.ts +12 -0
- package/dist/uninstall/revert-camera.js +199 -0
- package/dist/uninstall/revert-ceraph-dir.d.ts +27 -0
- package/dist/uninstall/revert-ceraph-dir.js +38 -0
- package/dist/uninstall/revert-claude-hooks.d.ts +19 -0
- package/dist/uninstall/revert-claude-hooks.js +191 -0
- package/dist/uninstall/revert-gitignore.d.ts +17 -0
- package/dist/uninstall/revert-gitignore.js +43 -0
- package/dist/uninstall/revert-mcp-clients.d.ts +57 -0
- package/dist/uninstall/revert-mcp-clients.js +194 -0
- package/dist/uninstall/revert-package.d.ts +34 -0
- package/dist/uninstall/revert-package.js +98 -0
- package/dist/uninstall/revert-scheme.d.ts +36 -0
- package/dist/uninstall/revert-scheme.js +139 -0
- package/dist/uninstall/revert-signal-host-env.d.ts +31 -0
- package/dist/uninstall/revert-signal-host-env.js +61 -0
- package/dist/uninstall/walkthrough.d.ts +80 -0
- package/dist/uninstall/walkthrough.js +1244 -0
- package/dist/utils/atomic-write.d.ts +1 -0
- package/dist/utils/atomic-write.js +30 -0
- package/dist/wait-for-device.d.ts +68 -0
- package/dist/wait-for-device.js +368 -0
- package/dist/wda-manager.d.ts +38 -0
- package/dist/wda-manager.js +186 -0
- package/dist/wda-simulator.d.ts +28 -0
- package/dist/wda-simulator.js +257 -0
- package/package.json +59 -5
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
interface LinkingLike {
|
|
2
|
+
addEventListener: (type: "url", handler: (event: {
|
|
3
|
+
url: string;
|
|
4
|
+
}) => void) => {
|
|
5
|
+
remove?: () => void;
|
|
6
|
+
} | undefined;
|
|
7
|
+
}
|
|
8
|
+
interface AppStateLike {
|
|
9
|
+
addEventListener: (type: "change", handler: (state: string) => void) => {
|
|
10
|
+
remove?: () => void;
|
|
11
|
+
} | undefined;
|
|
12
|
+
}
|
|
13
|
+
interface ApplyRegistryFn {
|
|
14
|
+
(): void;
|
|
15
|
+
}
|
|
16
|
+
interface KeepAwakeLike {
|
|
17
|
+
enable: () => {
|
|
18
|
+
applied: boolean;
|
|
19
|
+
};
|
|
20
|
+
disable: () => void;
|
|
21
|
+
}
|
|
22
|
+
interface InstallCeraphDeps {
|
|
23
|
+
linking?: LinkingLike;
|
|
24
|
+
appState?: AppStateLike | null;
|
|
25
|
+
keepAwake?: KeepAwakeLike | null;
|
|
26
|
+
applyCameraImageRegistry?: ApplyRegistryFn | null;
|
|
27
|
+
}
|
|
28
|
+
export interface InstallCeraphResult {
|
|
29
|
+
cameraRegistryApplied: boolean;
|
|
30
|
+
deepLinkListenerInstalled: boolean;
|
|
31
|
+
signalCapture: {
|
|
32
|
+
errorHandler: boolean;
|
|
33
|
+
fetchInterceptor: boolean;
|
|
34
|
+
reloadListener: boolean;
|
|
35
|
+
} | null;
|
|
36
|
+
keepAwakeActivated: boolean;
|
|
37
|
+
appStateListenerInstalled: boolean;
|
|
38
|
+
}
|
|
39
|
+
export declare function installCeraph(deps?: InstallCeraphDeps): InstallCeraphResult;
|
|
40
|
+
export declare function _resetForTesting(): void;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { handleCeraphDeepLink } from "./deep-link.js";
|
|
2
|
+
import { isDevRuntime } from "./dev-guard.js";
|
|
3
|
+
import { disableKeepAwake, enableKeepAwake } from "./keep-awake.js";
|
|
4
|
+
import { installSignalCapture } from "./signal-capture.js";
|
|
5
|
+
let currentDeepLinkSub;
|
|
6
|
+
let currentAppStateSub;
|
|
7
|
+
export function installCeraph(deps = {}) {
|
|
8
|
+
if (!isDevRuntime()) {
|
|
9
|
+
return {
|
|
10
|
+
cameraRegistryApplied: false,
|
|
11
|
+
deepLinkListenerInstalled: false,
|
|
12
|
+
signalCapture: null,
|
|
13
|
+
keepAwakeActivated: false,
|
|
14
|
+
appStateListenerInstalled: false,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
let cameraRegistryApplied = false;
|
|
18
|
+
const applyRegistry = deps.applyCameraImageRegistry === undefined
|
|
19
|
+
? loadGeneratedRegistry()
|
|
20
|
+
: deps.applyCameraImageRegistry;
|
|
21
|
+
if (applyRegistry) {
|
|
22
|
+
try {
|
|
23
|
+
applyRegistry();
|
|
24
|
+
cameraRegistryApplied = true;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
let deepLinkListenerInstalled = false;
|
|
30
|
+
currentDeepLinkSub?.remove?.();
|
|
31
|
+
currentDeepLinkSub = undefined;
|
|
32
|
+
let linking;
|
|
33
|
+
try {
|
|
34
|
+
linking = deps.linking ?? loadLinking();
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
linking = undefined;
|
|
38
|
+
}
|
|
39
|
+
if (linking && typeof linking.addEventListener === "function") {
|
|
40
|
+
const sub = linking.addEventListener("url", (event) => {
|
|
41
|
+
if (typeof event?.url === "string") {
|
|
42
|
+
handleCeraphDeepLink(event.url);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
currentDeepLinkSub = sub;
|
|
46
|
+
deepLinkListenerInstalled = true;
|
|
47
|
+
}
|
|
48
|
+
const signalCapture = installSignalCapture();
|
|
49
|
+
let keepAwakeActivated = false;
|
|
50
|
+
const keepAwakeBinding = deps.keepAwake === undefined
|
|
51
|
+
? {
|
|
52
|
+
enable: () => enableKeepAwake(),
|
|
53
|
+
disable: () => disableKeepAwake(),
|
|
54
|
+
}
|
|
55
|
+
: deps.keepAwake ?? { enable: () => ({ applied: false }), disable: () => undefined };
|
|
56
|
+
try {
|
|
57
|
+
const res = keepAwakeBinding.enable();
|
|
58
|
+
keepAwakeActivated = res.applied;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
keepAwakeActivated = false;
|
|
62
|
+
}
|
|
63
|
+
let appStateListenerInstalled = false;
|
|
64
|
+
currentAppStateSub?.remove?.();
|
|
65
|
+
currentAppStateSub = undefined;
|
|
66
|
+
let appState;
|
|
67
|
+
try {
|
|
68
|
+
appState = deps.appState === undefined ? loadAppState() : deps.appState;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
appState = null;
|
|
72
|
+
}
|
|
73
|
+
if (appState && typeof appState.addEventListener === "function") {
|
|
74
|
+
const sub = appState.addEventListener("change", (state) => {
|
|
75
|
+
if (state === "active") {
|
|
76
|
+
try {
|
|
77
|
+
const res = keepAwakeBinding.enable();
|
|
78
|
+
keepAwakeActivated = res.applied;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
try {
|
|
85
|
+
keepAwakeBinding.disable();
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
currentAppStateSub = sub;
|
|
92
|
+
appStateListenerInstalled = true;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
cameraRegistryApplied,
|
|
96
|
+
deepLinkListenerInstalled,
|
|
97
|
+
signalCapture,
|
|
98
|
+
keepAwakeActivated,
|
|
99
|
+
appStateListenerInstalled,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function loadGeneratedRegistry() {
|
|
103
|
+
const req = globalThis.require;
|
|
104
|
+
if (typeof req !== "function")
|
|
105
|
+
return null;
|
|
106
|
+
const candidates = [
|
|
107
|
+
"../.ceraph/camera-images/_registry",
|
|
108
|
+
"./.ceraph/camera-images/_registry",
|
|
109
|
+
];
|
|
110
|
+
for (const id of candidates) {
|
|
111
|
+
try {
|
|
112
|
+
const mod = req(id);
|
|
113
|
+
if (mod && typeof mod.applyCameraImageRegistry === "function") {
|
|
114
|
+
return mod.applyCameraImageRegistry;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
function loadLinking() {
|
|
123
|
+
const req = globalThis.require;
|
|
124
|
+
if (typeof req !== "function")
|
|
125
|
+
return undefined;
|
|
126
|
+
const mod = req("react-native");
|
|
127
|
+
return mod?.Linking;
|
|
128
|
+
}
|
|
129
|
+
function loadAppState() {
|
|
130
|
+
const req = globalThis.require;
|
|
131
|
+
if (typeof req !== "function")
|
|
132
|
+
return undefined;
|
|
133
|
+
const mod = req("react-native");
|
|
134
|
+
return mod?.AppState;
|
|
135
|
+
}
|
|
136
|
+
export function _resetForTesting() {
|
|
137
|
+
currentDeepLinkSub?.remove?.();
|
|
138
|
+
currentDeepLinkSub = undefined;
|
|
139
|
+
currentAppStateSub?.remove?.();
|
|
140
|
+
currentAppStateSub = undefined;
|
|
141
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Ref, type ReactNode } from "react";
|
|
2
|
+
export interface CeraphCameraProps {
|
|
3
|
+
facing?: "front" | "back";
|
|
4
|
+
flash?: "auto" | "on" | "off";
|
|
5
|
+
zoom?: number;
|
|
6
|
+
style?: unknown;
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
imageKey?: string;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface CeraphCameraHandle {
|
|
12
|
+
takePictureAsync: (options?: Record<string, unknown>) => Promise<{
|
|
13
|
+
uri: string;
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
base64?: string;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
export declare function buildTakePictureOverride(): CeraphCameraHandle["takePictureAsync"] | null;
|
|
20
|
+
export declare const CeraphCamera: import("react").ForwardRefExoticComponent<CeraphCameraProps & {
|
|
21
|
+
ref?: Ref<CeraphCameraHandle> | undefined;
|
|
22
|
+
}>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createElement, forwardRef, useImperativeHandle, } from "react";
|
|
2
|
+
import { View, Image } from "react-native";
|
|
3
|
+
import { getCurrentTestImage, selectTestImageKey } from "./config.js";
|
|
4
|
+
import { isDevRuntime } from "./dev-guard.js";
|
|
5
|
+
function resolveTestImageUri(source) {
|
|
6
|
+
if (typeof source === "number") {
|
|
7
|
+
const resolved = Image.resolveAssetSource(source);
|
|
8
|
+
return resolved?.uri ?? "";
|
|
9
|
+
}
|
|
10
|
+
if (Array.isArray(source)) {
|
|
11
|
+
const first = source[0];
|
|
12
|
+
return first ? first.uri : "";
|
|
13
|
+
}
|
|
14
|
+
return source.uri;
|
|
15
|
+
}
|
|
16
|
+
export function buildTakePictureOverride() {
|
|
17
|
+
if (!isDevRuntime())
|
|
18
|
+
return null;
|
|
19
|
+
if (getCurrentTestImage() === null)
|
|
20
|
+
return null;
|
|
21
|
+
return async () => {
|
|
22
|
+
const live = getCurrentTestImage();
|
|
23
|
+
if (!live) {
|
|
24
|
+
return { uri: "", width: 0, height: 0, base64: undefined };
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
uri: resolveTestImageUri(live),
|
|
28
|
+
width: 0,
|
|
29
|
+
height: 0,
|
|
30
|
+
base64: undefined,
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export const CeraphCamera = forwardRef(function CeraphCamera(props, ref) {
|
|
35
|
+
if (isDevRuntime() &&
|
|
36
|
+
typeof props.imageKey === "string" &&
|
|
37
|
+
props.imageKey.length > 0 &&
|
|
38
|
+
!props.imageKey.startsWith("@")) {
|
|
39
|
+
selectTestImageKey(props.imageKey);
|
|
40
|
+
}
|
|
41
|
+
const override = buildTakePictureOverride();
|
|
42
|
+
useImperativeHandle(override !== null ? ref : null, () => ({ takePictureAsync: override }), [override]);
|
|
43
|
+
if (override !== null) {
|
|
44
|
+
const image = getCurrentTestImage();
|
|
45
|
+
const imageElement = image != null
|
|
46
|
+
? createElement(Image, {
|
|
47
|
+
key: "ceraph-test-image",
|
|
48
|
+
source: image,
|
|
49
|
+
style: { width: "100%", height: "100%" },
|
|
50
|
+
resizeMode: "cover",
|
|
51
|
+
})
|
|
52
|
+
: null;
|
|
53
|
+
return createElement(View, {
|
|
54
|
+
style: props.style,
|
|
55
|
+
children: [imageElement, props.children],
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const expoCamera = require("expo-camera");
|
|
59
|
+
const CameraView = expoCamera.CameraView;
|
|
60
|
+
return createElement(CameraView, { ...props, ref });
|
|
61
|
+
});
|
|
62
|
+
CeraphCamera.displayName = "CeraphCamera";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ImageSourcePropType } from "react-native";
|
|
2
|
+
export declare function configureTestImage(keyOrSource: string | ImageSourcePropType, source?: ImageSourcePropType): void;
|
|
3
|
+
export declare function selectTestImageKey(key: string): void;
|
|
4
|
+
export declare function getCurrentTestImage(): ImageSourcePropType | null;
|
|
5
|
+
export declare function _resetForTesting(): void;
|
|
6
|
+
export declare function _registerBundledDefaultForTesting(source: ImageSourcePropType): void;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const registry = new Map();
|
|
2
|
+
let currentKey = null;
|
|
3
|
+
const DEFAULT_KEY = "default";
|
|
4
|
+
let bundledDefaultRegistered = false;
|
|
5
|
+
function ensureBundledDefault() {
|
|
6
|
+
if (bundledDefaultRegistered)
|
|
7
|
+
return;
|
|
8
|
+
bundledDefaultRegistered = true;
|
|
9
|
+
const req = globalThis.require;
|
|
10
|
+
if (typeof req !== "function")
|
|
11
|
+
return;
|
|
12
|
+
try {
|
|
13
|
+
const asset = req("../../assets/default.png");
|
|
14
|
+
if (asset != null && !registry.has(DEFAULT_KEY)) {
|
|
15
|
+
registry.set(DEFAULT_KEY, asset);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function configureTestImage(keyOrSource, source) {
|
|
22
|
+
if (source === undefined) {
|
|
23
|
+
registry.set(DEFAULT_KEY, keyOrSource);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (typeof keyOrSource !== "string") {
|
|
27
|
+
throw new Error("configureTestImage: key must be a string when 2 args are passed");
|
|
28
|
+
}
|
|
29
|
+
registry.set(keyOrSource, source);
|
|
30
|
+
}
|
|
31
|
+
export function selectTestImageKey(key) {
|
|
32
|
+
currentKey = key;
|
|
33
|
+
}
|
|
34
|
+
export function getCurrentTestImage() {
|
|
35
|
+
if (currentKey && registry.has(currentKey)) {
|
|
36
|
+
return registry.get(currentKey) ?? null;
|
|
37
|
+
}
|
|
38
|
+
if (!registry.has(DEFAULT_KEY)) {
|
|
39
|
+
ensureBundledDefault();
|
|
40
|
+
}
|
|
41
|
+
if (registry.has(DEFAULT_KEY)) {
|
|
42
|
+
return registry.get(DEFAULT_KEY) ?? null;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
export function _resetForTesting() {
|
|
47
|
+
registry.clear();
|
|
48
|
+
currentKey = null;
|
|
49
|
+
bundledDefaultRegistered = false;
|
|
50
|
+
}
|
|
51
|
+
export function _registerBundledDefaultForTesting(source) {
|
|
52
|
+
bundledDefaultRegistered = true;
|
|
53
|
+
if (!registry.has(DEFAULT_KEY)) {
|
|
54
|
+
registry.set(DEFAULT_KEY, source);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function handleCeraphDeepLink(url: string): boolean;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { selectTestImageKey } from "./config.js";
|
|
2
|
+
const MAX_KEY_LEN = 255;
|
|
3
|
+
export function handleCeraphDeepLink(url) {
|
|
4
|
+
if (!url)
|
|
5
|
+
return false;
|
|
6
|
+
let parsed;
|
|
7
|
+
try {
|
|
8
|
+
parsed = new URL(url);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
if (parsed.protocol !== "ceraph:")
|
|
14
|
+
return false;
|
|
15
|
+
const action = parsed.hostname;
|
|
16
|
+
if (action === "test-image") {
|
|
17
|
+
const key = parsed.searchParams.get("key");
|
|
18
|
+
if (key && key.length > 0 && key.length <= MAX_KEY_LEN) {
|
|
19
|
+
selectTestImageKey(key);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isDevRuntime(): boolean;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type TransportOptions } from "./signal-transport.js";
|
|
2
|
+
interface ErrorUtilsLike {
|
|
3
|
+
getGlobalHandler?: () => ((error: Error, isFatal?: boolean) => void) | undefined;
|
|
4
|
+
setGlobalHandler: (handler: (error: Error, isFatal?: boolean) => void) => void;
|
|
5
|
+
}
|
|
6
|
+
interface HermesInternalLike {
|
|
7
|
+
enablePromiseRejectionTracker?: (opts: {
|
|
8
|
+
allRejections?: boolean;
|
|
9
|
+
onUnhandled?: (id: number, error: unknown) => void;
|
|
10
|
+
onHandled?: (id: number) => void;
|
|
11
|
+
}) => void;
|
|
12
|
+
}
|
|
13
|
+
export interface InstallErrorHandlerOptions {
|
|
14
|
+
transport?: TransportOptions;
|
|
15
|
+
errorUtils?: ErrorUtilsLike;
|
|
16
|
+
hermesInternal?: HermesInternalLike;
|
|
17
|
+
}
|
|
18
|
+
export declare function installErrorHandler(opts?: InstallErrorHandlerOptions): boolean;
|
|
19
|
+
export declare function _resetForTesting(): void;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { isDevRuntime } from "./dev-guard.js";
|
|
2
|
+
import { postSignal } from "./signal-transport.js";
|
|
3
|
+
let installed = false;
|
|
4
|
+
export function installErrorHandler(opts = {}) {
|
|
5
|
+
if (!isDevRuntime())
|
|
6
|
+
return false;
|
|
7
|
+
if (installed)
|
|
8
|
+
return false;
|
|
9
|
+
installed = true;
|
|
10
|
+
const utils = opts.errorUtils ??
|
|
11
|
+
globalThis.ErrorUtils;
|
|
12
|
+
if (utils && typeof utils.setGlobalHandler === "function") {
|
|
13
|
+
const previous = utils.getGlobalHandler?.();
|
|
14
|
+
utils.setGlobalHandler((error, isFatal) => {
|
|
15
|
+
void postSignal({
|
|
16
|
+
kind: "js-error",
|
|
17
|
+
message: error?.message ?? String(error),
|
|
18
|
+
stack: error?.stack,
|
|
19
|
+
timestampMs: Date.now(),
|
|
20
|
+
}, opts.transport);
|
|
21
|
+
try {
|
|
22
|
+
previous?.(error, isFatal);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const hermes = opts.hermesInternal ??
|
|
29
|
+
globalThis.HermesInternal;
|
|
30
|
+
if (hermes && typeof hermes.enablePromiseRejectionTracker === "function") {
|
|
31
|
+
hermes.enablePromiseRejectionTracker({
|
|
32
|
+
allRejections: true,
|
|
33
|
+
onUnhandled: (_id, error) => {
|
|
34
|
+
const message = extractMessage(error);
|
|
35
|
+
const stack = extractStack(error);
|
|
36
|
+
void postSignal({
|
|
37
|
+
kind: "unhandled-rejection",
|
|
38
|
+
message,
|
|
39
|
+
stack,
|
|
40
|
+
timestampMs: Date.now(),
|
|
41
|
+
}, opts.transport);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
function extractMessage(error) {
|
|
48
|
+
if (error instanceof Error)
|
|
49
|
+
return error.message;
|
|
50
|
+
if (typeof error === "string")
|
|
51
|
+
return error;
|
|
52
|
+
try {
|
|
53
|
+
return JSON.stringify(error);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return String(error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function extractStack(error) {
|
|
60
|
+
if (error instanceof Error)
|
|
61
|
+
return error.stack;
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
export function _resetForTesting() {
|
|
65
|
+
installed = false;
|
|
66
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type TransportOptions } from "./signal-transport.js";
|
|
2
|
+
type FetchFn = (input: unknown, init?: unknown) => Promise<Response>;
|
|
3
|
+
export interface InstallFetchInterceptorOptions {
|
|
4
|
+
transport?: TransportOptions;
|
|
5
|
+
globalRef?: {
|
|
6
|
+
fetch?: FetchFn;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export declare function installFetchInterceptor(opts?: InstallFetchInterceptorOptions): boolean;
|
|
10
|
+
export declare function _uninstallForTesting(globalRef?: {
|
|
11
|
+
fetch?: FetchFn;
|
|
12
|
+
}): void;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { isDevRuntime } from "./dev-guard.js";
|
|
2
|
+
import { postSignal } from "./signal-transport.js";
|
|
3
|
+
export function installFetchInterceptor(opts = {}) {
|
|
4
|
+
if (!isDevRuntime())
|
|
5
|
+
return false;
|
|
6
|
+
const target = opts.globalRef ?? globalThis;
|
|
7
|
+
const current = target.fetch;
|
|
8
|
+
if (typeof current !== "function")
|
|
9
|
+
return false;
|
|
10
|
+
const original = current.__ceraphOriginalFetch ?? current;
|
|
11
|
+
const wrapper = async (input, init) => {
|
|
12
|
+
const url = extractUrl(input);
|
|
13
|
+
const method = extractMethod(input, init);
|
|
14
|
+
const startedAt = Date.now();
|
|
15
|
+
try {
|
|
16
|
+
const response = await original(input, init);
|
|
17
|
+
const durationMs = Date.now() - startedAt;
|
|
18
|
+
const status = response.status;
|
|
19
|
+
if (status >= 500) {
|
|
20
|
+
void postSignal({
|
|
21
|
+
kind: "network-error",
|
|
22
|
+
message: `${method} ${url} → ${status}`,
|
|
23
|
+
timestampMs: Date.now(),
|
|
24
|
+
url,
|
|
25
|
+
status,
|
|
26
|
+
method,
|
|
27
|
+
durationMs,
|
|
28
|
+
}, opts.transport);
|
|
29
|
+
}
|
|
30
|
+
else if (status >= 400) {
|
|
31
|
+
void postSignal({
|
|
32
|
+
kind: "network-4xx",
|
|
33
|
+
message: `${method} ${url} → ${status}`,
|
|
34
|
+
timestampMs: Date.now(),
|
|
35
|
+
url,
|
|
36
|
+
status,
|
|
37
|
+
method,
|
|
38
|
+
durationMs,
|
|
39
|
+
}, opts.transport);
|
|
40
|
+
}
|
|
41
|
+
return response;
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
const durationMs = Date.now() - startedAt;
|
|
45
|
+
void postSignal({
|
|
46
|
+
kind: "network-error",
|
|
47
|
+
message: `${method} ${url} failed: ${err?.message ?? String(err)}`,
|
|
48
|
+
stack: err?.stack,
|
|
49
|
+
timestampMs: Date.now(),
|
|
50
|
+
url,
|
|
51
|
+
method,
|
|
52
|
+
durationMs,
|
|
53
|
+
}, opts.transport);
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
wrapper.__ceraphOriginalFetch = original;
|
|
58
|
+
target.fetch = wrapper;
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
function extractUrl(input) {
|
|
62
|
+
if (typeof input === "string")
|
|
63
|
+
return input;
|
|
64
|
+
if (input && typeof input === "object") {
|
|
65
|
+
const maybeUrl = input.url;
|
|
66
|
+
if (typeof maybeUrl === "string")
|
|
67
|
+
return maybeUrl;
|
|
68
|
+
if (typeof input.toString === "function") {
|
|
69
|
+
try {
|
|
70
|
+
return String(input);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return "<unknown-url>";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return "<unknown-url>";
|
|
78
|
+
}
|
|
79
|
+
function extractMethod(input, init) {
|
|
80
|
+
const fromInit = init?.method;
|
|
81
|
+
if (typeof fromInit === "string")
|
|
82
|
+
return fromInit.toUpperCase();
|
|
83
|
+
const fromRequest = input?.method;
|
|
84
|
+
if (typeof fromRequest === "string")
|
|
85
|
+
return fromRequest.toUpperCase();
|
|
86
|
+
return "GET";
|
|
87
|
+
}
|
|
88
|
+
export function _uninstallForTesting(globalRef = globalThis) {
|
|
89
|
+
const current = globalRef.fetch;
|
|
90
|
+
if (current?.__ceraphOriginalFetch) {
|
|
91
|
+
globalRef.fetch = current.__ceraphOriginalFetch;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { installCeraph, type InstallCeraphResult, } from "./boot.js";
|
|
2
|
+
export { CeraphCamera, type CeraphCameraProps } from "./camera.js";
|
|
3
|
+
export { configureTestImage, getCurrentTestImage, selectTestImageKey, } from "./config.js";
|
|
4
|
+
export { handleCeraphDeepLink } from "./deep-link.js";
|
|
5
|
+
export { installSignalCapture, type InstallSignalCaptureOptions, type InstallSignalCaptureResult, } from "./signal-capture.js";
|
|
6
|
+
export { installReloadListener, triggerJsReload } from "./reload.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { installCeraph, } from "./boot.js";
|
|
2
|
+
export { CeraphCamera } from "./camera.js";
|
|
3
|
+
export { configureTestImage, getCurrentTestImage, selectTestImageKey, } from "./config.js";
|
|
4
|
+
export { handleCeraphDeepLink } from "./deep-link.js";
|
|
5
|
+
export { installSignalCapture, } from "./signal-capture.js";
|
|
6
|
+
export { installReloadListener, triggerJsReload } from "./reload.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface KeepAwakeModuleLike {
|
|
2
|
+
activate?: () => void;
|
|
3
|
+
deactivate?: () => void;
|
|
4
|
+
}
|
|
5
|
+
export declare function _resetForTesting(): void;
|
|
6
|
+
export declare function _setProviderForTesting(provider: KeepAwakeModuleLike | null): void;
|
|
7
|
+
export interface KeepAwakeResult {
|
|
8
|
+
applied: boolean;
|
|
9
|
+
provider: "expo-keep-awake" | "react-native-keep-awake" | "RNCKeepAwake" | null;
|
|
10
|
+
}
|
|
11
|
+
export declare function enableKeepAwake(): KeepAwakeResult;
|
|
12
|
+
export declare function disableKeepAwake(): void;
|
|
13
|
+
export {};
|