Skip to content

Commit a64dbd0

Browse files
committed
Type native module
1 parent 54586c6 commit a64dbd0

11 files changed

Lines changed: 154 additions & 79 deletions

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"test:fast:ios": "mocha --recursive bin/test --ios",
4242
"test:debugger:android": "mocha --recursive --inspect-brk=0.0.0.0 bin/test --android",
4343
"test:debugger:ios": "mocha --recursive --inspect-brk=0.0.0.0 bin/test --ios",
44+
"test:types": "tsc --noEmit",
4445
"test:format": "prettier --check \"{src,docs}/**/*.{ts,js,md}\" README.md react-native.config.js",
4546
"tslint": "tslint -c tslint.json test/**/*.ts",
4647
"prepare": "genversion src/internals/version.ts --esm -s && bob build"
@@ -97,6 +98,7 @@
9798
[
9899
"typescript",
99100
{
101+
"project": "tsconfig.build.json",
100102
"esm": true
101103
}
102104
]

src/enums/InstallMode.enum.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,25 @@ export enum InstallMode {
77
/**
88
* Indicates that you want to install the update and restart the app immediately.
99
*/
10-
IMMEDIATE = NativeRNAppZungCodePushModule.codePushInstallModeImmediate,
10+
IMMEDIATE = NativeRNAppZungCodePushModule.getConstants().codePushInstallModeImmediate,
1111

1212
/**
1313
* Indicates that you want to install the update, but not forcibly restart the app.
1414
*/
15-
ON_NEXT_RESTART = NativeRNAppZungCodePushModule.codePushInstallModeOnNextRestart,
15+
ON_NEXT_RESTART = NativeRNAppZungCodePushModule.getConstants().codePushInstallModeOnNextRestart,
1616

1717
/**
1818
* Indicates that you want to install the update, but don't want to restart the app until the next time
1919
* the end user resumes it from the background. This way, you don't disrupt their current session,
2020
* but you can get the update in front of them sooner than having to wait for the next natural restart.
2121
* This value is appropriate for silent installs that can be applied on resume in a non-invasive way.
2222
*/
23-
ON_NEXT_RESUME = NativeRNAppZungCodePushModule.codePushInstallModeOnNextResume,
23+
ON_NEXT_RESUME = NativeRNAppZungCodePushModule.getConstants().codePushInstallModeOnNextResume,
2424

2525
/**
2626
* Indicates that you want to install the update when the app is in the background,
2727
* but only after it has been in the background for "minimumBackgroundDuration" seconds (0 by default),
2828
* so that user context isn't lost unless the app suspension is long enough to not matter.
2929
*/
30-
ON_NEXT_SUSPEND = NativeRNAppZungCodePushModule.codePushInstallModeOnNextSuspend,
30+
ON_NEXT_SUSPEND = NativeRNAppZungCodePushModule.getConstants().codePushInstallModeOnNextSuspend,
3131
}

src/enums/UpdateState.enum.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ export enum UpdateState {
88
* Indicates that an update represents the
99
* version of the app that is currently running.
1010
*/
11-
RUNNING = NativeRNAppZungCodePushModule.codePushUpdateStateRunning,
11+
RUNNING = NativeRNAppZungCodePushModule.getConstants().codePushUpdateStateRunning,
1212

1313
/**
1414
* Indicates than an update has been installed, but the
1515
* app hasn't been restarted yet in order to apply it.
1616
*/
17-
PENDING = NativeRNAppZungCodePushModule.codePushUpdateStatePending,
17+
PENDING = NativeRNAppZungCodePushModule.getConstants().codePushUpdateStatePending,
1818

1919
/**
2020
* Indicates than an update represents the latest available
2121
* release, and can be either currently running or pending.
2222
*/
23-
LATEST = NativeRNAppZungCodePushModule.codePushUpdateStateLatest,
23+
LATEST = NativeRNAppZungCodePushModule.getConstants().codePushUpdateStateLatest,
2424
}
Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,29 @@
1+
import type { InstallMode } from '../enums/InstallMode.enum';
12
import type { LocalPackage } from '../types';
23
import { NativeRNAppZungCodePushModule } from './NativeRNAppZungCodePushModule';
3-
import { cloneWithoutFunctions } from './utils/cloneWithoutFunctions';
44

55
export class LocalPackageImplementation implements LocalPackage {
6-
constructor(localPackageData: LocalPackage) {
6+
constructor(localPackageData: Omit<LocalPackage, 'install'>) {
77
Object.assign(this, localPackageData);
8+
9+
this.install = async (
10+
installMode = NativeRNAppZungCodePushModule.getConstants().codePushInstallModeOnNextRestart,
11+
minimumBackgroundDuration = 0,
12+
updateInstalledCallback?: () => Promise<void> | void,
13+
) => {
14+
const { ...localPackageCopy } = localPackageData;
15+
await NativeRNAppZungCodePushModule.installUpdate(localPackageCopy, installMode, minimumBackgroundDuration);
16+
17+
if (installMode === NativeRNAppZungCodePushModule.getConstants().codePushInstallModeImmediate) {
18+
await updateInstalledCallback?.();
19+
await NativeRNAppZungCodePushModule.restartApp(false);
20+
return;
21+
}
22+
23+
await NativeRNAppZungCodePushModule.clearPendingRestart();
24+
this.isPending = true; // Mark the package as pending since it hasn't been applied yet
25+
await updateInstalledCallback?.();
26+
};
827
}
928

1029
appVersion!: string;
@@ -18,22 +37,9 @@ export class LocalPackageImplementation implements LocalPackage {
1837
packageSize!: number;
1938
releaseChannelPublicId!: string;
2039

21-
async install(
22-
installMode = NativeRNAppZungCodePushModule.codePushInstallModeOnNextRestart,
23-
minimumBackgroundDuration = 0,
24-
updateInstalledCallback?: () => Promise<void> | void,
25-
) {
26-
const localPackageCopy = cloneWithoutFunctions(this); // In dev mode, React Native deep freezes any object queued over the bridge
27-
await NativeRNAppZungCodePushModule.installUpdate(localPackageCopy, installMode, minimumBackgroundDuration);
28-
29-
if (installMode === NativeRNAppZungCodePushModule.codePushInstallModeImmediate) {
30-
await updateInstalledCallback?.();
31-
NativeRNAppZungCodePushModule.restartApp(false);
32-
return;
33-
}
34-
35-
NativeRNAppZungCodePushModule.clearPendingRestart();
36-
this.isPending = true; // Mark the package as pending since it hasn't been applied yet
37-
await updateInstalledCallback?.();
38-
}
40+
install: (
41+
installMode?: InstallMode,
42+
minimumBackgroundDuration?: number,
43+
onUpdateInstalled?: () => Promise<void> | void,
44+
) => Promise<void>;
3945
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
export const NativeRNAppZungCodePushModule = require('react-native').NativeModules.CodePush;
1+
import { type Spec } from './RNAppZungCodePushModuleSpec';
2+
3+
export const NativeRNAppZungCodePushModule = require('react-native').NativeModules.CodePush as Spec;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { type TurboModule } from 'react-native';
2+
import type { InstallMode } from '../enums/InstallMode.enum';
3+
import type { UpdateState } from '../enums/UpdateState.enum';
4+
import type { LocalPackage, RemotePackage, StatusReport } from '../types';
5+
import type { Configuration, LatestRollbackInfo, OmitFunctions } from './types';
6+
7+
export interface Spec extends TurboModule {
8+
addListener: (eventName: string) => void;
9+
removeListeners: (count: number) => void;
10+
11+
getConstants(): {
12+
codePushInstallModeImmediate: number;
13+
codePushInstallModeOnNextRestart: number;
14+
codePushInstallModeOnNextResume: number;
15+
codePushInstallModeOnNextSuspend: number;
16+
17+
codePushUpdateStateLatest: number;
18+
codePushUpdateStatePending: number;
19+
codePushUpdateStateRunning: number;
20+
};
21+
22+
getConfiguration(): Promise<Configuration>;
23+
24+
getUpdateMetadata(updateState: UpdateState): Promise<OmitFunctions<LocalPackage>>;
25+
installUpdate(
26+
localPackage: OmitFunctions<LocalPackage>,
27+
installMode: InstallMode,
28+
minimumBackgroundDuration: number,
29+
): Promise<void>;
30+
downloadUpdate(
31+
remotePackage: OmitFunctions<RemotePackage>,
32+
notifyProgress: boolean,
33+
): Promise<OmitFunctions<LocalPackage>>;
34+
35+
restartApp(onlyIfUpdateIsPending: boolean): Promise<void>;
36+
clearPendingRestart(): Promise<void>;
37+
38+
disallow(): Promise<void>;
39+
allow(): Promise<void>;
40+
41+
clearUpdates(): void;
42+
43+
notifyApplicationReady(): Promise<void>;
44+
setLatestRollbackInfo(packageHash: string): Promise<void>;
45+
46+
getNewStatusReport(): Promise<StatusReport>;
47+
recordStatusReported(statusReport: StatusReport): void;
48+
saveStatusReportForRetry(statusReport: StatusReport): void;
49+
50+
getLatestRollbackInfo(): Promise<LatestRollbackInfo>;
51+
52+
isFirstRun(packageHash: string): Promise<boolean>;
53+
isFailedUpdate(packageHash: string): Promise<boolean>;
54+
}
Lines changed: 44 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,60 @@
11
import type { Package } from 'code-push/script/acquisition-sdk';
22
import { NativeEventEmitter } from 'react-native';
3-
import type { DownloadProgressCallback, RemotePackage } from '../types';
3+
import type { DownloadProgressCallback, LocalPackage, RemotePackage } from '../types';
44
import { LocalPackageImplementation } from './LocalPackageImplementation';
55
import { NativeRNAppZungCodePushModule } from './NativeRNAppZungCodePushModule';
6-
import { cloneWithoutFunctions } from './utils/cloneWithoutFunctions';
76
import { log } from './utils/log';
87

98
export class RemotePackageImpl implements RemotePackage {
109
constructor(
1110
remotePackageData: Omit<RemotePackage, 'download'>,
12-
private readonly reportStatusDownload?: (downloadedPackage: Package) => Promise<void>,
11+
reportStatusDownload?: (downloadedPackage: Package) => Promise<void>,
1312
) {
1413
Object.assign(this, remotePackageData);
14+
15+
this.download = async (downloadProgressCallback?: DownloadProgressCallback) => {
16+
if (!this.downloadUrl) {
17+
throw new Error('Cannot download an update without a download url');
18+
}
19+
20+
let downloadProgressSubscription;
21+
22+
if (downloadProgressCallback) {
23+
const codePushEventEmitter = new NativeEventEmitter(NativeRNAppZungCodePushModule);
24+
// Use event subscription to obtain download progress.
25+
downloadProgressSubscription = codePushEventEmitter.addListener(
26+
'CodePushDownloadProgress',
27+
downloadProgressCallback,
28+
);
29+
}
30+
31+
// Use the downloaded package info. Native code will save the package info
32+
// so that the client knows what the current package version is.
33+
try {
34+
const { ...updatePackageCopy } = remotePackageData; // In dev mode, React Native deep freezes any object queued over the bridge
35+
const downloadedPackage = await NativeRNAppZungCodePushModule.downloadUpdate(
36+
updatePackageCopy,
37+
!!downloadProgressCallback,
38+
);
39+
40+
if (reportStatusDownload) {
41+
reportStatusDownload({
42+
...this,
43+
deploymentKey: this.releaseChannelPublicId,
44+
}).catch((err) => {
45+
log(`Report download status failed: ${err}`);
46+
});
47+
}
48+
49+
return new LocalPackageImplementation(downloadedPackage);
50+
} finally {
51+
downloadProgressSubscription && downloadProgressSubscription.remove();
52+
}
53+
};
1554
}
1655

56+
download: (downloadProgressCallback?: DownloadProgressCallback) => Promise<LocalPackage>;
57+
1758
appVersion!: string;
1859
description!: string;
1960
downloadUrl!: string;
@@ -25,44 +66,4 @@ export class RemotePackageImpl implements RemotePackage {
2566
packageHash!: string;
2667
packageSize!: number;
2768
releaseChannelPublicId!: string;
28-
29-
async download(downloadProgressCallback: DownloadProgressCallback) {
30-
if (!this.downloadUrl) {
31-
throw new Error('Cannot download an update without a download url');
32-
}
33-
34-
let downloadProgressSubscription;
35-
36-
if (downloadProgressCallback) {
37-
const codePushEventEmitter = new NativeEventEmitter(NativeRNAppZungCodePushModule);
38-
// Use event subscription to obtain download progress.
39-
downloadProgressSubscription = codePushEventEmitter.addListener(
40-
'CodePushDownloadProgress',
41-
downloadProgressCallback,
42-
);
43-
}
44-
45-
// Use the downloaded package info. Native code will save the package info
46-
// so that the client knows what the current package version is.
47-
try {
48-
const updatePackageCopy = cloneWithoutFunctions(this); // In dev mode, React Native deep freezes any object queued over the bridge
49-
const downloadedPackage = await NativeRNAppZungCodePushModule.downloadUpdate(
50-
updatePackageCopy,
51-
!!downloadProgressCallback,
52-
);
53-
54-
if (this.reportStatusDownload) {
55-
this.reportStatusDownload({
56-
...this,
57-
deploymentKey: this.releaseChannelPublicId,
58-
}).catch((err) => {
59-
log(`Report download status failed: ${err}`);
60-
});
61-
}
62-
63-
return new LocalPackageImplementation(downloadedPackage);
64-
} finally {
65-
downloadProgressSubscription && downloadProgressSubscription.remove();
66-
}
67-
}
6869
}

src/internals/shouldUpdateBeIgnored.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import type { RemotePackage, RollbackRetryOptions, SyncOptions } from '../types';
22
import { NativeRNAppZungCodePushModule } from './NativeRNAppZungCodePushModule';
3+
import type { LatestRollbackInfo } from './types';
34
import { log } from './utils/log';
45

56
const DEFAULT_ROLLBACK_RETRY_OPTIONS = {
67
delayInHours: 24,
78
maxRetryAttempts: 1,
89
};
910

10-
// TODO type latestRollbackInfo
11-
function validateLatestRollbackInfo(latestRollbackInfo: any, packageHash: string) {
11+
function validateLatestRollbackInfo(latestRollbackInfo: LatestRollbackInfo, packageHash: string) {
1212
return (
1313
latestRollbackInfo &&
1414
latestRollbackInfo.time &&

src/internals/types.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,15 @@ export interface Configuration extends BaseConfiguration {
44
releaseChannelPublicId: string;
55
packageHash?: string;
66
}
7+
8+
export interface LatestRollbackInfo {
9+
time: number;
10+
count: number;
11+
packageHash: string;
12+
}
13+
14+
type FunctionPropertyNames<T> = {
15+
[K in keyof T]: T[K] extends Function ? K : never;
16+
}[keyof T];
17+
18+
export type OmitFunctions<T extends object> = Omit<T, FunctionPropertyNames<T>>;

src/internals/utils/cloneWithoutFunctions.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)