pushkoncept-react-native 0.0.0-stage → 1.0.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/OTAUpdate.js ADDED
@@ -0,0 +1,161 @@
1
+ // React Native OTA SDK
2
+ import { Platform, DevSettings } from 'react-native';
3
+ import RNFS from 'react-native-fs';
4
+
5
+ class OTAClient {
6
+ constructor() {
7
+ this.config = {
8
+ serverUrl: null,
9
+ appId: null,
10
+ appToken: null,
11
+ apiKey: null,
12
+ channel: 'production'
13
+ };
14
+ }
15
+
16
+ /**
17
+ * Initialize the SDK with your App ID, Server URL, and API Key / App Token.
18
+ * You get the App ID and Token from your OTA Dashboard.
19
+ */
20
+ configure(options) {
21
+ if (!options.serverUrl || !options.appId) {
22
+ console.error('[OTA] Missing serverUrl or appId in configuration!');
23
+ return;
24
+ }
25
+ this.config = { ...this.config, ...options };
26
+ console.log('[OTA] Configured for App ID:', this.config.appId);
27
+ }
28
+
29
+ /**
30
+ * Returns current active OTA version if any, or the default version.
31
+ */
32
+ async getCurrentVersion(defaultVersion) {
33
+ try {
34
+ const AsyncStorage = require('@react-native-async-storage/async-storage').default;
35
+ const savedVersion = await AsyncStorage.getItem('@ota_bundle_version');
36
+ return savedVersion || defaultVersion;
37
+ } catch (e) {
38
+ return defaultVersion;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Checks the server to see if a new update is available.
44
+ * @param {string} currentVersion - The base version of your app (e.g., '1.0.0')
45
+ */
46
+ async checkForUpdate(currentVersion) {
47
+ if (!this.config.serverUrl) throw new Error('[OTA] You must call OTA.configure() first!');
48
+
49
+ try {
50
+ const activeVersion = await this.getCurrentVersion(currentVersion);
51
+ const deviceId = await this._getDeviceId();
52
+ const url = new URL(`${this.config.serverUrl}/api/update/check`);
53
+ url.searchParams.set('app_id', this.config.appId);
54
+ url.searchParams.set('version', activeVersion);
55
+ url.searchParams.set('app_version', currentVersion);
56
+ url.searchParams.set('channel', this.config.channel);
57
+ url.searchParams.set('device_id', deviceId);
58
+ url.searchParams.set('platform', Platform.OS); // 'ios' or 'android'
59
+ url.searchParams.set('os', Platform.OS); // 'ios' or 'android'
60
+
61
+ const token = this.config.apiKey || this.config.appToken || this.config.token;
62
+ if (token) url.searchParams.set('token', token);
63
+
64
+ const headers = { 'ngrok-skip-browser-warning': 'true' };
65
+ if (token) headers['x-api-key'] = token;
66
+
67
+ const response = await fetch(url.toString(), { headers });
68
+ const data = await response.json();
69
+
70
+ return data.update_available ? data : null;
71
+ } catch (error) {
72
+ console.warn('[OTA] Update check failed:', error.message);
73
+ return null;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Downloads and verifies the update bundle.
79
+ */
80
+ async downloadAndApplyUpdate(update, onProgress) {
81
+ if (!update?.bundle_url) return false;
82
+
83
+ const destPath = RNFS.DocumentDirectoryPath + '/ota_update.bundle';
84
+
85
+ try {
86
+ const result = await RNFS.downloadFile({
87
+ fromUrl: update.bundle_url,
88
+ toFile: destPath,
89
+ headers: {
90
+ 'ngrok-skip-browser-warning': 'true',
91
+ },
92
+ progress: (res) => {
93
+ const pct = Math.round(res.bytesWritten / res.contentLength * 100);
94
+ if (onProgress) onProgress(pct);
95
+ },
96
+ }).promise;
97
+
98
+ if (result.statusCode !== 200) throw new Error(`Download failed: ${result.statusCode}`);
99
+
100
+ if (update.bundle_hash) {
101
+ const fileHash = await RNFS.hash(destPath, 'sha256');
102
+ if (fileHash !== update.bundle_hash) {
103
+ await RNFS.unlink(destPath);
104
+ throw new Error('Hash mismatch - bundle corrupted');
105
+ }
106
+ }
107
+
108
+ await this._reportDownload(update.release_id, 'success');
109
+
110
+ // ✅ Bundle path save karo taake next launch par use ho
111
+ try {
112
+ const AsyncStorage = require('@react-native-async-storage/async-storage').default;
113
+ await AsyncStorage.setItem('@ota_bundle_path', destPath);
114
+ await AsyncStorage.setItem('@ota_bundle_version', update.version);
115
+ } catch(e) {}
116
+
117
+ // ✅ App reload karo naya bundle apply karne ke liye
118
+ setTimeout(() => {
119
+ try { DevSettings.reload(); } catch(e) {}
120
+ }, 500);
121
+
122
+ return true;
123
+
124
+ } catch (error) {
125
+ console.error('[OTA] Download failed:', error.message);
126
+ await this._reportDownload(update.release_id, 'failed');
127
+ try { await RNFS.unlink(destPath); } catch(e) {}
128
+ return false;
129
+ }
130
+ }
131
+
132
+ async _reportDownload(releaseId, status) {
133
+ try {
134
+ await fetch(`${this.config.serverUrl}/api/update/download-${status}`, {
135
+ method: 'POST',
136
+ headers: {
137
+ 'Content-Type': 'application/json',
138
+ 'ngrok-skip-browser-warning': 'true', // ngrok fix
139
+ },
140
+ body: JSON.stringify({ release_id: releaseId, platform: Platform.OS }),
141
+ });
142
+ } catch (e) {}
143
+ }
144
+
145
+ async _getDeviceId() {
146
+ try {
147
+ const AsyncStorage = require('@react-native-async-storage/async-storage').default;
148
+ let deviceId = await AsyncStorage.getItem('@ota_device_id');
149
+ if (!deviceId) {
150
+ deviceId = Math.random().toString(36).slice(2) + Date.now().toString(36);
151
+ await AsyncStorage.setItem('@ota_device_id', deviceId);
152
+ }
153
+ return deviceId;
154
+ } catch (e) {
155
+ return 'unknown';
156
+ }
157
+ }
158
+ }
159
+
160
+ // Export a single instance to be used throughout the app
161
+ export default new OTAClient();
package/README.md CHANGED
@@ -1,3 +1,94 @@
1
- # Temporary Holding Version
1
+ # 🚀 PushKoncept React Native SDK
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Seamless, instant Over-The-Air (OTA) updates for React Native apps. Push JavaScript bundle updates and assets directly to users' devices without waiting for App Store or Google Play approval.
4
+
5
+ ---
6
+
7
+ ## 📦 Installation
8
+
9
+ ```bash
10
+ # Using npm
11
+ npm install pushkoncept-react-native react-native-fs @react-native-async-storage/async-storage
12
+
13
+ # Or using yarn
14
+ yarn add pushkoncept-react-native react-native-fs @react-native-async-storage/async-storage
15
+ ```
16
+
17
+ > **iOS Note**: Run `cd ios && pod install` after installation.
18
+
19
+ ---
20
+
21
+ ## ⚡ Quick Start (1 Line Integration)
22
+
23
+ Wrap your root `App` component with `withPushKoncept`:
24
+
25
+ ```jsx
26
+ import React from 'react';
27
+ import { View, Text } from 'react-native';
28
+ import { withPushKoncept } from 'pushkoncept-react-native';
29
+
30
+ const App = () => {
31
+ return (
32
+ <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
33
+ <Text>Welcome to My App!</Text>
34
+ </View>
35
+ );
36
+ };
37
+
38
+ // Wrap your root component with PushKoncept
39
+ export default withPushKoncept(App, {
40
+ serverUrl: 'http://localhost:3001', // Your PushKoncept Server URL
41
+ appId: 'YOUR_APP_ID', // Found in PushKoncept Portal
42
+ appToken: 'YOUR_APP_TOKEN', // Found in App Settings
43
+ channel: 'production' // 'production' | 'staging' | 'beta'
44
+ });
45
+ ```
46
+
47
+ That's it! Your app will now:
48
+ 1. Automatically check for OTA updates on app launch and resume.
49
+ 2. Silently download and apply updates in the background.
50
+ 3. Automatically prompt and show a progress modal for mandatory / force updates.
51
+
52
+ ---
53
+
54
+ ## 🛠️ Manual / Custom Control (CodePush Style)
55
+
56
+ If you want fine-grained control over when updates are checked and applied:
57
+
58
+ ```javascript
59
+ import PushKoncept, { InstallMode } from 'pushkoncept-react-native';
60
+
61
+ // 1. Configure during app startup (e.g. index.js or App.js)
62
+ PushKoncept.configure({
63
+ serverUrl: 'http://localhost:3001',
64
+ appId: 'YOUR_APP_ID',
65
+ appToken: 'YOUR_APP_TOKEN',
66
+ channel: 'production'
67
+ });
68
+
69
+ // 2. Check and Sync
70
+ async function handleCheckUpdate() {
71
+ const update = await PushKoncept.checkForUpdate('1.0.0');
72
+ if (update) {
73
+ console.log('Update found! Version:', update.version);
74
+
75
+ await PushKoncept.downloadAndApplyUpdate(update, {
76
+ installMode: InstallMode.IMMEDIATE, // or InstallMode.ON_NEXT_RESUME
77
+ onProgress: (pct) => {
78
+ console.log(`Downloading: ${pct}%`);
79
+ }
80
+ });
81
+ }
82
+ }
83
+ ```
84
+
85
+ ---
86
+
87
+ ## 💻 Deploying Updates with PushKoncept CLI
88
+
89
+ Deploy updates directly from your terminal in 1 command without creating zip bundles manually:
90
+
91
+ ```bash
92
+ # In your React Native project root:
93
+ pushkoncept release-react --app "My App" --version 1.0.1
94
+ ```
package/index.d.ts ADDED
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+
3
+ export declare const InstallMode: {
4
+ readonly IMMEDIATE: 'IMMEDIATE';
5
+ readonly ON_NEXT_RESUME: 'ON_NEXT_RESUME';
6
+ readonly ON_NEXT_RESTART: 'ON_NEXT_RESTART';
7
+ };
8
+
9
+ export interface PushKonceptConfig {
10
+ serverUrl: string;
11
+ appId: string;
12
+ appToken?: string;
13
+ apiKey?: string;
14
+ channel?: 'production' | 'staging' | 'beta' | string;
15
+ debug?: boolean;
16
+ appVersion?: string;
17
+ }
18
+
19
+ export interface UpdateInfo {
20
+ update_available: boolean;
21
+ version: string;
22
+ build_number: number;
23
+ bundle_url: string;
24
+ bundle_hash: string;
25
+ bundle_size: number;
26
+ release_notes?: string;
27
+ rollout_percentage: number;
28
+ is_mandatory: boolean;
29
+ release_id: string;
30
+ }
31
+
32
+ export interface SyncOptions {
33
+ appVersion?: string;
34
+ installMode?: keyof typeof InstallMode;
35
+ onProgress?: (percentage: number) => void;
36
+ onMandatoryUpdate?: (update: UpdateInfo, proceed: () => void) => void;
37
+ }
38
+
39
+ export declare class PushKonceptClient {
40
+ configure(options: PushKonceptConfig): void;
41
+ getCurrentVersion(baseVersion?: string): Promise<string>;
42
+ checkForUpdate(currentAppVersion?: string): Promise<UpdateInfo | null>;
43
+ downloadAndApplyUpdate(update: UpdateInfo, options?: SyncOptions): Promise<boolean>;
44
+ sync(options?: SyncOptions): Promise<void>;
45
+ reloadApp(): void;
46
+ }
47
+
48
+ export declare const PushKoncept: PushKonceptClient;
49
+
50
+ export declare function withPushKoncept<P extends object>(
51
+ WrappedComponent: React.ComponentType<P>,
52
+ config: PushKonceptConfig
53
+ ): React.ComponentType<P>;
54
+
55
+ export default PushKoncept;
package/index.js ADDED
@@ -0,0 +1,348 @@
1
+ /**
2
+ * PushKoncept React Native SDK
3
+ * Fast, reliable Over-The-Air (OTA) updates for React Native apps.
4
+ */
5
+
6
+ import React, { useEffect, useState } from 'react';
7
+ import { Platform, DevSettings, Alert, AppState, View, Text, Modal, ActivityIndicator, StyleSheet } from 'react-native';
8
+ import RNFS from 'react-native-fs';
9
+ import AsyncStorage from '@react-native-async-storage/async-storage';
10
+
11
+ export const InstallMode = {
12
+ IMMEDIATE: 'IMMEDIATE',
13
+ ON_NEXT_RESUME: 'ON_NEXT_RESUME',
14
+ ON_NEXT_RESTART: 'ON_NEXT_RESTART'
15
+ };
16
+
17
+ class PushKonceptClient {
18
+ constructor() {
19
+ this.config = {
20
+ serverUrl: null,
21
+ appId: null,
22
+ appToken: null,
23
+ channel: 'production',
24
+ debug: false
25
+ };
26
+ this._isSyncing = false;
27
+ this._hasPendingUpdate = false;
28
+ }
29
+
30
+ log(...args) {
31
+ if (this.config.debug) {
32
+ console.log('[PushKoncept]', ...args);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Configure the SDK with your App ID and Server URL
38
+ */
39
+ configure(options = {}) {
40
+ if (!options.serverUrl || !options.appId) {
41
+ console.error('[PushKoncept] Error: serverUrl and appId are required.');
42
+ return;
43
+ }
44
+ this.config = { ...this.config, ...options };
45
+ this.log('Configured for App ID:', this.config.appId, 'Channel:', this.config.channel);
46
+ }
47
+
48
+ /**
49
+ * Get active OTA version from local storage or fallback to base
50
+ */
51
+ async getCurrentVersion(baseVersion = '1.0.0') {
52
+ try {
53
+ const savedVersion = await AsyncStorage.getItem('@pushkoncept_bundle_version');
54
+ return savedVersion || baseVersion;
55
+ } catch (e) {
56
+ return baseVersion;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Check if an update is available on the PushKoncept server
62
+ */
63
+ async checkForUpdate(currentAppVersion = '1.0.0') {
64
+ if (!this.config.serverUrl || !this.config.appId) {
65
+ throw new Error('[PushKoncept] You must call PushKoncept.configure() first.');
66
+ }
67
+
68
+ try {
69
+ const activeVersion = await this.getCurrentVersion(currentAppVersion);
70
+ const deviceId = await this._getDeviceId();
71
+
72
+ const url = new URL(`${this.config.serverUrl.replace(/\/$/, '')}/api/update/check`);
73
+ url.searchParams.set('app_id', this.config.appId);
74
+ url.searchParams.set('version', activeVersion);
75
+ url.searchParams.set('app_version', currentAppVersion);
76
+ url.searchParams.set('channel', this.config.channel || 'production');
77
+ url.searchParams.set('device_id', deviceId);
78
+ url.searchParams.set('platform', Platform.OS);
79
+
80
+ const token = this.config.appToken || this.config.apiKey || this.config.token;
81
+ if (token) {
82
+ url.searchParams.set('token', token);
83
+ }
84
+
85
+ const headers = { 'ngrok-skip-browser-warning': 'true' };
86
+ if (token) headers['x-api-key'] = token;
87
+
88
+ this.log('Checking for update at:', url.toString());
89
+ const response = await fetch(url.toString(), { headers });
90
+ const data = await response.json();
91
+
92
+ if (data && data.update_available) {
93
+ this.log('Update found! Version:', data.version, 'Mandatory:', data.is_mandatory);
94
+ return data;
95
+ }
96
+
97
+ this.log('App is up to date.');
98
+ return null;
99
+ } catch (error) {
100
+ console.warn('[PushKoncept] Update check failed:', error.message);
101
+ return null;
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Download and install a bundle update
107
+ */
108
+ async downloadAndApplyUpdate(update, options = {}) {
109
+ if (!update?.bundle_url) return false;
110
+
111
+ const onProgress = options.onProgress || (() => {});
112
+ const installMode = options.installMode || (update.is_mandatory ? InstallMode.IMMEDIATE : InstallMode.ON_NEXT_RESUME);
113
+
114
+ const destPath = `${RNFS.DocumentDirectoryPath}/pushkoncept_update.bundle`;
115
+
116
+ try {
117
+ this.log('Starting bundle download from:', update.bundle_url);
118
+ const result = await RNFS.downloadFile({
119
+ fromUrl: update.bundle_url,
120
+ toFile: destPath,
121
+ headers: { 'ngrok-skip-browser-warning': 'true' },
122
+ progress: (res) => {
123
+ const pct = Math.round((res.bytesWritten / res.contentLength) * 100);
124
+ onProgress(pct);
125
+ }
126
+ }).promise;
127
+
128
+ if (result.statusCode !== 200) {
129
+ throw new Error(`Download failed with HTTP ${result.statusCode}`);
130
+ }
131
+
132
+ // SHA-256 Hash verification
133
+ if (update.bundle_hash) {
134
+ const fileHash = await RNFS.hash(destPath, 'sha256');
135
+ if (fileHash !== update.bundle_hash) {
136
+ await RNFS.unlink(destPath).catch(() => {});
137
+ throw new Error('Hash mismatch: bundle may be corrupted.');
138
+ }
139
+ this.log('SHA-256 Hash verified successfully.');
140
+ }
141
+
142
+ // Record successful download
143
+ await this._reportDownload(update.release_id, 'success');
144
+
145
+ // Save version and path metadata
146
+ await AsyncStorage.setItem('@pushkoncept_bundle_path', destPath);
147
+ await AsyncStorage.setItem('@pushkoncept_bundle_version', update.version);
148
+ this._hasPendingUpdate = true;
149
+
150
+ this.log('Update installed successfully! InstallMode:', installMode);
151
+
152
+ if (installMode === InstallMode.IMMEDIATE) {
153
+ setTimeout(() => {
154
+ this.reloadApp();
155
+ }, 300);
156
+ }
157
+
158
+ return true;
159
+ } catch (error) {
160
+ console.error('[PushKoncept] Download failed:', error.message);
161
+ await this._reportDownload(update.release_id, 'failed');
162
+ await RNFS.unlink(destPath).catch(() => {});
163
+ return false;
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Reload app to activate new JavaScript bundle
169
+ */
170
+ reloadApp() {
171
+ try {
172
+ if (DevSettings && DevSettings.reload) {
173
+ DevSettings.reload();
174
+ }
175
+ } catch (e) {
176
+ console.warn('[PushKoncept] Unable to auto-reload. Update will apply on next restart.');
177
+ }
178
+ }
179
+
180
+ /**
181
+ * One-line CodePush style sync method
182
+ */
183
+ async sync(options = {}) {
184
+ if (this._isSyncing) return;
185
+ this._isSyncing = true;
186
+
187
+ try {
188
+ const update = await this.checkForUpdate(options.appVersion || '1.0.0');
189
+ if (!update) {
190
+ this._isSyncing = false;
191
+ return;
192
+ }
193
+
194
+ if (update.is_mandatory && options.onMandatoryUpdate) {
195
+ options.onMandatoryUpdate(update, async () => {
196
+ await this.downloadAndApplyUpdate(update, options);
197
+ });
198
+ } else {
199
+ await this.downloadAndApplyUpdate(update, options);
200
+ }
201
+ } finally {
202
+ this._isSyncing = false;
203
+ }
204
+ }
205
+
206
+ async _reportDownload(releaseId, status) {
207
+ try {
208
+ await fetch(`${this.config.serverUrl.replace(/\/$/, '')}/api/update/download-${status}`, {
209
+ method: 'POST',
210
+ headers: {
211
+ 'Content-Type': 'application/json',
212
+ 'ngrok-skip-browser-warning': 'true'
213
+ },
214
+ body: JSON.stringify({ release_id: releaseId, platform: Platform.OS })
215
+ });
216
+ } catch (e) {}
217
+ }
218
+
219
+ async _getDeviceId() {
220
+ try {
221
+ let deviceId = await AsyncStorage.getItem('@pushkoncept_device_id');
222
+ if (!deviceId) {
223
+ deviceId = 'pk_' + Math.random().toString(36).slice(2) + Date.now().toString(36);
224
+ await AsyncStorage.setItem('@pushkoncept_device_id', deviceId);
225
+ }
226
+ return deviceId;
227
+ } catch (e) {
228
+ return 'device_' + Platform.OS;
229
+ }
230
+ }
231
+ }
232
+
233
+ export const PushKoncept = new PushKonceptClient();
234
+
235
+ /**
236
+ * Higher-Order Component (HOC) to wrap your root App component
237
+ * Automatically checks for updates on launch and app resume!
238
+ */
239
+ export function withPushKoncept(WrappedComponent, config = {}) {
240
+ return function PushKonceptRoot(props) {
241
+ const [downloading, setDownloading] = useState(false);
242
+ const [progress, setProgress] = useState(0);
243
+ const [mandatory, setMandatory] = useState(false);
244
+
245
+ useEffect(() => {
246
+ PushKoncept.configure(config);
247
+
248
+ const performSync = () => {
249
+ PushKoncept.sync({
250
+ appVersion: config.appVersion || '1.0.0',
251
+ onProgress: (pct) => {
252
+ setProgress(pct);
253
+ },
254
+ onMandatoryUpdate: (update, proceed) => {
255
+ setMandatory(true);
256
+ setDownloading(true);
257
+ proceed();
258
+ }
259
+ });
260
+ };
261
+
262
+ performSync();
263
+
264
+ const subscription = AppState.addEventListener('change', (nextState) => {
265
+ if (nextState === 'active') {
266
+ performSync();
267
+ }
268
+ });
269
+
270
+ return () => {
271
+ if (subscription && subscription.remove) {
272
+ subscription.remove();
273
+ }
274
+ };
275
+ }, []);
276
+
277
+ return (
278
+ <View style={{ flex: 1 }}>
279
+ <WrappedComponent {...props} />
280
+ {downloading && (
281
+ <Modal transparent visible={downloading} animationType="fade">
282
+ <View style={styles.overlay}>
283
+ <View style={styles.dialog}>
284
+ <ActivityIndicator size="large" color="#00E5FF" />
285
+ <Text style={styles.title}>
286
+ {mandatory ? 'Critical Update Required' : 'Installing Update'}
287
+ </Text>
288
+ <Text style={styles.subtitle}>
289
+ Downloading latest improvements ({progress}%)...
290
+ </Text>
291
+ <View style={styles.progressBarBg}>
292
+ <View style={[styles.progressBarFill, { width: `${progress}%` }]} />
293
+ </View>
294
+ </View>
295
+ </View>
296
+ </Modal>
297
+ )}
298
+ </View>
299
+ );
300
+ };
301
+ }
302
+
303
+ const styles = StyleSheet.create({
304
+ overlay: {
305
+ flex: 1,
306
+ backgroundColor: 'rgba(10, 14, 26, 0.85)',
307
+ justifyContent: 'center',
308
+ alignItems: 'center',
309
+ padding: 24
310
+ },
311
+ dialog: {
312
+ width: '100%',
313
+ maxWidth: 340,
314
+ backgroundColor: '#121829',
315
+ borderRadius: 16,
316
+ padding: 24,
317
+ alignItems: 'center',
318
+ borderWidth: 1,
319
+ borderColor: 'rgba(0, 229, 255, 0.3)'
320
+ },
321
+ title: {
322
+ color: '#EAF0FF',
323
+ fontSize: 18,
324
+ fontWeight: 'bold',
325
+ marginTop: 16,
326
+ marginBottom: 6,
327
+ textAlign: 'center'
328
+ },
329
+ subtitle: {
330
+ color: '#8A94AD',
331
+ fontSize: 13,
332
+ marginBottom: 16,
333
+ textAlign: 'center'
334
+ },
335
+ progressBarBg: {
336
+ width: '100%',
337
+ height: 6,
338
+ backgroundColor: 'rgba(255, 255, 255, 0.1)',
339
+ borderRadius: 3,
340
+ overflow: 'hidden'
341
+ },
342
+ progressBarFill: {
343
+ height: '100%',
344
+ backgroundColor: '#00E5FF'
345
+ }
346
+ });
347
+
348
+ export default PushKoncept;
package/package.json CHANGED
@@ -1,6 +1,35 @@
1
1
  {
2
2
  "name": "pushkoncept-react-native",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Official PushKoncept React Native SDK for seamless Over-The-Air (OTA) updates and hotfixes",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "scripts": {
8
+ "test": "echo \"Error: no test specified\" && exit 1"
9
+ },
10
+ "keywords": [
11
+ "pushkoncept",
12
+ "ota",
13
+ "codepush",
14
+ "react-native",
15
+ "hotfix",
16
+ "live-update",
17
+ "over-the-air"
18
+ ],
19
+ "author": "PushKoncept",
20
+ "license": "MIT",
21
+ "peerDependencies": {
22
+ "react": "*",
23
+ "react-native": "*",
24
+ "react-native-fs": ">=2.18.0",
25
+ "@react-native-async-storage/async-storage": ">=1.15.0"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/pushkoncept/Push-Koncept.git",
30
+ "directory": "sdk/react-native"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ }
35
+ }