react-native-image-compression-kit 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.
- package/LICENSE +21 -0
- package/README.md +562 -0
- package/android/build.gradle +63 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/imagecompressionkit/ImageCompressionKitModule.kt +1127 -0
- package/android/src/main/java/com/imagecompressionkit/ImageCompressionKitPackage.kt +33 -0
- package/android/src/main/java/com/imagecompressionkit/ImageCompressionOutput.kt +396 -0
- package/android/src/main/java/com/imagecompressionkit/JpegExifMetadata.kt +233 -0
- package/ios/RCTImageCompressionKit.h +10 -0
- package/ios/RCTImageCompressionKit.mm +72 -0
- package/lib/NativeImageCompressionKit.d.ts +55 -0
- package/lib/NativeImageCompressionKit.d.ts.map +1 -0
- package/lib/NativeImageCompressionKit.js +5 -0
- package/lib/NativeImageCompressionKit.js.map +1 -0
- package/lib/api.d.ts +4 -0
- package/lib/api.d.ts.map +1 -0
- package/lib/api.js +25 -0
- package/lib/api.js.map +1 -0
- package/lib/errors.d.ts +9 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +53 -0
- package/lib/errors.js.map +1 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +14 -0
- package/lib/index.js.map +1 -0
- package/lib/nativeModule.d.ts +7 -0
- package/lib/nativeModule.d.ts.map +1 -0
- package/lib/nativeModule.js +73 -0
- package/lib/nativeModule.js.map +1 -0
- package/lib/types.d.ts +59 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +23 -0
- package/lib/types.js.map +1 -0
- package/lib/validation.d.ts +3 -0
- package/lib/validation.d.ts.map +1 -0
- package/lib/validation.js +108 -0
- package/lib/validation.js.map +1 -0
- package/package.json +106 -0
- package/react-native-image-compression-kit.podspec +21 -0
- package/react-native.config.js +12 -0
- package/src/NativeImageCompressionKit.ts +81 -0
- package/src/api.ts +28 -0
- package/src/errors.ts +91 -0
- package/src/index.ts +25 -0
- package/src/nativeModule.ts +130 -0
- package/src/types.ts +88 -0
- package/src/validation.ts +181 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import {
|
|
2
|
+
METADATA_POLICIES,
|
|
3
|
+
OUTPUT_FORMATS,
|
|
4
|
+
RESIZE_MODES,
|
|
5
|
+
type CompressionOptions,
|
|
6
|
+
type MetadataPolicy,
|
|
7
|
+
type NormalizedCompressionOptions,
|
|
8
|
+
type OutputFormat,
|
|
9
|
+
type ResizeMode,
|
|
10
|
+
} from './types';
|
|
11
|
+
import { ImageCompressionKitError } from './errors';
|
|
12
|
+
|
|
13
|
+
export function normalizeCompressionOptions(
|
|
14
|
+
options: CompressionOptions
|
|
15
|
+
): NormalizedCompressionOptions {
|
|
16
|
+
if (!isRecord(options)) {
|
|
17
|
+
throw invalidOptions('Compression options must be an object.');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const source = options.source;
|
|
21
|
+
|
|
22
|
+
if (!isRecord(source)) {
|
|
23
|
+
throw invalidOptions('Compression source must be an object.');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const uri = source.uri;
|
|
27
|
+
|
|
28
|
+
if (typeof uri !== 'string' || uri.trim().length === 0) {
|
|
29
|
+
throw invalidOptions('Compression source.uri must be a non-empty string.');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (isRemoteOrInlineUri(uri)) {
|
|
33
|
+
throw new ImageCompressionKitError(
|
|
34
|
+
'ERR_UNSUPPORTED_SOURCE',
|
|
35
|
+
'Compression source.uri must point to a local image. Remote URLs and inline data URIs are outside this package scope.'
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!isRecord(options.output)) {
|
|
40
|
+
throw invalidOptions('Compression output must be an object.');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const format = options.output.format;
|
|
44
|
+
|
|
45
|
+
if (!isOutputFormat(format)) {
|
|
46
|
+
throw invalidOptions(
|
|
47
|
+
`Compression output.format must be one of: ${OUTPUT_FORMATS.join(', ')}.`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const output: NormalizedCompressionOptions['output'] = { format };
|
|
52
|
+
|
|
53
|
+
if (options.output.quality !== undefined) {
|
|
54
|
+
assertIntegerInRange(
|
|
55
|
+
options.output.quality,
|
|
56
|
+
'Compression output.quality',
|
|
57
|
+
0,
|
|
58
|
+
100
|
|
59
|
+
);
|
|
60
|
+
output.quality = options.output.quality;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (options.output.maxBytes !== undefined) {
|
|
64
|
+
assertPositiveInteger(options.output.maxBytes, 'Compression output.maxBytes');
|
|
65
|
+
output.maxBytes = options.output.maxBytes;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const metadata = options.metadata ?? 'safe';
|
|
69
|
+
|
|
70
|
+
if (!isMetadataPolicy(metadata)) {
|
|
71
|
+
throw invalidOptions(
|
|
72
|
+
`Compression metadata must be one of: ${METADATA_POLICIES.join(', ')}.`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const normalized: NormalizedCompressionOptions = {
|
|
77
|
+
source: { uri },
|
|
78
|
+
output,
|
|
79
|
+
metadata,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
if (options.resize !== undefined) {
|
|
83
|
+
if (!isRecord(options.resize)) {
|
|
84
|
+
throw invalidOptions('Compression resize must be an object.');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const { maxWidth, maxHeight } = options.resize;
|
|
88
|
+
|
|
89
|
+
if (maxWidth === undefined && maxHeight === undefined) {
|
|
90
|
+
throw invalidOptions(
|
|
91
|
+
'Compression resize must include maxWidth, maxHeight, or both.'
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (maxWidth !== undefined) {
|
|
96
|
+
assertPositiveInteger(maxWidth, 'Compression resize.maxWidth');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (maxHeight !== undefined) {
|
|
100
|
+
assertPositiveInteger(maxHeight, 'Compression resize.maxHeight');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const mode = options.resize.mode ?? 'contain';
|
|
104
|
+
|
|
105
|
+
if (!isResizeMode(mode)) {
|
|
106
|
+
throw invalidOptions(
|
|
107
|
+
`Compression resize.mode must be one of: ${RESIZE_MODES.join(', ')}.`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
normalized.resize = {
|
|
112
|
+
...(maxWidth !== undefined ? { maxWidth } : {}),
|
|
113
|
+
...(maxHeight !== undefined ? { maxHeight } : {}),
|
|
114
|
+
mode,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return normalized;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function invalidOptions(message: string): ImageCompressionKitError {
|
|
122
|
+
return new ImageCompressionKitError('ERR_INVALID_OPTIONS', message);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
126
|
+
return typeof value === 'object' && value !== null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function isOutputFormat(value: unknown): value is OutputFormat {
|
|
130
|
+
return (
|
|
131
|
+
typeof value === 'string' &&
|
|
132
|
+
(OUTPUT_FORMATS as readonly string[]).includes(value)
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function isMetadataPolicy(value: unknown): value is MetadataPolicy {
|
|
137
|
+
return (
|
|
138
|
+
typeof value === 'string' &&
|
|
139
|
+
(METADATA_POLICIES as readonly string[]).includes(value)
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function isResizeMode(value: unknown): value is ResizeMode {
|
|
144
|
+
return (
|
|
145
|
+
typeof value === 'string' &&
|
|
146
|
+
(RESIZE_MODES as readonly string[]).includes(value)
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function isRemoteOrInlineUri(uri: string): boolean {
|
|
151
|
+
return /^(https?:|data:)/i.test(uri.trim());
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function assertIntegerInRange(
|
|
155
|
+
value: unknown,
|
|
156
|
+
label: string,
|
|
157
|
+
min: number,
|
|
158
|
+
max: number
|
|
159
|
+
): asserts value is number {
|
|
160
|
+
if (
|
|
161
|
+
typeof value !== 'number' ||
|
|
162
|
+
!Number.isInteger(value) ||
|
|
163
|
+
value < min ||
|
|
164
|
+
value > max
|
|
165
|
+
) {
|
|
166
|
+
throw invalidOptions(`${label} must be an integer from ${min} to ${max}.`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function assertPositiveInteger(
|
|
171
|
+
value: unknown,
|
|
172
|
+
label: string
|
|
173
|
+
): asserts value is number {
|
|
174
|
+
if (
|
|
175
|
+
typeof value !== 'number' ||
|
|
176
|
+
!Number.isInteger(value) ||
|
|
177
|
+
value <= 0
|
|
178
|
+
) {
|
|
179
|
+
throw invalidOptions(`${label} must be a positive integer.`);
|
|
180
|
+
}
|
|
181
|
+
}
|