react-native-update 9.0.0-beta.0 → 9.0.0-beta.1

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.
@@ -1,6 +1,6 @@
1
1
  let currentEndpoint = 'https://update.react-native.cn/api';
2
2
 
3
- function ping(url, rejectImmediate) {
3
+ function ping(url: string, rejectImmediate?: boolean) {
4
4
  return new Promise((resolve, reject) => {
5
5
  const xhr = new XMLHttpRequest();
6
6
  xhr.onreadystatechange = (e) => {
@@ -20,12 +20,12 @@ function ping(url, rejectImmediate) {
20
20
  });
21
21
  }
22
22
 
23
- function logger(...args) {
24
- // console.warn('pushy', ...args);
23
+ function logger(...args: any[]) {
24
+ console.log('Pushy: ', ...args);
25
25
  }
26
26
 
27
- let backupEndpoints = [];
28
- let backupEndpointsQueryUrl =
27
+ let backupEndpoints: string[] = [];
28
+ let backupEndpointsQueryUrl: string | null =
29
29
  'https://cdn.jsdelivr.net/gh/reactnativecn/react-native-pushy@master/endpoints.json';
30
30
 
31
31
  export async function tryBackupEndpoints() {
@@ -34,10 +34,10 @@ export async function tryBackupEndpoints() {
34
34
  }
35
35
  try {
36
36
  await ping(getStatusUrl(), true);
37
- logger('current endpoint ok');
37
+ logger('current endpoint ok', currentEndpoint);
38
38
  return;
39
39
  } catch (e) {
40
- logger('current endpoint failed');
40
+ logger('current endpoint failed', currentEndpoint);
41
41
  }
42
42
  if (!backupEndpoints.length && backupEndpointsQueryUrl) {
43
43
  try {
@@ -76,11 +76,21 @@ export function getCheckUrl(APPKEY, endpoint = currentEndpoint) {
76
76
  return `${endpoint}/checkUpdate/${APPKEY}`;
77
77
  }
78
78
 
79
- export function getReportUrl(endpoint = currentEndpoint) {
80
- return `${endpoint}/report`;
81
- }
82
-
83
- export function setCustomEndpoints({ main, backups, backupQueryUrl }) {
79
+ /**
80
+ * @param {string} main - The main api endpoint
81
+ * @param {string[]} [backups] - The back up endpoints.
82
+ * @param {string} [backupQueryUrl] - An url that return a json file containing an array of endpoint.
83
+ * like: ["https://backup.api/1", "https://backup.api/2"]
84
+ */
85
+ export function setCustomEndpoints({
86
+ main,
87
+ backups,
88
+ backupQueryUrl,
89
+ }: {
90
+ main: string;
91
+ backups?: string[];
92
+ backupQueryUrl?: string;
93
+ }) {
84
94
  currentEndpoint = main;
85
95
  backupEndpointsQueryUrl = null;
86
96
  if (Array.isArray(backups) && backups.length > 0) {
package/lib/index.web.js CHANGED
@@ -15,3 +15,4 @@ export const downloadAndInstallApk = noop;
15
15
  export const setCustomEndpoints = noop;
16
16
  export const getCurrentVersionInfo = noop;
17
17
  export const simpleUpdate = noop;
18
+ export const onEvents = noop;
@@ -2,7 +2,6 @@ import {
2
2
  tryBackupEndpoints,
3
3
  getCheckUrl,
4
4
  setCustomEndpoints,
5
- getReportUrl,
6
5
  } from './endpoint';
7
6
  import {
8
7
  NativeEventEmitter,
@@ -10,6 +9,12 @@ import {
10
9
  Platform,
11
10
  PermissionsAndroid,
12
11
  } from 'react-native';
12
+ import {
13
+ EventType,
14
+ ProgressData,
15
+ UpdateAvailableResult,
16
+ UpdateEventsListener,
17
+ } from './type';
13
18
  export { setCustomEndpoints };
14
19
  const {
15
20
  version: v,
@@ -45,15 +50,19 @@ if (Platform.OS === 'android' && !PushyConstants.isUsingBundleUrl) {
45
50
  );
46
51
  }
47
52
 
48
- function setLocalHashInfo(hash, info) {
53
+ function setLocalHashInfo(hash: string, info: Record<string, any>) {
49
54
  PushyModule.setLocalHashInfo(hash, JSON.stringify(info));
50
55
  }
51
56
 
52
- async function getLocalHashInfo(hash) {
57
+ async function getLocalHashInfo(hash: string) {
53
58
  return JSON.parse(await PushyModule.getLocalHashInfo(hash));
54
59
  }
55
60
 
56
- export async function getCurrentVersionInfo() {
61
+ export async function getCurrentVersionInfo(): Promise<{
62
+ name?: string;
63
+ description?: string;
64
+ metaInfo?: string;
65
+ }> {
57
66
  return currentVersion ? (await getLocalHashInfo(currentVersion)) || {} : {};
58
67
  }
59
68
 
@@ -64,34 +73,50 @@ if (!uuid) {
64
73
  PushyModule.setUuid(uuid);
65
74
  }
66
75
 
67
- function logger(text) {
68
- console.log(`Pushy: ${text}`);
76
+ function logger(...args: string[]) {
77
+ console.log('Pushy: ', ...args);
69
78
  }
70
79
 
71
- function report(hash, type) {
72
- logger(type);
73
- fetch(getReportUrl(), {
74
- method: 'POST',
75
- headers: {
76
- Accept: 'application/json',
77
- 'Content-Type': 'application/json',
78
- },
79
- body: JSON.stringify({
80
- hash,
81
- type,
80
+ const noop = () => {};
81
+ let reporter: UpdateEventsListener = noop;
82
+
83
+ export function onEvents(customReporter: UpdateEventsListener) {
84
+ reporter = customReporter;
85
+ if (isRolledBack) {
86
+ report({
87
+ type: 'rollback',
88
+ data: {
89
+ rolledBackVersion,
90
+ },
91
+ });
92
+ }
93
+ }
94
+
95
+ function report({
96
+ type,
97
+ message = '',
98
+ data = {},
99
+ }: {
100
+ type: EventType;
101
+ message?: string;
102
+ data?: Record<string, string | number>;
103
+ }) {
104
+ logger(type + ' ' + message);
105
+ reporter({
106
+ type,
107
+ data: {
108
+ currentVersion,
82
109
  cInfo,
83
110
  packageVersion,
84
111
  buildTime,
85
- }),
86
- }).catch((_e) => {});
112
+ message,
113
+ ...data,
114
+ },
115
+ });
87
116
  }
88
117
 
89
118
  logger('uuid: ' + uuid);
90
119
 
91
- if (isRolledBack) {
92
- report(rolledBackVersion, 'rollback');
93
- }
94
-
95
120
  export const cInfo = {
96
121
  pushy: require('../package.json').version,
97
122
  rn: RNVersion,
@@ -100,13 +125,14 @@ export const cInfo = {
100
125
  };
101
126
 
102
127
  function assertRelease() {
128
+ // @ts-expect-error
103
129
  if (__DEV__) {
104
130
  throw new Error('react-native-update 只能在 RELEASE 版本中运行.');
105
131
  }
106
132
  }
107
133
 
108
134
  let checkingThrottling = false;
109
- export async function checkUpdate(APPKEY, isRetry) {
135
+ export async function checkUpdate(APPKEY: string, isRetry?: boolean) {
110
136
  assertRelease();
111
137
  if (checkingThrottling) {
112
138
  logger('repeated checking, ignored');
@@ -117,16 +143,14 @@ export async function checkUpdate(APPKEY, isRetry) {
117
143
  checkingThrottling = false;
118
144
  }, 3000);
119
145
  if (blockUpdate && blockUpdate.until > Date.now() / 1000) {
120
- throw new Error(
121
- `热更新已暂停,原因:${blockUpdate.reason}。请在"${new Date(
146
+ return report({
147
+ type: 'errorChecking',
148
+ message: `热更新已暂停,原因:${blockUpdate.reason}。请在"${new Date(
122
149
  blockUpdate.until * 1000,
123
150
  ).toLocaleString()}"之后重试。`,
124
- );
125
- }
126
- if (typeof APPKEY !== 'string') {
127
- throw new Error('未检查到合法的APPKEY,请查看update.json文件是否正确生成');
151
+ });
128
152
  }
129
- logger('checking update');
153
+ report({ type: 'checking' });
130
154
  let resp;
131
155
  try {
132
156
  resp = await fetch(getCheckUrl(APPKEY), {
@@ -144,7 +168,10 @@ export async function checkUpdate(APPKEY, isRetry) {
144
168
  });
145
169
  } catch (e) {
146
170
  if (isRetry) {
147
- throw new Error('无法连接更新服务器,请检查网络连接后重试');
171
+ return report({
172
+ type: 'errorChecking',
173
+ message: '无法连接更新服务器,请检查网络连接后重试',
174
+ });
148
175
  }
149
176
  await tryBackupEndpoints();
150
177
  return checkUpdate(APPKEY, true);
@@ -153,13 +180,15 @@ export async function checkUpdate(APPKEY, isRetry) {
153
180
  checkOperation(result.op);
154
181
 
155
182
  if (resp.status !== 200) {
156
- throw new Error(result.message);
183
+ return report({ type: 'errorChecking', message: result.message });
157
184
  }
158
185
 
159
186
  return result;
160
187
  }
161
188
 
162
- function checkOperation(op) {
189
+ function checkOperation(
190
+ op: { type: string; reason: string; duration: number }[],
191
+ ) {
163
192
  if (!Array.isArray(op)) {
164
193
  return;
165
194
  }
@@ -175,8 +204,13 @@ function checkOperation(op) {
175
204
  }
176
205
 
177
206
  let downloadingThrottling = false;
178
- let downloadedHash;
179
- export async function downloadUpdate(options, eventListeners) {
207
+ let downloadedHash: string;
208
+ export async function downloadUpdate(
209
+ options: UpdateAvailableResult,
210
+ eventListeners?: {
211
+ onDownloadProgress?: (data: ProgressData) => void;
212
+ },
213
+ ) {
180
214
  assertRelease();
181
215
  if (!options.update) {
182
216
  return;
@@ -212,6 +246,7 @@ export async function downloadUpdate(options, eventListeners) {
212
246
  }
213
247
  }
214
248
  let succeeded = false;
249
+ report({ type: 'downloading' });
215
250
  if (options.diffUrl) {
216
251
  logger('downloading diff');
217
252
  try {
@@ -251,8 +286,7 @@ export async function downloadUpdate(options, eventListeners) {
251
286
  }
252
287
  progressHandler && progressHandler.remove();
253
288
  if (!succeeded) {
254
- report(options.hash, 'error');
255
- throw new Error('all update attempts failed');
289
+ return report({ type: 'errorUpdate', data: { newVersion: options.hash } });
256
290
  }
257
291
  setLocalHashInfo(options.hash, {
258
292
  name: options.name,
@@ -263,7 +297,7 @@ export async function downloadUpdate(options, eventListeners) {
263
297
  return options.hash;
264
298
  }
265
299
 
266
- function assertHash(hash) {
300
+ function assertHash(hash: string) {
267
301
  if (!downloadedHash) {
268
302
  logger(`no downloaded hash`);
269
303
  return;
@@ -275,7 +309,7 @@ function assertHash(hash) {
275
309
  return true;
276
310
  }
277
311
 
278
- export function switchVersion(hash) {
312
+ export function switchVersion(hash: string) {
279
313
  assertRelease();
280
314
  if (assertHash(hash)) {
281
315
  logger('switchVersion: ' + hash);
@@ -283,7 +317,7 @@ export function switchVersion(hash) {
283
317
  }
284
318
  }
285
319
 
286
- export function switchVersionLater(hash) {
320
+ export function switchVersionLater(hash: string) {
287
321
  assertRelease();
288
322
  if (assertHash(hash)) {
289
323
  logger('switchVersionLater: ' + hash);
@@ -300,21 +334,30 @@ export function markSuccess() {
300
334
  }
301
335
  marked = true;
302
336
  PushyModule.markSuccess();
303
- report(currentVersion, 'success');
337
+ report({ type: 'markSuccess' });
304
338
  }
305
339
 
306
- export async function downloadAndInstallApk({ url, onDownloadProgress }) {
307
- logger('downloadAndInstallApk');
308
- if (Platform.OS === 'android' && Platform.Version <= 23) {
340
+ export async function downloadAndInstallApk({
341
+ url,
342
+ onDownloadProgress,
343
+ }: {
344
+ url: string;
345
+ onDownloadProgress?: (data: ProgressData) => void;
346
+ }) {
347
+ if (Platform.OS !== 'android') {
348
+ return;
349
+ }
350
+ report({ type: 'downloadingApk' });
351
+ if (Platform.Version <= 23) {
309
352
  try {
310
353
  const granted = await PermissionsAndroid.request(
311
354
  PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
312
355
  );
313
356
  if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
314
- return;
357
+ return report({ type: 'rejectStoragePermission' });
315
358
  }
316
359
  } catch (err) {
317
- console.warn(err);
360
+ return report({ type: 'errorStoragePermission' });
318
361
  }
319
362
  }
320
363
  let hash = Date.now().toString();
@@ -322,7 +365,7 @@ export async function downloadAndInstallApk({ url, onDownloadProgress }) {
322
365
  if (onDownloadProgress) {
323
366
  progressHandler = eventEmitter.addListener(
324
367
  'RCTPushyDownloadProgress',
325
- (progressData) => {
368
+ (progressData: ProgressData) => {
326
369
  if (progressData.hash === hash) {
327
370
  onDownloadProgress(progressData);
328
371
  }
@@ -333,6 +376,8 @@ export async function downloadAndInstallApk({ url, onDownloadProgress }) {
333
376
  url,
334
377
  target: 'update.apk',
335
378
  hash,
379
+ }).catch(() => {
380
+ report({ type: 'errowDownloadAndInstallApk' });
336
381
  });
337
382
  progressHandler && progressHandler.remove();
338
383
  }
@@ -1,4 +1,4 @@
1
- import React, { Component } from 'react';
1
+ import React, { PureComponent } from 'react';
2
2
  import { Platform, Alert, Linking, AppState } from 'react-native';
3
3
 
4
4
  import {
@@ -10,16 +10,25 @@ import {
10
10
  switchVersionLater,
11
11
  markSuccess,
12
12
  downloadAndInstallApk,
13
+ onEvents,
13
14
  } from './main';
15
+ import { UpdateEventsListener } from './type';
14
16
 
15
- export function simpleUpdate(WrappedComponent, options = {}) {
16
- const { appKey } = options;
17
+ export function simpleUpdate(
18
+ WrappedComponent: JSX.Element,
19
+ options: { appKey?: string; onEvents?: UpdateEventsListener } = {},
20
+ ) {
21
+ const { appKey, onEvents: eventListeners } = options;
17
22
  if (!appKey) {
18
23
  throw new Error('appKey is required for simpleUpdate()');
19
24
  }
25
+ if (typeof eventListeners === 'function') {
26
+ onEvents(eventListeners);
27
+ }
28
+ // @ts-expect-error
20
29
  return __DEV__
21
30
  ? WrappedComponent
22
- : class AppUpdate extends Component {
31
+ : class AppUpdate extends PureComponent {
23
32
  componentDidMount() {
24
33
  if (isRolledBack) {
25
34
  Alert.alert('抱歉', '刚刚更新遭遇错误,已为您恢复到更新前版本');
package/lib/type.ts ADDED
@@ -0,0 +1,70 @@
1
+ export interface ExpiredResult {
2
+ upToDate?: false;
3
+ expired: true;
4
+ downloadUrl: string;
5
+ }
6
+
7
+ export interface UpTodateResult {
8
+ expired?: false;
9
+ upToDate: true;
10
+ paused?: 'app' | 'package';
11
+ }
12
+
13
+ export interface UpdateAvailableResult {
14
+ expired?: false;
15
+ upToDate?: false;
16
+ update: true;
17
+ name: string; // version name
18
+ hash: string;
19
+ description: string;
20
+ metaInfo: string;
21
+ pdiffUrl: string;
22
+ diffUrl?: string;
23
+ updateUrl?: string;
24
+ }
25
+
26
+ export type CheckResult =
27
+ | ExpiredResult
28
+ | UpTodateResult
29
+ | UpdateAvailableResult;
30
+
31
+ export interface ProgressData {
32
+ hash: string;
33
+ received: number;
34
+ total: number;
35
+ }
36
+
37
+ export type EventType =
38
+ | 'rollback'
39
+ | 'errorChecking'
40
+ | 'checking'
41
+ | 'downloading'
42
+ | 'errorUpdate'
43
+ | 'markSuccess'
44
+ | 'downloadingApk'
45
+ | 'rejectStoragePermission'
46
+ | 'errorStoragePermission'
47
+ | 'errowDownloadAndInstallApk';
48
+
49
+ export interface EventData {
50
+ currentVersion: string;
51
+ cInfo: {
52
+ pushy: string;
53
+ rn: string;
54
+ os: string;
55
+ uuid: string;
56
+ };
57
+ packageVersion: string;
58
+ buildTime: number;
59
+ message?: string;
60
+ rolledBackVersion?: string;
61
+ newVersion?: string;
62
+ [key: string]: any;
63
+ }
64
+ export type UpdateEventsListener = ({
65
+ type,
66
+ data,
67
+ }: {
68
+ type: EventType;
69
+ data: EventData;
70
+ }) => void;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "react-native-update",
3
- "version": "9.0.0-beta.0",
3
+ "version": "9.0.0-beta.1",
4
4
  "description": "react-native hot update",
5
- "main": "lib/index.js",
5
+ "main": "lib/index.ts",
6
6
  "scripts": {
7
7
  "prepublish": "yarn submodule",
8
8
  "submodule": "git submodule update --init --recursive",
package/lib/index.d.ts DELETED
@@ -1,94 +0,0 @@
1
- export const downloadRootDir: string;
2
- export const packageVersion: string;
3
- export const currentVersion: string;
4
- export const isFirstTime: boolean;
5
- export const isRolledBack: boolean;
6
-
7
- export interface ExpiredResult {
8
- upToDate?: false;
9
- expired: true;
10
- downloadUrl: string;
11
- }
12
-
13
- export interface UpTodateResult {
14
- expired?: false;
15
- upToDate: true;
16
- paused?: 'app' | 'package';
17
- }
18
-
19
- export interface UpdateAvailableResult {
20
- expired?: false;
21
- upToDate?: false;
22
- update: true;
23
- name: string; // version name
24
- hash: string;
25
- description: string;
26
- metaInfo: string;
27
- pdiffUrl: string;
28
- diffUrl?: string;
29
- }
30
-
31
- export type CheckResult =
32
- | ExpiredResult
33
- | UpTodateResult
34
- | UpdateAvailableResult;
35
-
36
- export function checkUpdate(appkey: string): Promise<CheckResult>;
37
-
38
- export function downloadUpdate(
39
- info: UpdateAvailableResult,
40
- eventListeners?: {
41
- onDownloadProgress?: (data: ProgressData) => void;
42
- },
43
- ): Promise<undefined | string>;
44
-
45
- export function switchVersion(hash: string): void;
46
-
47
- export function switchVersionLater(hash: string): void;
48
-
49
- export function markSuccess(): void;
50
-
51
- export function downloadAndInstallApk({
52
- url,
53
- onDownloadProgress,
54
- }: {
55
- url: string;
56
- onDownloadProgress?: (data: ProgressData) => void;
57
- }): Promise<void>;
58
-
59
- /**
60
- * @param {string} main - The main api endpoint
61
- * @param {string[]} [backups] - The back up endpoints.
62
- * @param {string} [backupQueryUrl] - An url that return a json file containing an array of endpoint.
63
- * like: ["https://backup.api/1", "https://backup.api/2"]
64
- */
65
- export function setCustomEndpoints({
66
- main,
67
- backups,
68
- backupQueryUrl,
69
- }: {
70
- main: string;
71
- backups?: string[];
72
- backupQueryUrl?: string;
73
- }): void;
74
-
75
- export function getCurrentVersionInfo(): Promise<{
76
- name?: string;
77
- description?: string;
78
- metaInfo?: string;
79
- }>;
80
-
81
- interface ProgressData {
82
- hash: string;
83
- received: number;
84
- total: number;
85
- }
86
-
87
- interface SimpleUpdateOptions {
88
- appKey: string;
89
- }
90
-
91
- export function simpleUpdate(
92
- wrappedComponent: any,
93
- options: SimpleUpdateOptions,
94
- ): any;
File without changes