rn-remove-image-bg 0.0.10

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 (74) hide show
  1. package/NitroRnRemoveImageBg.podspec +33 -0
  2. package/README.md +386 -0
  3. package/android/CMakeLists.txt +28 -0
  4. package/android/build.gradle +142 -0
  5. package/android/fix-prefab.gradle +51 -0
  6. package/android/gradle.properties +5 -0
  7. package/android/src/main/AndroidManifest.xml +3 -0
  8. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  9. package/android/src/main/java/com/margelo/nitro/rnremoveimagebg/HybridImageBackgroundRemover.kt +189 -0
  10. package/android/src/main/java/com/margelo/nitro/rnremoveimagebg/NitroRnRemoveImageBgPackage.kt +31 -0
  11. package/app.plugin.js +12 -0
  12. package/ios/Bridge.h +8 -0
  13. package/ios/HybridImageBackgroundRemover.swift +224 -0
  14. package/ios/NitroRnRemoveImageBgOnLoad.mm +22 -0
  15. package/lib/ImageProcessing.d.ts +167 -0
  16. package/lib/ImageProcessing.js +323 -0
  17. package/lib/ImageProcessing.web.d.ts +80 -0
  18. package/lib/ImageProcessing.web.js +248 -0
  19. package/lib/__tests__/cache.test.d.ts +1 -0
  20. package/lib/__tests__/cache.test.js +87 -0
  21. package/lib/__tests__/errors.test.d.ts +1 -0
  22. package/lib/__tests__/errors.test.js +82 -0
  23. package/lib/cache.d.ts +72 -0
  24. package/lib/cache.js +228 -0
  25. package/lib/errors.d.ts +20 -0
  26. package/lib/errors.js +64 -0
  27. package/lib/index.d.ts +6 -0
  28. package/lib/index.js +9 -0
  29. package/lib/specs/Example.nitro.d.ts +0 -0
  30. package/lib/specs/Example.nitro.js +2 -0
  31. package/lib/specs/ImageBackgroundRemover.nitro.d.ts +41 -0
  32. package/lib/specs/ImageBackgroundRemover.nitro.js +1 -0
  33. package/nitro.json +17 -0
  34. package/nitrogen/generated/.gitattributes +1 -0
  35. package/nitrogen/generated/android/NitroRnRemoveImageBg+autolinking.cmake +81 -0
  36. package/nitrogen/generated/android/NitroRnRemoveImageBg+autolinking.gradle +27 -0
  37. package/nitrogen/generated/android/NitroRnRemoveImageBgOnLoad.cpp +44 -0
  38. package/nitrogen/generated/android/NitroRnRemoveImageBgOnLoad.hpp +25 -0
  39. package/nitrogen/generated/android/c++/JHybridImageBackgroundRemoverSpec.cpp +72 -0
  40. package/nitrogen/generated/android/c++/JHybridImageBackgroundRemoverSpec.hpp +65 -0
  41. package/nitrogen/generated/android/c++/JNativeRemoveBackgroundOptions.hpp +66 -0
  42. package/nitrogen/generated/android/c++/JOutputFormat.hpp +59 -0
  43. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rnremoveimagebg/HybridImageBackgroundRemoverSpec.kt +58 -0
  44. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rnremoveimagebg/NativeRemoveBackgroundOptions.kt +44 -0
  45. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rnremoveimagebg/NitroRnRemoveImageBgOnLoad.kt +35 -0
  46. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rnremoveimagebg/OutputFormat.kt +21 -0
  47. package/nitrogen/generated/ios/NitroRnRemoveImageBg+autolinking.rb +60 -0
  48. package/nitrogen/generated/ios/NitroRnRemoveImageBg-Swift-Cxx-Bridge.cpp +49 -0
  49. package/nitrogen/generated/ios/NitroRnRemoveImageBg-Swift-Cxx-Bridge.hpp +111 -0
  50. package/nitrogen/generated/ios/NitroRnRemoveImageBg-Swift-Cxx-Umbrella.hpp +51 -0
  51. package/nitrogen/generated/ios/c++/HybridImageBackgroundRemoverSpecSwift.cpp +11 -0
  52. package/nitrogen/generated/ios/c++/HybridImageBackgroundRemoverSpecSwift.hpp +82 -0
  53. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
  54. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
  55. package/nitrogen/generated/ios/swift/HybridImageBackgroundRemoverSpec.swift +56 -0
  56. package/nitrogen/generated/ios/swift/HybridImageBackgroundRemoverSpec_cxx.swift +138 -0
  57. package/nitrogen/generated/ios/swift/NativeRemoveBackgroundOptions.swift +58 -0
  58. package/nitrogen/generated/ios/swift/OutputFormat.swift +40 -0
  59. package/nitrogen/generated/shared/c++/HybridImageBackgroundRemoverSpec.cpp +21 -0
  60. package/nitrogen/generated/shared/c++/HybridImageBackgroundRemoverSpec.hpp +65 -0
  61. package/nitrogen/generated/shared/c++/NativeRemoveBackgroundOptions.hpp +84 -0
  62. package/nitrogen/generated/shared/c++/OutputFormat.hpp +76 -0
  63. package/package.json +104 -0
  64. package/react-native.config.js +16 -0
  65. package/src/ImageProcessing.ts +532 -0
  66. package/src/ImageProcessing.web.ts +342 -0
  67. package/src/__tests__/ImageProcessing.test.ts +278 -0
  68. package/src/__tests__/cache.test.ts +110 -0
  69. package/src/__tests__/errors.test.ts +117 -0
  70. package/src/cache.ts +305 -0
  71. package/src/errors.ts +93 -0
  72. package/src/index.ts +49 -0
  73. package/src/specs/Example.nitro.ts +1 -0
  74. package/src/specs/ImageBackgroundRemover.nitro.ts +49 -0
@@ -0,0 +1,87 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ // Mock expo-file-system before importing cache
3
+ vi.mock('expo-file-system/legacy', () => ({
4
+ cacheDirectory: '/mock/cache/',
5
+ getInfoAsync: vi.fn().mockResolvedValue({ exists: false }),
6
+ makeDirectoryAsync: vi.fn().mockResolvedValue(undefined),
7
+ readAsStringAsync: vi.fn().mockResolvedValue('{}'),
8
+ writeAsStringAsync: vi.fn().mockResolvedValue(undefined),
9
+ deleteAsync: vi.fn().mockResolvedValue(undefined),
10
+ }));
11
+ // Import after mocking
12
+ import { bgRemovalCache } from '../cache';
13
+ describe('BackgroundRemovalCache', () => {
14
+ beforeEach(async () => {
15
+ // Clear cache before each test
16
+ await bgRemovalCache.clear();
17
+ });
18
+ describe('hashOptions', () => {
19
+ it('should hash options to JSON string', () => {
20
+ const hash = bgRemovalCache.hashOptions({
21
+ format: 'PNG',
22
+ maxDimension: 1024,
23
+ quality: 100
24
+ });
25
+ expect(hash).toBe('{"format":"PNG","maxDimension":1024,"quality":100}');
26
+ });
27
+ it('should produce consistent hash regardless of key order', () => {
28
+ const hash1 = bgRemovalCache.hashOptions({ a: 1, b: 2 });
29
+ const hash2 = bgRemovalCache.hashOptions({ b: 2, a: 1 });
30
+ expect(hash1).toBe(hash2);
31
+ });
32
+ it('should exclude functions from hash', () => {
33
+ const hash = bgRemovalCache.hashOptions({
34
+ value: 1,
35
+ callback: () => { }
36
+ });
37
+ expect(hash).toBe('{"value":1}');
38
+ });
39
+ });
40
+ describe('size', () => {
41
+ it('should return 0 for empty cache', () => {
42
+ expect(bgRemovalCache.size).toBe(0);
43
+ });
44
+ });
45
+ describe('set and get', () => {
46
+ it('should store and retrieve cached results', async () => {
47
+ const path = 'file:///test/image.jpg';
48
+ const optionsHash = bgRemovalCache.hashOptions({ format: 'PNG' });
49
+ const resultPath = 'file:///cache/result.png';
50
+ bgRemovalCache.set(path, optionsHash, resultPath);
51
+ expect(bgRemovalCache.size).toBe(1);
52
+ });
53
+ });
54
+ describe('clear', () => {
55
+ it('should clear all cache entries', async () => {
56
+ bgRemovalCache.set('path1', 'hash1', 'result1');
57
+ bgRemovalCache.set('path2', 'hash2', 'result2');
58
+ expect(bgRemovalCache.size).toBe(2);
59
+ await bgRemovalCache.clear();
60
+ expect(bgRemovalCache.size).toBe(0);
61
+ });
62
+ });
63
+ describe('getCacheDirectory', () => {
64
+ it('should return the cache directory path', () => {
65
+ const dir = bgRemovalCache.getCacheDirectory();
66
+ expect(dir).toContain('bg-removal');
67
+ });
68
+ });
69
+ describe('configure', () => {
70
+ it('should allow updating cache configuration', () => {
71
+ bgRemovalCache.configure({
72
+ maxEntries: 100,
73
+ maxAgeMinutes: 60,
74
+ persistToDisk: true,
75
+ });
76
+ // Configuration applied - no error thrown
77
+ expect(true).toBe(true);
78
+ });
79
+ });
80
+ describe('prune', () => {
81
+ it('should return 0 when no entries are expired', () => {
82
+ bgRemovalCache.set('path', 'hash', 'result');
83
+ const removed = bgRemovalCache.prune();
84
+ expect(removed).toBe(0);
85
+ });
86
+ });
87
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,82 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { BackgroundRemovalError, wrapNativeError } from '../errors';
3
+ describe('BackgroundRemovalError', () => {
4
+ describe('constructor', () => {
5
+ it('should create an error with message and code', () => {
6
+ const error = new BackgroundRemovalError('Test message', 'INVALID_PATH');
7
+ expect(error.message).toBe('Test message');
8
+ expect(error.code).toBe('INVALID_PATH');
9
+ expect(error.name).toBe('BackgroundRemovalError');
10
+ expect(error.originalError).toBeUndefined();
11
+ });
12
+ it('should preserve original error', () => {
13
+ const originalError = new Error('Original error');
14
+ const error = new BackgroundRemovalError('Wrapped', 'UNKNOWN', originalError);
15
+ expect(error.originalError).toBe(originalError);
16
+ });
17
+ });
18
+ describe('toUserMessage', () => {
19
+ it('should return user-friendly message for INVALID_PATH', () => {
20
+ const error = new BackgroundRemovalError('test', 'INVALID_PATH');
21
+ expect(error.toUserMessage()).toBe('The image path provided is invalid.');
22
+ });
23
+ it('should return user-friendly message for FILE_NOT_FOUND', () => {
24
+ const error = new BackgroundRemovalError('test', 'FILE_NOT_FOUND');
25
+ expect(error.toUserMessage()).toBe('The image file could not be found.');
26
+ });
27
+ it('should return user-friendly message for DECODE_FAILED', () => {
28
+ const error = new BackgroundRemovalError('test', 'DECODE_FAILED');
29
+ expect(error.toUserMessage()).toBe('The image could not be read. Please ensure it is a valid image file.');
30
+ });
31
+ it('should return user-friendly message for ML_PROCESSING_FAILED', () => {
32
+ const error = new BackgroundRemovalError('test', 'ML_PROCESSING_FAILED');
33
+ expect(error.toUserMessage()).toBe('Background removal failed. Please try with a different image.');
34
+ });
35
+ it('should return user-friendly message for SAVE_FAILED', () => {
36
+ const error = new BackgroundRemovalError('test', 'SAVE_FAILED');
37
+ expect(error.toUserMessage()).toBe('Could not save the processed image.');
38
+ });
39
+ it('should return user-friendly message for INVALID_OPTIONS', () => {
40
+ const error = new BackgroundRemovalError('test', 'INVALID_OPTIONS');
41
+ expect(error.toUserMessage()).toBe('Invalid options provided for background removal.');
42
+ });
43
+ it('should return default message for UNKNOWN', () => {
44
+ const error = new BackgroundRemovalError('test', 'UNKNOWN');
45
+ expect(error.toUserMessage()).toBe('An unexpected error occurred during background removal.');
46
+ });
47
+ });
48
+ });
49
+ describe('wrapNativeError', () => {
50
+ it('should return same error if already BackgroundRemovalError', () => {
51
+ const error = new BackgroundRemovalError('test', 'INVALID_PATH');
52
+ const wrapped = wrapNativeError(error);
53
+ expect(wrapped).toBe(error);
54
+ });
55
+ it('should wrap string error', () => {
56
+ const wrapped = wrapNativeError('Something went wrong');
57
+ expect(wrapped).toBeInstanceOf(BackgroundRemovalError);
58
+ expect(wrapped.message).toBe('Something went wrong');
59
+ expect(wrapped.code).toBe('UNKNOWN');
60
+ });
61
+ it('should detect FILE_NOT_FOUND from message', () => {
62
+ const error = new Error('File does not exist at path');
63
+ const wrapped = wrapNativeError(error);
64
+ expect(wrapped.code).toBe('FILE_NOT_FOUND');
65
+ expect(wrapped.originalError).toBe(error);
66
+ });
67
+ it('should detect DECODE_FAILED from message', () => {
68
+ const error = new Error('Could not decode image');
69
+ const wrapped = wrapNativeError(error);
70
+ expect(wrapped.code).toBe('DECODE_FAILED');
71
+ });
72
+ it('should detect ML_PROCESSING_FAILED from message', () => {
73
+ const error = new Error('Failed to generate mask');
74
+ const wrapped = wrapNativeError(error);
75
+ expect(wrapped.code).toBe('ML_PROCESSING_FAILED');
76
+ });
77
+ it('should detect SAVE_FAILED from message', () => {
78
+ const error = new Error('Could not save output file');
79
+ const wrapped = wrapNativeError(error);
80
+ expect(wrapped.code).toBe('SAVE_FAILED');
81
+ });
82
+ });
package/lib/cache.d.ts ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Configuration for the background removal cache
3
+ */
4
+ export interface CacheConfig {
5
+ /** Maximum number of entries in memory (default: 50) */
6
+ maxEntries?: number;
7
+ /** Maximum age of cache entries in minutes (default: 30) */
8
+ maxAgeMinutes?: number;
9
+ /** Enable disk persistence (default: false) */
10
+ persistToDisk?: boolean;
11
+ /** Custom cache directory (default: FileSystem.cacheDirectory + 'bg-removal/') */
12
+ cacheDirectory?: string;
13
+ }
14
+ /**
15
+ * LRU cache for background removal results with optional disk persistence
16
+ */
17
+ declare class BackgroundRemovalCache {
18
+ private cache;
19
+ private _maxEntries;
20
+ private _maxAgeMs;
21
+ private persistToDisk;
22
+ private cacheDirectory;
23
+ private initialized;
24
+ constructor(config?: CacheConfig);
25
+ /**
26
+ * Configure cache settings
27
+ */
28
+ configure(config: CacheConfig): void;
29
+ /**
30
+ * Initialize disk cache (load manifest if exists)
31
+ */
32
+ initialize(): Promise<void>;
33
+ /**
34
+ * Save cache manifest to disk
35
+ */
36
+ private saveManifest;
37
+ /**
38
+ * Generate a cache key from path and options
39
+ */
40
+ private generateKey;
41
+ /**
42
+ * Hash options object to string for cache key
43
+ */
44
+ hashOptions(options: Record<string, unknown>): string;
45
+ /**
46
+ * Get cached result if valid
47
+ */
48
+ get(path: string, optionsHash: string): Promise<string | null>;
49
+ /**
50
+ * Store result in cache
51
+ */
52
+ set(path: string, optionsHash: string, resultPath: string): void;
53
+ /**
54
+ * Clear all cached entries
55
+ * @param deleteFiles - Also delete cached files from disk (default: false)
56
+ */
57
+ clear(deleteFiles?: boolean): Promise<void>;
58
+ /**
59
+ * Get current cache size
60
+ */
61
+ get size(): number;
62
+ /**
63
+ * Remove expired entries
64
+ */
65
+ prune(): number;
66
+ /**
67
+ * Get cache directory path
68
+ */
69
+ getCacheDirectory(): string;
70
+ }
71
+ export declare const bgRemovalCache: BackgroundRemovalCache;
72
+ export {};
package/lib/cache.js ADDED
@@ -0,0 +1,228 @@
1
+ import * as FileSystem from 'expo-file-system/legacy';
2
+ const MANIFEST_FILENAME = 'cache-manifest.json';
3
+ const CACHE_VERSION = 1;
4
+ /**
5
+ * LRU cache for background removal results with optional disk persistence
6
+ */
7
+ class BackgroundRemovalCache {
8
+ cache = new Map();
9
+ _maxEntries;
10
+ _maxAgeMs;
11
+ persistToDisk;
12
+ cacheDirectory;
13
+ initialized = false;
14
+ constructor(config = {}) {
15
+ this._maxEntries = config.maxEntries ?? 50;
16
+ this._maxAgeMs = (config.maxAgeMinutes ?? 30) * 60 * 1000;
17
+ this.persistToDisk = config.persistToDisk ?? false;
18
+ this.cacheDirectory =
19
+ config.cacheDirectory ?? `${FileSystem.cacheDirectory}bg-removal/`;
20
+ }
21
+ /**
22
+ * Configure cache settings
23
+ */
24
+ configure(config) {
25
+ if (config.maxEntries !== undefined) {
26
+ this._maxEntries = config.maxEntries;
27
+ }
28
+ if (config.maxAgeMinutes !== undefined) {
29
+ this._maxAgeMs = config.maxAgeMinutes * 60 * 1000;
30
+ }
31
+ if (config.persistToDisk !== undefined) {
32
+ this.persistToDisk = config.persistToDisk;
33
+ }
34
+ if (config.cacheDirectory !== undefined) {
35
+ this.cacheDirectory = config.cacheDirectory;
36
+ }
37
+ }
38
+ /**
39
+ * Initialize disk cache (load manifest if exists)
40
+ */
41
+ async initialize() {
42
+ if (this.initialized || !this.persistToDisk)
43
+ return;
44
+ try {
45
+ // Ensure cache directory exists
46
+ const dirInfo = await FileSystem.getInfoAsync(this.cacheDirectory);
47
+ if (!dirInfo.exists) {
48
+ await FileSystem.makeDirectoryAsync(this.cacheDirectory, {
49
+ intermediates: true,
50
+ });
51
+ }
52
+ // Load manifest if exists
53
+ const manifestPath = `${this.cacheDirectory}${MANIFEST_FILENAME}`;
54
+ const manifestInfo = await FileSystem.getInfoAsync(manifestPath);
55
+ if (manifestInfo.exists) {
56
+ const manifestJson = await FileSystem.readAsStringAsync(manifestPath);
57
+ const manifest = JSON.parse(manifestJson);
58
+ if (manifest.version === CACHE_VERSION) {
59
+ // Validate and load entries
60
+ for (const [key, entry] of Object.entries(manifest.entries)) {
61
+ const fileInfo = await FileSystem.getInfoAsync(entry.resultPath);
62
+ if (fileInfo.exists) {
63
+ this.cache.set(key, entry);
64
+ }
65
+ }
66
+ }
67
+ }
68
+ }
69
+ catch (error) {
70
+ console.warn('[rn-remove-image-bg] Failed to load cache manifest:', error);
71
+ }
72
+ this.initialized = true;
73
+ }
74
+ /**
75
+ * Save cache manifest to disk
76
+ */
77
+ async saveManifest() {
78
+ if (!this.persistToDisk)
79
+ return;
80
+ try {
81
+ const manifest = {
82
+ version: CACHE_VERSION,
83
+ entries: Object.fromEntries(this.cache.entries()),
84
+ };
85
+ const manifestPath = `${this.cacheDirectory}${MANIFEST_FILENAME}`;
86
+ await FileSystem.writeAsStringAsync(manifestPath, JSON.stringify(manifest, null, 2));
87
+ }
88
+ catch (error) {
89
+ console.warn('[rn-remove-image-bg] Failed to save cache manifest:', error);
90
+ }
91
+ }
92
+ /**
93
+ * Generate a cache key from path and options
94
+ */
95
+ generateKey(path, optionsHash) {
96
+ return `${path}::${optionsHash}`;
97
+ }
98
+ /**
99
+ * Hash options object to string for cache key
100
+ */
101
+ hashOptions(options) {
102
+ const sorted = Object.keys(options)
103
+ .sort()
104
+ .reduce((acc, key) => {
105
+ const value = options[key];
106
+ // Exclude functions from hash
107
+ if (typeof value !== 'function') {
108
+ acc[key] = value;
109
+ }
110
+ return acc;
111
+ }, {});
112
+ return JSON.stringify(sorted);
113
+ }
114
+ /**
115
+ * Get cached result if valid
116
+ */
117
+ async get(path, optionsHash) {
118
+ await this.initialize();
119
+ const key = this.generateKey(path, optionsHash);
120
+ const entry = this.cache.get(key);
121
+ if (!entry) {
122
+ return null;
123
+ }
124
+ // Check if expired
125
+ if (Date.now() - entry.timestamp > this._maxAgeMs) {
126
+ this.cache.delete(key);
127
+ this.saveManifest();
128
+ return null;
129
+ }
130
+ // Verify result file still exists
131
+ try {
132
+ const info = await FileSystem.getInfoAsync(entry.resultPath);
133
+ if (!info.exists) {
134
+ this.cache.delete(key);
135
+ this.saveManifest();
136
+ return null;
137
+ }
138
+ }
139
+ catch {
140
+ this.cache.delete(key);
141
+ this.saveManifest();
142
+ return null;
143
+ }
144
+ // Move to end (most recently used)
145
+ this.cache.delete(key);
146
+ this.cache.set(key, entry);
147
+ return entry.resultPath;
148
+ }
149
+ /**
150
+ * Store result in cache
151
+ */
152
+ set(path, optionsHash, resultPath) {
153
+ const key = this.generateKey(path, optionsHash);
154
+ // Evict oldest entries if at capacity
155
+ while (this.cache.size >= this._maxEntries) {
156
+ const oldestKey = this.cache.keys().next().value;
157
+ if (oldestKey) {
158
+ this.cache.delete(oldestKey);
159
+ }
160
+ }
161
+ this.cache.set(key, {
162
+ originalPath: path,
163
+ resultPath,
164
+ timestamp: Date.now(),
165
+ optionsHash,
166
+ });
167
+ this.saveManifest();
168
+ }
169
+ /**
170
+ * Clear all cached entries
171
+ * @param deleteFiles - Also delete cached files from disk (default: false)
172
+ */
173
+ async clear(deleteFiles = false) {
174
+ if (deleteFiles && this.persistToDisk) {
175
+ try {
176
+ // Delete all cached result files
177
+ for (const entry of this.cache.values()) {
178
+ try {
179
+ await FileSystem.deleteAsync(entry.resultPath, {
180
+ idempotent: true,
181
+ });
182
+ }
183
+ catch {
184
+ // Ignore individual file deletion errors
185
+ }
186
+ }
187
+ // Delete manifest
188
+ const manifestPath = `${this.cacheDirectory}${MANIFEST_FILENAME}`;
189
+ await FileSystem.deleteAsync(manifestPath, { idempotent: true });
190
+ }
191
+ catch (error) {
192
+ console.warn('[rn-remove-image-bg] Error clearing cache files:', error);
193
+ }
194
+ }
195
+ this.cache.clear();
196
+ }
197
+ /**
198
+ * Get current cache size
199
+ */
200
+ get size() {
201
+ return this.cache.size;
202
+ }
203
+ /**
204
+ * Remove expired entries
205
+ */
206
+ prune() {
207
+ const now = Date.now();
208
+ let removed = 0;
209
+ for (const [key, entry] of this.cache.entries()) {
210
+ if (now - entry.timestamp > this._maxAgeMs) {
211
+ this.cache.delete(key);
212
+ removed++;
213
+ }
214
+ }
215
+ if (removed > 0) {
216
+ this.saveManifest();
217
+ }
218
+ return removed;
219
+ }
220
+ /**
221
+ * Get cache directory path
222
+ */
223
+ getCacheDirectory() {
224
+ return this.cacheDirectory;
225
+ }
226
+ }
227
+ // Export singleton instance
228
+ export const bgRemovalCache = new BackgroundRemovalCache();
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Error codes for background removal operations
3
+ */
4
+ export type BackgroundRemovalErrorCode = 'INVALID_PATH' | 'FILE_NOT_FOUND' | 'DECODE_FAILED' | 'ML_PROCESSING_FAILED' | 'SAVE_FAILED' | 'INVALID_OPTIONS' | 'UNKNOWN';
5
+ /**
6
+ * Custom error class for background removal operations
7
+ */
8
+ export declare class BackgroundRemovalError extends Error {
9
+ readonly code: BackgroundRemovalErrorCode;
10
+ readonly originalError?: Error;
11
+ constructor(message: string, code: BackgroundRemovalErrorCode, originalError?: Error);
12
+ /**
13
+ * Create a user-friendly error message
14
+ */
15
+ toUserMessage(): string;
16
+ }
17
+ /**
18
+ * Helper to wrap native errors with proper typing
19
+ */
20
+ export declare function wrapNativeError(error: unknown): BackgroundRemovalError;
package/lib/errors.js ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Custom error class for background removal operations
3
+ */
4
+ export class BackgroundRemovalError extends Error {
5
+ code;
6
+ originalError;
7
+ constructor(message, code, originalError) {
8
+ super(message);
9
+ this.name = 'BackgroundRemovalError';
10
+ this.code = code;
11
+ this.originalError = originalError;
12
+ // Maintain proper stack trace in V8 environments
13
+ if ('captureStackTrace' in Error) {
14
+ Error.captureStackTrace(this, BackgroundRemovalError);
15
+ }
16
+ }
17
+ /**
18
+ * Create a user-friendly error message
19
+ */
20
+ toUserMessage() {
21
+ switch (this.code) {
22
+ case 'INVALID_PATH':
23
+ return 'The image path provided is invalid.';
24
+ case 'FILE_NOT_FOUND':
25
+ return 'The image file could not be found.';
26
+ case 'DECODE_FAILED':
27
+ return 'The image could not be read. Please ensure it is a valid image file.';
28
+ case 'ML_PROCESSING_FAILED':
29
+ return 'Background removal failed. Please try with a different image.';
30
+ case 'SAVE_FAILED':
31
+ return 'Could not save the processed image.';
32
+ case 'INVALID_OPTIONS':
33
+ return 'Invalid options provided for background removal.';
34
+ default:
35
+ return 'An unexpected error occurred during background removal.';
36
+ }
37
+ }
38
+ }
39
+ /**
40
+ * Helper to wrap native errors with proper typing
41
+ */
42
+ export function wrapNativeError(error) {
43
+ if (error instanceof BackgroundRemovalError) {
44
+ return error;
45
+ }
46
+ const message = error instanceof Error ? error.message : String(error);
47
+ const originalError = error instanceof Error ? error : undefined;
48
+ // Try to determine error code from message
49
+ if (message.includes('does not exist') || message.includes('not found')) {
50
+ return new BackgroundRemovalError(message, 'FILE_NOT_FOUND', originalError);
51
+ }
52
+ if (message.includes('decode') || message.includes('load image')) {
53
+ return new BackgroundRemovalError(message, 'DECODE_FAILED', originalError);
54
+ }
55
+ if (message.includes('mask') ||
56
+ message.includes('segment') ||
57
+ message.includes('ML')) {
58
+ return new BackgroundRemovalError(message, 'ML_PROCESSING_FAILED', originalError);
59
+ }
60
+ if (message.includes('save') || message.includes('write')) {
61
+ return new BackgroundRemovalError(message, 'SAVE_FAILED', originalError);
62
+ }
63
+ return new BackgroundRemovalError(message, 'UNKNOWN', originalError);
64
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { compressImage, generateThumbhash, removeBgImage, removeBackground, clearCache, getCacheSize, onLowMemory, configureCache, getCacheDirectory } from './ImageProcessing';
2
+ import type { CompressImageOptions, GenerateThumbhashOptions, RemoveBgImageOptions, OutputFormat } from './ImageProcessing';
3
+ import { BackgroundRemovalError, type BackgroundRemovalErrorCode } from './errors';
4
+ import type { CacheConfig } from './cache';
5
+ export { compressImage, generateThumbhash, removeBgImage, removeBackground, clearCache, getCacheSize, onLowMemory, configureCache, getCacheDirectory, BackgroundRemovalError, };
6
+ export type { CompressImageOptions, GenerateThumbhashOptions, RemoveBgImageOptions, OutputFormat, BackgroundRemovalErrorCode, CacheConfig, };
package/lib/index.js ADDED
@@ -0,0 +1,9 @@
1
+ import { compressImage, generateThumbhash, removeBgImage, removeBackground, clearCache, getCacheSize, onLowMemory, configureCache, getCacheDirectory, } from './ImageProcessing';
2
+ import { BackgroundRemovalError, } from './errors';
3
+ export {
4
+ // Functions
5
+ compressImage, generateThumbhash, removeBgImage, removeBackground, clearCache, getCacheSize,
6
+ // Memory management
7
+ onLowMemory, configureCache, getCacheDirectory,
8
+ // Errors
9
+ BackgroundRemovalError, };
File without changes
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ // TODO: Export specs that extend HybridObject<...> here
@@ -0,0 +1,41 @@
1
+ import type { HybridObject } from 'react-native-nitro-modules';
2
+ /**
3
+ * Output format for processed images
4
+ */
5
+ export type OutputFormat = 'PNG' | 'WEBP';
6
+ /**
7
+ * Native options for background removal
8
+ */
9
+ export interface NativeRemoveBackgroundOptions {
10
+ /**
11
+ * Maximum dimension (width or height) for processing
12
+ * Larger images will be downsampled for better performance
13
+ * Default: 2048
14
+ */
15
+ maxDimension: number;
16
+ /**
17
+ * Output image format
18
+ * PNG: Lossless, larger file size
19
+ * WEBP: Smaller file size, good quality
20
+ * Default: PNG
21
+ */
22
+ format: OutputFormat;
23
+ /**
24
+ * Quality for WEBP format (0-100)
25
+ * Ignored for PNG format
26
+ * Default: 100
27
+ */
28
+ quality: number;
29
+ }
30
+ export interface ImageBackgroundRemover extends HybridObject<{
31
+ ios: 'swift';
32
+ android: 'kotlin';
33
+ }> {
34
+ /**
35
+ * Remove background from an image
36
+ * @param imagePath - File path or file:// URI to the source image
37
+ * @param options - Processing options
38
+ * @returns Promise resolving to the output file path
39
+ */
40
+ removeBackground(imagePath: string, options: NativeRemoveBackgroundOptions): Promise<string>;
41
+ }
@@ -0,0 +1 @@
1
+ export {};
package/nitro.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://nitro.margelo.com/nitro.schema.json",
3
+ "cxxNamespace": ["rnremoveimagebg"],
4
+ "ios": {
5
+ "iosModuleName": "NitroRnRemoveImageBg"
6
+ },
7
+ "android": {
8
+ "androidNamespace": ["rnremoveimagebg"],
9
+ "androidCxxLibName": "NitroRnRemoveImageBg"
10
+ },
11
+ "autolinking": {
12
+ "ImageBackgroundRemover": {
13
+ "kotlin": "HybridImageBackgroundRemover"
14
+ }
15
+ },
16
+ "ignorePaths": ["**/node_modules"]
17
+ }
@@ -0,0 +1 @@
1
+ ** linguist-generated=true