@pose-tracker/react-native-pose-estimation-light 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/LICENSE +45 -0
  2. package/PoseTrackerVision.podspec +28 -0
  3. package/README.md +161 -0
  4. package/THIRD_PARTY_NOTICES.md +19 -0
  5. package/ios/PoseTrackerVision/PoseTrackerBodyPosePlugin.m +18 -0
  6. package/ios/PoseTrackerVision/PoseTrackerBodyPosePlugin.swift +108 -0
  7. package/lib/PoseTrackerProvider.d.ts +102 -0
  8. package/lib/PoseTrackerProvider.js +204 -0
  9. package/lib/api/configure.d.ts +29 -0
  10. package/lib/api/configure.js +64 -0
  11. package/lib/api/skeleton.d.ts +17 -0
  12. package/lib/api/skeleton.js +53 -0
  13. package/lib/api/track.d.ts +61 -0
  14. package/lib/api/track.js +146 -0
  15. package/lib/backends/PoseBackend.d.ts +50 -0
  16. package/lib/backends/PoseBackend.js +11 -0
  17. package/lib/backends/vision/VisionPoseBackend.d.ts +72 -0
  18. package/lib/backends/vision/VisionPoseBackend.js +177 -0
  19. package/lib/backends/vision/mapVisionJoints.d.ts +35 -0
  20. package/lib/backends/vision/mapVisionJoints.js +162 -0
  21. package/lib/backends/vision/optionalVision.d.ts +32 -0
  22. package/lib/backends/vision/optionalVision.js +76 -0
  23. package/lib/backends/webview/WebViewPoseBackend.d.ts +223 -0
  24. package/lib/backends/webview/WebViewPoseBackend.js +382 -0
  25. package/lib/backends/webview/brandAssets.d.ts +5 -0
  26. package/lib/backends/webview/brandAssets.js +8 -0
  27. package/lib/backends/webview/onlineRuntime.d.ts +45 -0
  28. package/lib/backends/webview/onlineRuntime.js +57 -0
  29. package/lib/backends/webview/poseHtml.d.ts +71 -0
  30. package/lib/backends/webview/poseHtml.js +188 -0
  31. package/lib/backends/webview/poseRuntimeSource.d.ts +3 -0
  32. package/lib/backends/webview/poseRuntimeSource.js +4 -0
  33. package/lib/cache/obfuscate.d.ts +18 -0
  34. package/lib/cache/obfuscate.js +90 -0
  35. package/lib/camera/PoseCameraView.d.ts +51 -0
  36. package/lib/camera/PoseCameraView.js +99 -0
  37. package/lib/camera/WebViewPoseView.d.ts +81 -0
  38. package/lib/camera/WebViewPoseView.js +312 -0
  39. package/lib/client.d.ts +361 -0
  40. package/lib/client.js +1046 -0
  41. package/lib/diagnostics/logReport.d.ts +23 -0
  42. package/lib/diagnostics/logReport.js +102 -0
  43. package/lib/engine/EngineLoader.d.ts +95 -0
  44. package/lib/engine/EngineLoader.js +347 -0
  45. package/lib/engine/types.d.ts +83 -0
  46. package/lib/engine/types.js +11 -0
  47. package/lib/events/classicMessage.d.ts +31 -0
  48. package/lib/events/classicMessage.js +259 -0
  49. package/lib/exercises/aliases.d.ts +18 -0
  50. package/lib/exercises/aliases.js +48 -0
  51. package/lib/index.d.ts +72 -0
  52. package/lib/index.js +170 -0
  53. package/lib/models/poseModels.d.ts +39 -0
  54. package/lib/models/poseModels.js +63 -0
  55. package/lib/quality/AdaptiveQualityController.d.ts +120 -0
  56. package/lib/quality/AdaptiveQualityController.js +423 -0
  57. package/lib/quality/RuntimeGuard.d.ts +34 -0
  58. package/lib/quality/RuntimeGuard.js +105 -0
  59. package/lib/quality/captureMode.d.ts +82 -0
  60. package/lib/quality/captureMode.js +75 -0
  61. package/lib/quality/deviceCapability.d.ts +37 -0
  62. package/lib/quality/deviceCapability.js +177 -0
  63. package/lib/quality/profiles.d.ts +125 -0
  64. package/lib/quality/profiles.js +202 -0
  65. package/lib/runtime/RuntimeCache.d.ts +75 -0
  66. package/lib/runtime/RuntimeCache.js +230 -0
  67. package/lib/sdkVersion.d.ts +2 -0
  68. package/lib/sdkVersion.js +5 -0
  69. package/lib/support/optionalModules.d.ts +46 -0
  70. package/lib/support/optionalModules.js +82 -0
  71. package/lib/types/acceleration.d.ts +86 -0
  72. package/lib/types/acceleration.js +15 -0
  73. package/lib/types/events.d.ts +345 -0
  74. package/lib/types/events.js +10 -0
  75. package/lib/types/features.d.ts +90 -0
  76. package/lib/types/features.js +122 -0
  77. package/lib/types/manifest.d.ts +197 -0
  78. package/lib/types/manifest.js +10 -0
  79. package/lib/types/pose.d.ts +36 -0
  80. package/lib/types/pose.js +41 -0
  81. package/lib/types/preload.d.ts +17 -0
  82. package/lib/types/preload.js +10 -0
  83. package/lib/types/skeleton.d.ts +29 -0
  84. package/lib/types/skeleton.js +52 -0
  85. package/package.json +92 -0
  86. package/react-native.config.js +12 -0
  87. package/src/PoseTrackerProvider.tsx +332 -0
  88. package/src/api/configure.ts +82 -0
  89. package/src/api/skeleton.ts +60 -0
  90. package/src/api/track.ts +182 -0
  91. package/src/backends/PoseBackend.ts +59 -0
  92. package/src/backends/vision/VisionPoseBackend.ts +238 -0
  93. package/src/backends/vision/mapVisionJoints.ts +185 -0
  94. package/src/backends/vision/optionalVision.ts +94 -0
  95. package/src/backends/webview/WebViewPoseBackend.ts +582 -0
  96. package/src/backends/webview/brandAssets.ts +5 -0
  97. package/src/backends/webview/onlineRuntime.ts +94 -0
  98. package/src/backends/webview/poseHtml.ts +259 -0
  99. package/src/backends/webview/poseRuntimeSource.d.ts +3 -0
  100. package/src/cache/obfuscate.ts +94 -0
  101. package/src/camera/PoseCameraView.tsx +223 -0
  102. package/src/camera/WebViewPoseView.tsx +466 -0
  103. package/src/client.ts +1295 -0
  104. package/src/diagnostics/logReport.ts +131 -0
  105. package/src/engine/EngineLoader.ts +428 -0
  106. package/src/engine/types.ts +93 -0
  107. package/src/events/classicMessage.ts +271 -0
  108. package/src/exercises/aliases.ts +50 -0
  109. package/src/index.ts +243 -0
  110. package/src/models/poseModels.ts +93 -0
  111. package/src/quality/AdaptiveQualityController.ts +579 -0
  112. package/src/quality/RuntimeGuard.ts +119 -0
  113. package/src/quality/captureMode.ts +97 -0
  114. package/src/quality/deviceCapability.ts +187 -0
  115. package/src/quality/profiles.ts +253 -0
  116. package/src/runtime/RuntimeCache.ts +300 -0
  117. package/src/sdkVersion.ts +2 -0
  118. package/src/support/optionalModules.ts +114 -0
  119. package/src/types/acceleration.ts +94 -0
  120. package/src/types/events.ts +437 -0
  121. package/src/types/features.ts +176 -0
  122. package/src/types/manifest.ts +230 -0
  123. package/src/types/pose.ts +85 -0
  124. package/src/types/preload.ts +19 -0
  125. package/src/types/skeleton.ts +77 -0
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ /**
3
+ * Apple Vision implementation of `PoseBackend` (iOS only).
4
+ *
5
+ * Uses `VNDetectHumanBodyPoseRequest` via a VisionCamera frame-processor
6
+ * plugin shipped in this package's `ios/` folder. Up to 19 anatomical
7
+ * joints (COCO-17 + `neck` + `root`). Primary fast path is
8
+ * `PoseCameraView` → worklet → native plugin (CVPixelBuffer, no RGB copy).
9
+ *
10
+ * Never loaded in Expo Go (`isExpoGo()` guard). On Android,
11
+ * `init()` throws {@link VisionUnavailableError} so the client can fall
12
+ * back to TFLite / TF.js when selection is `'auto'`.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.VisionPoseBackend = exports.VisionUnavailableError = void 0;
16
+ const react_native_1 = require("react-native");
17
+ const optionalModules_1 = require("../../support/optionalModules");
18
+ const optionalVision_1 = require("./optionalVision");
19
+ const mapVisionJoints_1 = require("./mapVisionJoints");
20
+ const DEFAULT_MAX_ACCEPTABLE_INFERENCE_MS = 50;
21
+ /** Thrown when Apple Vision cannot run (wrong platform, Expo Go, missing pod). */
22
+ class VisionUnavailableError extends Error {
23
+ constructor(message) {
24
+ super(message);
25
+ this.name = 'VisionUnavailableError';
26
+ }
27
+ }
28
+ exports.VisionUnavailableError = VisionUnavailableError;
29
+ class VisionPoseBackend {
30
+ constructor(options = {}) {
31
+ this.options = options;
32
+ this.name = 'apple-vision';
33
+ this.plugin = null;
34
+ this.warm = false;
35
+ this.acceleration = 'unknown';
36
+ this.inferenceTimesMs = [];
37
+ this.reasons = [];
38
+ this.maxAcceptableInferenceMs =
39
+ options.maxAcceptableInferenceMs ?? DEFAULT_MAX_ACCEPTABLE_INFERENCE_MS;
40
+ this.onDiagnostic = options.onDiagnostic;
41
+ this.onAccelerationChange = options.onAccelerationChange;
42
+ }
43
+ async init(_options) {
44
+ if (this.plugin) {
45
+ return;
46
+ }
47
+ if (react_native_1.Platform.OS !== 'ios') {
48
+ throw new VisionUnavailableError('Apple Vision body pose is iOS-only. Use preferredBackend "tflite" or "tfjs" on Android.');
49
+ }
50
+ if ((0, optionalModules_1.isExpoGo)()) {
51
+ throw new VisionUnavailableError('Apple Vision requires a native iOS build — not available in Expo Go.');
52
+ }
53
+ if (!(0, optionalVision_1.isVisionBackendAvailable)()) {
54
+ throw new VisionUnavailableError('Apple Vision frame-processor plugin is not linked. Rebuild with the SDK ios/ pod ' +
55
+ '(VisionCamera + PoseTrackerVision) and ensure react-native-vision-camera + worklets are installed.');
56
+ }
57
+ this.plugin = (0, optionalVision_1.getVisionBodyPosePlugin)();
58
+ if (!this.plugin) {
59
+ throw new VisionUnavailableError('Failed to initialize detectBodyPose frame-processor plugin.');
60
+ }
61
+ this.onDiagnostic?.('[posetracker] apple-vision backend ready (VNDetectHumanBodyPoseRequest, up to 19 joints)');
62
+ }
63
+ /**
64
+ * Vision has no separate model load — warm-up records the accelerator
65
+ * verdict. Real timings come from the first camera frames via
66
+ * {@link recordInferenceTime}.
67
+ */
68
+ async warmup() {
69
+ this.requirePlugin();
70
+ if (this.warm) {
71
+ return;
72
+ }
73
+ // Neural Engine / GPU path — Vision does not expose a CPU-only mode.
74
+ this.setAcceleration('gpu');
75
+ this.warm = true;
76
+ this.onDiagnostic?.(`[posetracker] apple-vision warm-up done: acceleration=${this.acceleration} ` +
77
+ '(per-frame ms reported from PoseCameraView)');
78
+ }
79
+ /**
80
+ * JS-path estimation. Accepts:
81
+ * - a VisionCamera `Frame` (has `.width`/`.height` and is callable via plugin);
82
+ * - a precomputed {@link VisionPluginResult} `{ joints }`.
83
+ *
84
+ * Prefer `PoseCameraView` (worklet) for production — this path is for
85
+ * TensorCamera-style hosts and unit tests.
86
+ */
87
+ async estimatePose(frame) {
88
+ const plugin = this.requirePlugin();
89
+ const asResult = frame;
90
+ if (asResult && Array.isArray(asResult.joints)) {
91
+ return (0, mapVisionJoints_1.poseFromVisionPluginResult)(asResult);
92
+ }
93
+ // VisionCamera Frame — structural duck-type (worklet Frame has width/height).
94
+ const anyFrame = frame;
95
+ if (anyFrame &&
96
+ typeof anyFrame.width === 'number' &&
97
+ typeof anyFrame.height === 'number' &&
98
+ typeof plugin.call === 'function') {
99
+ const start = Date.now();
100
+ const raw = plugin.call(frame);
101
+ const elapsed = Date.now() - start;
102
+ this.recordInferenceTime(elapsed);
103
+ return (0, mapVisionJoints_1.poseFromVisionPluginResult)(raw ?? undefined);
104
+ }
105
+ throw new Error('VisionPoseBackend: pass a VisionCamera Frame, a { joints } plugin result, ' +
106
+ 'or use PoseCameraView (worklet path).');
107
+ }
108
+ getAcceleration() {
109
+ if (!this.plugin && this.acceleration === 'unknown') {
110
+ return null;
111
+ }
112
+ return {
113
+ state: this.acceleration,
114
+ tfjsBackend: null,
115
+ runtime: 'vision',
116
+ delegate: 'apple-vision',
117
+ medianInferenceMs: median(this.inferenceTimesMs),
118
+ inferenceTimesMs: [...this.inferenceTimesMs],
119
+ maxAcceptableInferenceMs: this.maxAcceptableInferenceMs,
120
+ capabilities: null,
121
+ flags: {},
122
+ contextLossCount: 0,
123
+ reasons: [...this.reasons],
124
+ };
125
+ }
126
+ async dispose() {
127
+ this.plugin = null;
128
+ this.warm = false;
129
+ this.acceleration = 'unknown';
130
+ this.inferenceTimesMs = [];
131
+ this.reasons = [];
132
+ }
133
+ /** Plugin handle for PoseCameraView worklets. */
134
+ getFrameProcessorPlugin() {
135
+ return this.plugin;
136
+ }
137
+ /** Feed live inference timings from the camera worklet into diagnostics. */
138
+ recordInferenceTime(ms) {
139
+ this.inferenceTimesMs.push(ms);
140
+ if (this.inferenceTimesMs.length > 30) {
141
+ this.inferenceTimesMs.shift();
142
+ }
143
+ const med = median(this.inferenceTimesMs);
144
+ if (med !== null && med > this.maxAcceptableInferenceMs && this.acceleration === 'gpu') {
145
+ this.noteReason(`apple-vision median ${med.toFixed(0)} ms exceeds ${this.maxAcceptableInferenceMs} ms budget`);
146
+ this.setAcceleration('cpu-fallback');
147
+ }
148
+ }
149
+ setAcceleration(state) {
150
+ if (this.acceleration !== state) {
151
+ this.acceleration = state;
152
+ this.onAccelerationChange?.(state);
153
+ }
154
+ }
155
+ noteReason(reason) {
156
+ this.reasons.push(reason);
157
+ this.onDiagnostic?.(`[posetracker] ${reason}`);
158
+ }
159
+ requirePlugin() {
160
+ if (!this.plugin) {
161
+ throw new Error('VisionPoseBackend not initialized — call init() first.');
162
+ }
163
+ return this.plugin;
164
+ }
165
+ }
166
+ exports.VisionPoseBackend = VisionPoseBackend;
167
+ function median(values) {
168
+ if (values.length === 0) {
169
+ return null;
170
+ }
171
+ const sorted = [...values].sort((a, b) => a - b);
172
+ const mid = Math.floor(sorted.length / 2);
173
+ if (sorted.length % 2 === 0) {
174
+ return (sorted[mid - 1] + sorted[mid]) / 2;
175
+ }
176
+ return sorted[mid];
177
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Apple Vision body-pose joint → SDK keypoint mapping.
3
+ *
4
+ * `VNDetectHumanBodyPoseRequest` exposes 19 joints. Seventeen align 1:1 with
5
+ * the COCO / MoveNet topology; `neck` and `root` (mid-hip) are Vision extras
6
+ * kept under their Vision names so A/B overlays can show the full set.
7
+ *
8
+ * Vision normalized points use a **bottom-left** origin — the native plugin
9
+ * already flips Y (`y = 1 - visionY`) before returning joints here.
10
+ *
11
+ * `poseFromVisionPluginResult` carries a `'worklet'` directive so
12
+ * PoseCameraView can call it on the frame-processor thread.
13
+ */
14
+ import type { KeypointName, Pose } from '../../types/pose';
15
+ /** Vision `VNHumanBodyPoseObservation.JointName` camelCase → SDK name. */
16
+ export declare const VISION_JOINT_TO_SDK: Readonly<Record<string, KeypointName>>;
17
+ /** One joint as returned by the native frame-processor plugin. */
18
+ export interface VisionPluginJoint {
19
+ name: string;
20
+ x: number;
21
+ y: number;
22
+ score: number;
23
+ }
24
+ /** Payload of `detectBodyPose` frame-processor plugin (already Y-flipped). */
25
+ export interface VisionPluginResult {
26
+ joints: VisionPluginJoint[];
27
+ /** Wall time of the Vision request on the frame-processor thread, ms. */
28
+ inferenceTimeMs?: number;
29
+ }
30
+ /**
31
+ * Build a SDK `Pose` from the native plugin result. Unknown joint names are
32
+ * dropped; missing joints are filled with score 0 so overlays stay
33
+ * topology-stable when a joint is absent.
34
+ */
35
+ export declare function poseFromVisionPluginResult(result: VisionPluginResult | null | undefined, timestampMs?: number): Pose | null;
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ /**
3
+ * Apple Vision body-pose joint → SDK keypoint mapping.
4
+ *
5
+ * `VNDetectHumanBodyPoseRequest` exposes 19 joints. Seventeen align 1:1 with
6
+ * the COCO / MoveNet topology; `neck` and `root` (mid-hip) are Vision extras
7
+ * kept under their Vision names so A/B overlays can show the full set.
8
+ *
9
+ * Vision normalized points use a **bottom-left** origin — the native plugin
10
+ * already flips Y (`y = 1 - visionY`) before returning joints here.
11
+ *
12
+ * `poseFromVisionPluginResult` carries a `'worklet'` directive so
13
+ * PoseCameraView can call it on the frame-processor thread.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.VISION_JOINT_TO_SDK = void 0;
17
+ exports.poseFromVisionPluginResult = poseFromVisionPluginResult;
18
+ /** Vision `VNHumanBodyPoseObservation.JointName` camelCase → SDK name. */
19
+ exports.VISION_JOINT_TO_SDK = {
20
+ nose: 'nose',
21
+ leftEye: 'left_eye',
22
+ rightEye: 'right_eye',
23
+ leftEar: 'left_ear',
24
+ rightEar: 'right_ear',
25
+ leftShoulder: 'left_shoulder',
26
+ rightShoulder: 'right_shoulder',
27
+ leftElbow: 'left_elbow',
28
+ rightElbow: 'right_elbow',
29
+ leftWrist: 'left_wrist',
30
+ rightWrist: 'right_wrist',
31
+ leftHip: 'left_hip',
32
+ rightHip: 'right_hip',
33
+ leftKnee: 'left_knee',
34
+ rightKnee: 'right_knee',
35
+ leftAnkle: 'left_ankle',
36
+ rightAnkle: 'right_ankle',
37
+ neck: 'neck',
38
+ root: 'root',
39
+ };
40
+ /**
41
+ * Build a SDK `Pose` from the native plugin result. Unknown joint names are
42
+ * dropped; missing joints are filled with score 0 so overlays stay
43
+ * topology-stable when a joint is absent.
44
+ */
45
+ function poseFromVisionPluginResult(result, timestampMs = Date.now()) {
46
+ 'worklet';
47
+ // Inlined — worklets cannot capture module-level imports reliably.
48
+ const ORDER = [
49
+ 'nose',
50
+ 'left_eye',
51
+ 'right_eye',
52
+ 'left_ear',
53
+ 'right_ear',
54
+ 'left_shoulder',
55
+ 'right_shoulder',
56
+ 'left_elbow',
57
+ 'right_elbow',
58
+ 'left_wrist',
59
+ 'right_wrist',
60
+ 'left_hip',
61
+ 'right_hip',
62
+ 'left_knee',
63
+ 'right_knee',
64
+ 'left_ankle',
65
+ 'right_ankle',
66
+ 'neck',
67
+ 'root',
68
+ ];
69
+ const ALIAS = {
70
+ nose: 'nose',
71
+ leftEye: 'left_eye',
72
+ rightEye: 'right_eye',
73
+ leftEar: 'left_ear',
74
+ rightEar: 'right_ear',
75
+ leftShoulder: 'left_shoulder',
76
+ rightShoulder: 'right_shoulder',
77
+ leftElbow: 'left_elbow',
78
+ rightElbow: 'right_elbow',
79
+ leftWrist: 'left_wrist',
80
+ rightWrist: 'right_wrist',
81
+ leftHip: 'left_hip',
82
+ rightHip: 'right_hip',
83
+ leftKnee: 'left_knee',
84
+ rightKnee: 'right_knee',
85
+ leftAnkle: 'left_ankle',
86
+ rightAnkle: 'right_ankle',
87
+ neck: 'neck',
88
+ root: 'root',
89
+ left_eye: 'left_eye',
90
+ right_eye: 'right_eye',
91
+ left_ear: 'left_ear',
92
+ right_ear: 'right_ear',
93
+ left_shoulder: 'left_shoulder',
94
+ right_shoulder: 'right_shoulder',
95
+ left_elbow: 'left_elbow',
96
+ right_elbow: 'right_elbow',
97
+ left_wrist: 'left_wrist',
98
+ right_wrist: 'right_wrist',
99
+ left_hip: 'left_hip',
100
+ right_hip: 'right_hip',
101
+ left_knee: 'left_knee',
102
+ right_knee: 'right_knee',
103
+ left_ankle: 'left_ankle',
104
+ right_ankle: 'right_ankle',
105
+ };
106
+ if (!result || !result.joints || result.joints.length === 0) {
107
+ return null;
108
+ }
109
+ const byName = {};
110
+ for (let i = 0; i < result.joints.length; i++) {
111
+ const joint = result.joints[i];
112
+ if (!joint) {
113
+ continue;
114
+ }
115
+ const mapped = ALIAS[joint.name];
116
+ if (!mapped) {
117
+ continue;
118
+ }
119
+ const x = clamp01(joint.x);
120
+ const y = clamp01(joint.y);
121
+ const score = clamp01(joint.score);
122
+ byName[mapped] = { name: mapped, x, y, score };
123
+ }
124
+ const keypoints = [];
125
+ let scoreSum = 0;
126
+ let scoreCount = 0;
127
+ for (let i = 0; i < ORDER.length; i++) {
128
+ const name = ORDER[i];
129
+ const existing = byName[name];
130
+ if (existing) {
131
+ keypoints.push(existing);
132
+ if (existing.score > 0) {
133
+ scoreSum += existing.score;
134
+ scoreCount += 1;
135
+ }
136
+ }
137
+ else {
138
+ keypoints.push({ name, x: 0, y: 0, score: 0 });
139
+ }
140
+ }
141
+ if (scoreCount === 0) {
142
+ return null;
143
+ }
144
+ return {
145
+ keypoints,
146
+ score: scoreSum / scoreCount,
147
+ timestampMs,
148
+ };
149
+ }
150
+ function clamp01(n) {
151
+ 'worklet';
152
+ if (!Number.isFinite(n)) {
153
+ return 0;
154
+ }
155
+ if (n < 0) {
156
+ return 0;
157
+ }
158
+ if (n > 1) {
159
+ return 1;
160
+ }
161
+ return n;
162
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Guarded access to the Apple Vision body-pose frame-processor plugin.
3
+ *
4
+ * The plugin lives in this package's `ios/` folder (VisionCamera Swift
5
+ * FrameProcessorPlugin + `VNDetectHumanBodyPoseRequest`). It is only linked
6
+ * in native iOS builds — never in Expo Go. All requires are gated by
7
+ * `Platform.OS === 'ios' && !isExpoGo()` so Metro/Expo Go never evaluate
8
+ * Nitro/VisionCamera code paths unintentionally.
9
+ */
10
+ /** JS name registered via `VISION_EXPORT_SWIFT_FRAME_PROCESSOR(..., detectBodyPose)`. */
11
+ export declare const VISION_BODY_POSE_PLUGIN_NAME = "detectBodyPose";
12
+ export interface VisionFrameProcessorPlugin {
13
+ call(frame: unknown, args?: Record<string, unknown>): unknown;
14
+ }
15
+ /**
16
+ * True when we are on a native iOS build (not Expo Go) where the Vision
17
+ * frame-processor plugin can be loaded. Does not require Nitro / TFLite.
18
+ */
19
+ export declare function isVisionBackendAvailable(): boolean;
20
+ /**
21
+ * vision-camera + worklets present on iOS native build — enough to mount the
22
+ * Vision path of `PoseCameraView` once the backend has initialized.
23
+ */
24
+ export declare function isVisionPoseCameraAvailable(): boolean;
25
+ /**
26
+ * Lazily init the VisionCamera frame-processor plugin. Cached; `null` when
27
+ * the native binary does not include our Swift plugin (Expo Go, Android,
28
+ * or a host that excluded the pod).
29
+ */
30
+ export declare function getVisionBodyPosePlugin(): VisionFrameProcessorPlugin | null;
31
+ /** Test helper — clears the plugin cache between availability probes. */
32
+ export declare function resetVisionPluginCacheForTests(): void;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ /**
3
+ * Guarded access to the Apple Vision body-pose frame-processor plugin.
4
+ *
5
+ * The plugin lives in this package's `ios/` folder (VisionCamera Swift
6
+ * FrameProcessorPlugin + `VNDetectHumanBodyPoseRequest`). It is only linked
7
+ * in native iOS builds — never in Expo Go. All requires are gated by
8
+ * `Platform.OS === 'ios' && !isExpoGo()` so Metro/Expo Go never evaluate
9
+ * Nitro/VisionCamera code paths unintentionally.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.VISION_BODY_POSE_PLUGIN_NAME = void 0;
13
+ exports.isVisionBackendAvailable = isVisionBackendAvailable;
14
+ exports.isVisionPoseCameraAvailable = isVisionPoseCameraAvailable;
15
+ exports.getVisionBodyPosePlugin = getVisionBodyPosePlugin;
16
+ exports.resetVisionPluginCacheForTests = resetVisionPluginCacheForTests;
17
+ const react_native_1 = require("react-native");
18
+ const optionalModules_1 = require("../../support/optionalModules");
19
+ /** JS name registered via `VISION_EXPORT_SWIFT_FRAME_PROCESSOR(..., detectBodyPose)`. */
20
+ exports.VISION_BODY_POSE_PLUGIN_NAME = 'detectBodyPose';
21
+ let cachedPlugin;
22
+ /**
23
+ * True when we are on a native iOS build (not Expo Go) where the Vision
24
+ * frame-processor plugin can be loaded. Does not require Nitro / TFLite.
25
+ */
26
+ function isVisionBackendAvailable() {
27
+ if (react_native_1.Platform.OS !== 'ios' || (0, optionalModules_1.isExpoGo)()) {
28
+ return false;
29
+ }
30
+ if ((0, optionalModules_1.getVisionCamera)() === null || (0, optionalModules_1.getWorkletsCore)() === null) {
31
+ return false;
32
+ }
33
+ return getVisionBodyPosePlugin() !== null;
34
+ }
35
+ /**
36
+ * vision-camera + worklets present on iOS native build — enough to mount the
37
+ * Vision path of `PoseCameraView` once the backend has initialized.
38
+ */
39
+ function isVisionPoseCameraAvailable() {
40
+ return (react_native_1.Platform.OS === 'ios' &&
41
+ !(0, optionalModules_1.isExpoGo)() &&
42
+ (0, optionalModules_1.getVisionCamera)() !== null &&
43
+ (0, optionalModules_1.getWorkletsCore)() !== null);
44
+ }
45
+ /**
46
+ * Lazily init the VisionCamera frame-processor plugin. Cached; `null` when
47
+ * the native binary does not include our Swift plugin (Expo Go, Android,
48
+ * or a host that excluded the pod).
49
+ */
50
+ function getVisionBodyPosePlugin() {
51
+ if (cachedPlugin !== undefined) {
52
+ return cachedPlugin;
53
+ }
54
+ if (react_native_1.Platform.OS !== 'ios' || (0, optionalModules_1.isExpoGo)()) {
55
+ cachedPlugin = null;
56
+ return cachedPlugin;
57
+ }
58
+ try {
59
+ const mod = require('react-native-vision-camera');
60
+ const proxy = mod.VisionCameraProxy;
61
+ if (!proxy || typeof proxy.initFrameProcessorPlugin !== 'function') {
62
+ cachedPlugin = null;
63
+ return cachedPlugin;
64
+ }
65
+ const plugin = proxy.initFrameProcessorPlugin(exports.VISION_BODY_POSE_PLUGIN_NAME, {});
66
+ cachedPlugin = plugin ?? null;
67
+ }
68
+ catch {
69
+ cachedPlugin = null;
70
+ }
71
+ return cachedPlugin;
72
+ }
73
+ /** Test helper — clears the plugin cache between availability probes. */
74
+ function resetVisionPluginCacheForTests() {
75
+ cachedPlugin = undefined;
76
+ }