@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,29 @@
1
+ /**
2
+ * Handshake client: `configure(apiToken)` calls the Strapi backend, which
3
+ * validates the API token (plan/quota) and returns the `SdkManifest` —
4
+ * exercise configs, engine bundle descriptor, pose-runtime payload
5
+ * descriptor, model profiles, reference movements. All server-driven
6
+ * behavior flows from this single call.
7
+ *
8
+ * Keyless mode (`apiToken` null): the backend returns the PUBLIC manifest —
9
+ * pose-runtime descriptor only, no session/engine/exercises. This is how
10
+ * keypoints-only installs bootstrap the pose estimation payload.
11
+ */
12
+ import { SDK_VERSION } from '../sdkVersion';
13
+ import type { ConfigureRequest, PoseModelProfile, SdkManifest } from '../types/manifest';
14
+ export declare const SDK_NAME: "posetracker-rn-light";
15
+ export { SDK_VERSION };
16
+ /** Overridable for staging/self-hosted backends. */
17
+ export declare const DEFAULT_BASE_URL = "https://movelytics-strapi-c78a339b7070.herokuapp.com";
18
+ export interface ConfigureOptions {
19
+ baseUrl?: string;
20
+ poseModelProfile?: PoseModelProfile;
21
+ locale?: string;
22
+ /** Local cache versions, so the backend can answer `upToDate` per module. */
23
+ localVersions?: ConfigureRequest['localVersions'];
24
+ }
25
+ export declare class ConfigureError extends Error {
26
+ readonly code: 'invalid_token' | 'quota_exceeded' | 'network' | 'internal';
27
+ constructor(code: 'invalid_token' | 'quota_exceeded' | 'network' | 'internal', message: string);
28
+ }
29
+ export declare function configure(apiToken: string | null, options?: ConfigureOptions): Promise<SdkManifest>;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * Handshake client: `configure(apiToken)` calls the Strapi backend, which
4
+ * validates the API token (plan/quota) and returns the `SdkManifest` —
5
+ * exercise configs, engine bundle descriptor, pose-runtime payload
6
+ * descriptor, model profiles, reference movements. All server-driven
7
+ * behavior flows from this single call.
8
+ *
9
+ * Keyless mode (`apiToken` null): the backend returns the PUBLIC manifest —
10
+ * pose-runtime descriptor only, no session/engine/exercises. This is how
11
+ * keypoints-only installs bootstrap the pose estimation payload.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.ConfigureError = exports.DEFAULT_BASE_URL = exports.SDK_VERSION = exports.SDK_NAME = void 0;
15
+ exports.configure = configure;
16
+ const react_native_1 = require("react-native");
17
+ const sdkVersion_1 = require("../sdkVersion");
18
+ Object.defineProperty(exports, "SDK_VERSION", { enumerable: true, get: function () { return sdkVersion_1.SDK_VERSION; } });
19
+ exports.SDK_NAME = 'posetracker-rn-light';
20
+ /** Overridable for staging/self-hosted backends. */
21
+ exports.DEFAULT_BASE_URL = 'https://movelytics-strapi-c78a339b7070.herokuapp.com';
22
+ class ConfigureError extends Error {
23
+ constructor(code, message) {
24
+ super(message);
25
+ this.code = code;
26
+ this.name = 'ConfigureError';
27
+ }
28
+ }
29
+ exports.ConfigureError = ConfigureError;
30
+ async function configure(apiToken, options = {}) {
31
+ const baseUrl = (options.baseUrl ?? exports.DEFAULT_BASE_URL).replace(/\/$/, '');
32
+ const body = {
33
+ ...(apiToken ? { apiToken } : {}),
34
+ sdkName: exports.SDK_NAME,
35
+ sdkVersion: sdkVersion_1.SDK_VERSION,
36
+ targetPlatform: react_native_1.Platform.OS === 'ios' ? 'ios' : 'android',
37
+ poseModelProfile: options.poseModelProfile ?? 'AdaptiveChoice',
38
+ locale: options.locale,
39
+ localVersions: options.localVersions,
40
+ };
41
+ let response;
42
+ try {
43
+ response = await fetch(`${baseUrl}/api/sdk/configure`, {
44
+ method: 'POST',
45
+ headers: { 'Content-Type': 'application/json' },
46
+ body: JSON.stringify(body),
47
+ });
48
+ }
49
+ catch (err) {
50
+ throw new ConfigureError('network', `Handshake request failed: ${String(err)}`);
51
+ }
52
+ if (response.status === 401 || response.status === 403) {
53
+ // The 401 body carries `revoked: true` — the caller purges the sealed
54
+ // engine/session caches (a revoked key must not keep the engine alive).
55
+ throw new ConfigureError('invalid_token', 'Invalid, revoked or unauthorized API token.');
56
+ }
57
+ if (response.status === 429) {
58
+ throw new ConfigureError('quota_exceeded', 'API call quota exceeded for the current plan.');
59
+ }
60
+ if (!response.ok) {
61
+ throw new ConfigureError('internal', `Handshake failed with HTTP ${response.status}.`);
62
+ }
63
+ return (await response.json());
64
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Fetch a custom skeleton overlay by Strapi `api_uuid`
3
+ * (WebView `?skeleton=<uuid>` parity).
4
+ */
5
+ import type { SkeletonDefinition } from '../types/skeleton';
6
+ export interface FetchSkeletonOptions {
7
+ baseUrl?: string;
8
+ }
9
+ export declare class SkeletonFetchError extends Error {
10
+ readonly code: 'not_found' | 'network' | 'invalid';
11
+ constructor(code: 'not_found' | 'network' | 'invalid', message: string);
12
+ }
13
+ /**
14
+ * GET /api/sdk/skeleton?uuid=…
15
+ * Returns the overlay definition or throws {@link SkeletonFetchError}.
16
+ */
17
+ export declare function fetchSkeletonDefinition(uuid: string, options?: FetchSkeletonOptions): Promise<SkeletonDefinition>;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ /**
3
+ * Fetch a custom skeleton overlay by Strapi `api_uuid`
4
+ * (WebView `?skeleton=<uuid>` parity).
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.SkeletonFetchError = void 0;
8
+ exports.fetchSkeletonDefinition = fetchSkeletonDefinition;
9
+ const configure_1 = require("./configure");
10
+ class SkeletonFetchError extends Error {
11
+ constructor(code, message) {
12
+ super(message);
13
+ this.code = code;
14
+ this.name = 'SkeletonFetchError';
15
+ }
16
+ }
17
+ exports.SkeletonFetchError = SkeletonFetchError;
18
+ /**
19
+ * GET /api/sdk/skeleton?uuid=…
20
+ * Returns the overlay definition or throws {@link SkeletonFetchError}.
21
+ */
22
+ async function fetchSkeletonDefinition(uuid, options = {}) {
23
+ const id = uuid.trim();
24
+ if (!id || id === 'true' || id === 'false') {
25
+ throw new SkeletonFetchError('invalid', 'skeleton uuid is required');
26
+ }
27
+ const baseUrl = (options.baseUrl ?? configure_1.DEFAULT_BASE_URL).replace(/\/$/, '');
28
+ let response;
29
+ try {
30
+ response = await fetch(`${baseUrl}/api/sdk/skeleton?uuid=${encodeURIComponent(id)}`);
31
+ }
32
+ catch (err) {
33
+ throw new SkeletonFetchError('network', `Skeleton fetch failed: ${String(err)}`);
34
+ }
35
+ if (response.status === 404) {
36
+ throw new SkeletonFetchError('not_found', `Skeleton '${id}' not found`);
37
+ }
38
+ if (!response.ok) {
39
+ throw new SkeletonFetchError('network', `Skeleton fetch HTTP ${response.status}`);
40
+ }
41
+ const body = (await response.json());
42
+ if (!body?.keypoints || !body?.keypoint_lines || !body?.circles || !body?.lines) {
43
+ throw new SkeletonFetchError('invalid', 'Skeleton payload incomplete');
44
+ }
45
+ return {
46
+ keypoints: body.keypoints,
47
+ keypoint_lines: body.keypoint_lines,
48
+ keypoint_angles: body.keypoint_angles ?? [],
49
+ circles: body.circles,
50
+ lines: body.lines,
51
+ angles: body.angles,
52
+ };
53
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Usage tracking client — `POST /api/sdk/track`.
3
+ *
4
+ * One call per real usage: when the camera starts with the model ready
5
+ * (`camera_start`), NOT on handshake and NOT on preload. Server-side, a
6
+ * valid API key increments the same usage counters as the WebView API
7
+ * (quota-checked) and inserts a `sdk-usage-event` row with the params;
8
+ * keyless calls insert an anonymous row.
9
+ *
10
+ * Delivery rules (see ARCHITECTURE.md §Offline):
11
+ * - metered (with API key): the track call MUST succeed online — offline
12
+ * metered sessions are refused because usage cannot be counted;
13
+ * - anonymous (no key): fire-and-forget with a local retry queue, flushed
14
+ * on the next connection.
15
+ */
16
+ import { type KeyValueStore } from '../engine/EngineLoader';
17
+ export declare class TrackError extends Error {
18
+ readonly code: 'invalid_token' | 'quota_exceeded' | 'network' | 'internal';
19
+ constructor(code: 'invalid_token' | 'quota_exceeded' | 'network' | 'internal', message: string);
20
+ }
21
+ export interface TrackRequest {
22
+ event?: string;
23
+ apiToken?: string | null;
24
+ params?: Record<string, unknown>;
25
+ }
26
+ export interface TrackResponse {
27
+ tracked: boolean;
28
+ anonymous?: boolean;
29
+ monthly_usage_counter?: number;
30
+ remainingCalls?: number;
31
+ type?: string;
32
+ }
33
+ export interface UsageTrackerOptions {
34
+ baseUrl?: string;
35
+ keyValueStore?: KeyValueStore | null;
36
+ fetchFn?: typeof fetch;
37
+ }
38
+ export declare class UsageTracker {
39
+ private readonly baseUrl;
40
+ private readonly kv;
41
+ private readonly fetchFn;
42
+ constructor(options?: UsageTrackerOptions);
43
+ /**
44
+ * Metered track (API key): throws on failure — the caller refuses to start
45
+ * a metered session it cannot count.
46
+ */
47
+ trackMetered(request: TrackRequest & {
48
+ apiToken: string;
49
+ }): Promise<TrackResponse>;
50
+ /**
51
+ * Anonymous track (no key): never throws. Failed sends are queued locally
52
+ * and flushed on the next connection.
53
+ */
54
+ trackAnonymous(request?: TrackRequest): Promise<void>;
55
+ /** Send queued anonymous events (called on preload and after online sends). */
56
+ flushQueue(): Promise<void>;
57
+ private post;
58
+ private readQueue;
59
+ private writeQueue;
60
+ private enqueue;
61
+ }
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ /**
3
+ * Usage tracking client — `POST /api/sdk/track`.
4
+ *
5
+ * One call per real usage: when the camera starts with the model ready
6
+ * (`camera_start`), NOT on handshake and NOT on preload. Server-side, a
7
+ * valid API key increments the same usage counters as the WebView API
8
+ * (quota-checked) and inserts a `sdk-usage-event` row with the params;
9
+ * keyless calls insert an anonymous row.
10
+ *
11
+ * Delivery rules (see ARCHITECTURE.md §Offline):
12
+ * - metered (with API key): the track call MUST succeed online — offline
13
+ * metered sessions are refused because usage cannot be counted;
14
+ * - anonymous (no key): fire-and-forget with a local retry queue, flushed
15
+ * on the next connection.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.UsageTracker = exports.TrackError = void 0;
19
+ const react_native_1 = require("react-native");
20
+ const configure_1 = require("./configure");
21
+ const EngineLoader_1 = require("../engine/EngineLoader");
22
+ const QUEUE_KEY = 'posetracker.track.queue';
23
+ const QUEUE_LIMIT = 100;
24
+ class TrackError extends Error {
25
+ constructor(code, message) {
26
+ super(message);
27
+ this.code = code;
28
+ this.name = 'TrackError';
29
+ }
30
+ }
31
+ exports.TrackError = TrackError;
32
+ class UsageTracker {
33
+ constructor(options = {}) {
34
+ this.baseUrl = (options.baseUrl ?? configure_1.DEFAULT_BASE_URL).replace(/\/$/, '');
35
+ this.kv =
36
+ options.keyValueStore !== undefined ? options.keyValueStore : (0, EngineLoader_1.createAsyncKeyValueStore)();
37
+ this.fetchFn = options.fetchFn ?? fetch;
38
+ }
39
+ /**
40
+ * Metered track (API key): throws on failure — the caller refuses to start
41
+ * a metered session it cannot count.
42
+ */
43
+ async trackMetered(request) {
44
+ return this.post({
45
+ event: request.event ?? 'camera_start',
46
+ apiToken: request.apiToken,
47
+ platform: react_native_1.Platform.OS,
48
+ sdkVersion: configure_1.SDK_VERSION,
49
+ params: request.params,
50
+ });
51
+ }
52
+ /**
53
+ * Anonymous track (no key): never throws. Failed sends are queued locally
54
+ * and flushed on the next connection.
55
+ */
56
+ async trackAnonymous(request = {}) {
57
+ const payload = {
58
+ event: request.event ?? 'camera_start',
59
+ platform: react_native_1.Platform.OS,
60
+ sdkVersion: configure_1.SDK_VERSION,
61
+ params: request.params,
62
+ queuedAtMs: Date.now(),
63
+ };
64
+ try {
65
+ await this.post({ ...payload });
66
+ // A send just worked: opportunistically drain older queued events.
67
+ void this.flushQueue();
68
+ }
69
+ catch {
70
+ await this.enqueue(payload);
71
+ }
72
+ }
73
+ /** Send queued anonymous events (called on preload and after online sends). */
74
+ async flushQueue() {
75
+ const queue = await this.readQueue();
76
+ if (queue.length === 0) {
77
+ return;
78
+ }
79
+ const remaining = [];
80
+ for (const item of queue) {
81
+ try {
82
+ await this.post({ ...item, params: { ...(item.params ?? {}), queuedAtMs: item.queuedAtMs } });
83
+ }
84
+ catch {
85
+ remaining.push(item);
86
+ }
87
+ }
88
+ await this.writeQueue(remaining);
89
+ }
90
+ // -------------------------------------------------------------------------
91
+ async post(body) {
92
+ let response;
93
+ try {
94
+ response = await this.fetchFn(`${this.baseUrl}/api/sdk/track`, {
95
+ method: 'POST',
96
+ headers: { 'Content-Type': 'application/json' },
97
+ body: JSON.stringify(body),
98
+ });
99
+ }
100
+ catch (err) {
101
+ throw new TrackError('network', `Usage tracking request failed: ${String(err)}`);
102
+ }
103
+ if (response.status === 401 || response.status === 403) {
104
+ throw new TrackError('invalid_token', 'Invalid, revoked or unauthorized API token.');
105
+ }
106
+ if (response.status === 429) {
107
+ throw new TrackError('quota_exceeded', 'API call quota exceeded for the current plan.');
108
+ }
109
+ if (!response.ok) {
110
+ throw new TrackError('internal', `Usage tracking failed with HTTP ${response.status}.`);
111
+ }
112
+ return (await response.json());
113
+ }
114
+ async readQueue() {
115
+ try {
116
+ const raw = await this.kv?.getItem(QUEUE_KEY);
117
+ if (!raw) {
118
+ return [];
119
+ }
120
+ const parsed = JSON.parse(raw);
121
+ return Array.isArray(parsed) ? parsed : [];
122
+ }
123
+ catch {
124
+ return [];
125
+ }
126
+ }
127
+ async writeQueue(queue) {
128
+ try {
129
+ if (queue.length === 0) {
130
+ await this.kv?.removeItem(QUEUE_KEY);
131
+ }
132
+ else {
133
+ await this.kv?.setItem(QUEUE_KEY, JSON.stringify(queue.slice(-QUEUE_LIMIT)));
134
+ }
135
+ }
136
+ catch {
137
+ // Queue persistence is best-effort.
138
+ }
139
+ }
140
+ async enqueue(item) {
141
+ const queue = await this.readQueue();
142
+ queue.push(item);
143
+ await this.writeQueue(queue);
144
+ }
145
+ }
146
+ exports.UsageTracker = UsageTracker;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Inference backend abstraction.
3
+ *
4
+ * v1 ships a single implementation (`TfjsMoveNetBackend`, TF.js +
5
+ * bundleResourceIO). The interface is deliberately minimal so a native
6
+ * TFLite backend (react-native-fast-tflite + custom models downloaded from
7
+ * the manifest) can be plugged in later without touching the engine or the
8
+ * provider: anything that turns an input frame into a `Pose` qualifies.
9
+ */
10
+ import type { Pose } from '../types/pose';
11
+ import type { ModelDescriptor } from '../types/manifest';
12
+ import type { AccelerationDiagnostics } from '../types/acceleration';
13
+ /**
14
+ * Input frame handed to the backend. For the TF.js backend this is a
15
+ * `tf.Tensor3D` ([height, width, 3], int32/float32). Kept opaque here so the
16
+ * interface does not force a TF.js dependency on future native backends
17
+ * (which would receive a native frame/buffer reference instead).
18
+ */
19
+ export type PoseInputFrame = unknown;
20
+ export interface PoseBackendInitOptions {
21
+ /** Model resolved from the manifest for the active profile. */
22
+ model: ModelDescriptor;
23
+ /**
24
+ * Local filesystem path of a downloaded model archive, when the manifest
25
+ * provided one and it passed integrity validation. Undefined = use the
26
+ * model bundled in the SDK package.
27
+ */
28
+ localModelPath?: string;
29
+ }
30
+ export interface PoseBackend {
31
+ readonly name: string;
32
+ /** Prepare the runtime (TF.js ready + backend selection for tfjs). */
33
+ init(options: PoseBackendInitOptions): Promise<void>;
34
+ /**
35
+ * Run 1–2 dummy inferences so shaders/kernels are compiled before the
36
+ * first real camera frame. No-op if already warm.
37
+ */
38
+ warmup(): Promise<void>;
39
+ /** Estimate a single pose from an input frame. */
40
+ estimatePose(frame: PoseInputFrame): Promise<Pose | null>;
41
+ /** Release model and GPU resources. */
42
+ dispose(): Promise<void>;
43
+ /**
44
+ * Optional: GPU-acceleration verdict + diagnostics collected during
45
+ * `warmup()` (see docs/ANDROID_GL_ACCELERATION.md). Backends that cannot
46
+ * introspect their accelerator (or a future native TFLite backend using
47
+ * GPU/NNAPI delegates) may omit it or return null before warm-up.
48
+ */
49
+ getAcceleration?(): AccelerationDiagnostics | null;
50
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ /**
3
+ * Inference backend abstraction.
4
+ *
5
+ * v1 ships a single implementation (`TfjsMoveNetBackend`, TF.js +
6
+ * bundleResourceIO). The interface is deliberately minimal so a native
7
+ * TFLite backend (react-native-fast-tflite + custom models downloaded from
8
+ * the manifest) can be plugged in later without touching the engine or the
9
+ * provider: anything that turns an input frame into a `Pose` qualifies.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Apple Vision implementation of `PoseBackend` (iOS only).
3
+ *
4
+ * Uses `VNDetectHumanBodyPoseRequest` via a VisionCamera frame-processor
5
+ * plugin shipped in this package's `ios/` folder. Up to 19 anatomical
6
+ * joints (COCO-17 + `neck` + `root`). Primary fast path is
7
+ * `PoseCameraView` → worklet → native plugin (CVPixelBuffer, no RGB copy).
8
+ *
9
+ * Never loaded in Expo Go (`isExpoGo()` guard). On Android,
10
+ * `init()` throws {@link VisionUnavailableError} so the client can fall
11
+ * back to TFLite / TF.js when selection is `'auto'`.
12
+ */
13
+ import type { PoseBackend, PoseBackendInitOptions, PoseInputFrame } from '../PoseBackend';
14
+ import type { Pose } from '../../types/pose';
15
+ import type { AccelerationDiagnostics, AccelerationState, DiagnosticListener } from '../../types/acceleration';
16
+ import { type VisionFrameProcessorPlugin } from './optionalVision';
17
+ import { type VisionPluginResult } from './mapVisionJoints';
18
+ /** Thrown when Apple Vision cannot run (wrong platform, Expo Go, missing pod). */
19
+ export declare class VisionUnavailableError extends Error {
20
+ constructor(message: string);
21
+ }
22
+ export interface VisionPoseBackendOptions {
23
+ maxAcceptableInferenceMs?: number;
24
+ onDiagnostic?: DiagnosticListener;
25
+ onAccelerationChange?: (state: AccelerationState) => void;
26
+ }
27
+ /**
28
+ * Opaque frame accepted on the JS path: either a VisionCamera `Frame`
29
+ * (preferred — passed through to the plugin) or a pre-decoded
30
+ * {@link VisionPluginResult} (for tests / hosts that already ran Vision).
31
+ */
32
+ export type VisionPoseInput = VisionPluginResult | {
33
+ __visionCameraFrame: true;
34
+ };
35
+ export declare class VisionPoseBackend implements PoseBackend {
36
+ private readonly options;
37
+ readonly name = "apple-vision";
38
+ private plugin;
39
+ private warm;
40
+ private acceleration;
41
+ private inferenceTimesMs;
42
+ private reasons;
43
+ private readonly maxAcceptableInferenceMs;
44
+ private readonly onDiagnostic;
45
+ private readonly onAccelerationChange;
46
+ constructor(options?: VisionPoseBackendOptions);
47
+ init(_options: PoseBackendInitOptions): Promise<void>;
48
+ /**
49
+ * Vision has no separate model load — warm-up records the accelerator
50
+ * verdict. Real timings come from the first camera frames via
51
+ * {@link recordInferenceTime}.
52
+ */
53
+ warmup(): Promise<void>;
54
+ /**
55
+ * JS-path estimation. Accepts:
56
+ * - a VisionCamera `Frame` (has `.width`/`.height` and is callable via plugin);
57
+ * - a precomputed {@link VisionPluginResult} `{ joints }`.
58
+ *
59
+ * Prefer `PoseCameraView` (worklet) for production — this path is for
60
+ * TensorCamera-style hosts and unit tests.
61
+ */
62
+ estimatePose(frame: PoseInputFrame): Promise<Pose | null>;
63
+ getAcceleration(): AccelerationDiagnostics | null;
64
+ dispose(): Promise<void>;
65
+ /** Plugin handle for PoseCameraView worklets. */
66
+ getFrameProcessorPlugin(): VisionFrameProcessorPlugin | null;
67
+ /** Feed live inference timings from the camera worklet into diagnostics. */
68
+ recordInferenceTime(ms: number): void;
69
+ private setAcceleration;
70
+ private noteReason;
71
+ private requirePlugin;
72
+ }