react-native-store-version 1.4.2 → 2.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/CHANGELOG.md +28 -0
- package/README.md +514 -43
- package/dist/checkVersion.d.ts +73 -0
- package/dist/checkVersion.d.ts.map +1 -0
- package/dist/checkVersion.js +193 -0
- package/dist/cli/check.js +38 -0
- package/dist/cli/checks/ids.js +111 -0
- package/dist/cli/checks/installed.js +51 -0
- package/dist/cli/checks/peers.js +31 -0
- package/dist/cli/checks/rollout.js +141 -0
- package/dist/cli/checks/store.js +71 -0
- package/dist/cli/checks/versions.js +83 -0
- package/dist/cli/index.js +43 -0
- package/dist/cli/main.js +182 -0
- package/dist/cli/output.js +77 -0
- package/dist/cli/packages.js +40 -0
- package/dist/cli/project/android.js +60 -0
- package/dist/cli/project/expo.js +194 -0
- package/dist/cli/project/files.js +42 -0
- package/dist/cli/project/gradle.js +145 -0
- package/dist/cli/project/index.js +10 -0
- package/dist/cli/project/ios.js +190 -0
- package/dist/cli/project/jsx.js +327 -0
- package/dist/cli/project/plist.js +166 -0
- package/dist/cli/project/sources.js +86 -0
- package/dist/cli/result.js +12 -0
- package/dist/compare.d.ts +31 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/compare.js +68 -0
- package/dist/detect.d.ts +68 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +103 -0
- package/dist/errors.d.ts +33 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +23 -0
- package/dist/fetchers/android.d.ts +53 -0
- package/dist/fetchers/android.d.ts.map +1 -0
- package/dist/fetchers/android.js +91 -0
- package/dist/fetchers/custom.d.ts +29 -0
- package/dist/fetchers/custom.d.ts.map +1 -0
- package/dist/fetchers/custom.js +41 -0
- package/dist/fetchers/ios.d.ts +61 -0
- package/dist/fetchers/ios.d.ts.map +1 -0
- package/dist/fetchers/ios.js +79 -0
- package/dist/index.d.ts +15 -14
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -58
- package/dist/openStore.d.ts +29 -0
- package/dist/openStore.d.ts.map +1 -0
- package/dist/openStore.js +46 -0
- package/dist/policy.d.ts +75 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +128 -0
- package/dist/react/DefaultForceModal.d.ts +26 -0
- package/dist/react/DefaultForceModal.d.ts.map +1 -0
- package/dist/react/DefaultForceModal.js +58 -0
- package/dist/react/DefaultWarningSheet.d.ts +26 -0
- package/dist/react/DefaultWarningSheet.d.ts.map +1 -0
- package/dist/react/DefaultWarningSheet.js +66 -0
- package/dist/react/StoreVersionModal.d.ts +52 -0
- package/dist/react/StoreVersionModal.d.ts.map +1 -0
- package/dist/react/StoreVersionModal.js +250 -0
- package/dist/react/StoreVersionProvider.d.ts +51 -0
- package/dist/react/StoreVersionProvider.d.ts.map +1 -0
- package/dist/react/StoreVersionProvider.js +69 -0
- package/dist/react/defaultUi.d.ts +72 -0
- package/dist/react/defaultUi.d.ts.map +1 -0
- package/dist/react/defaultUi.js +118 -0
- package/dist/react/engine.d.ts +146 -0
- package/dist/react/engine.d.ts.map +1 -0
- package/dist/react/engine.js +541 -0
- package/dist/react/texts.d.ts +43 -0
- package/dist/react/texts.d.ts.map +1 -0
- package/dist/react/texts.js +57 -0
- package/dist/react/useIsomorphicLayoutEffect.d.ts +3 -0
- package/dist/react/useIsomorphicLayoutEffect.d.ts.map +1 -0
- package/dist/react/useIsomorphicLayoutEffect.js +6 -0
- package/dist/react/useStoreVersion.d.ts +121 -0
- package/dist/react/useStoreVersion.d.ts.map +1 -0
- package/dist/react/useStoreVersion.js +23 -0
- package/dist/request.d.ts +78 -0
- package/dist/request.d.ts.map +1 -0
- package/dist/request.js +227 -0
- package/dist/resolve.d.ts +98 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +218 -0
- package/dist/texts/ja.d.ts +8 -0
- package/dist/texts/ja.d.ts.map +1 -0
- package/dist/texts/ja.js +18 -0
- package/dist/vendor/compare-versions.d.ts +52 -0
- package/dist/vendor/compare-versions.d.ts.map +1 -0
- package/dist/vendor/compare-versions.js +111 -0
- package/package.json +83 -44
- package/src/checkVersion.ts +273 -0
- package/src/cli/check.ts +59 -0
- package/src/cli/checks/ids.ts +163 -0
- package/src/cli/checks/installed.ts +75 -0
- package/src/cli/checks/peers.ts +34 -0
- package/src/cli/checks/rollout.ts +183 -0
- package/src/cli/checks/store.ts +105 -0
- package/src/cli/checks/versions.ts +108 -0
- package/src/cli/index.ts +51 -0
- package/src/cli/main.ts +229 -0
- package/src/cli/output.ts +122 -0
- package/src/cli/packages.ts +43 -0
- package/src/cli/project/android.ts +96 -0
- package/src/cli/project/expo.ts +232 -0
- package/src/cli/project/files.ts +56 -0
- package/src/cli/project/gradle.ts +181 -0
- package/src/cli/project/index.ts +18 -0
- package/src/cli/project/ios.ts +284 -0
- package/src/cli/project/jsx.ts +354 -0
- package/src/cli/project/plist.ts +179 -0
- package/src/cli/project/sources.ts +118 -0
- package/src/cli/result.ts +34 -0
- package/src/compare.ts +101 -0
- package/src/detect.ts +178 -0
- package/src/errors.ts +63 -0
- package/src/fetchers/android.ts +136 -0
- package/src/fetchers/custom.ts +84 -0
- package/src/fetchers/ios.ts +160 -0
- package/src/index.ts +37 -0
- package/src/openStore.ts +74 -0
- package/src/policy.ts +223 -0
- package/src/react/DefaultForceModal.tsx +100 -0
- package/src/react/DefaultWarningSheet.tsx +115 -0
- package/src/react/StoreVersionModal.tsx +365 -0
- package/src/react/StoreVersionProvider.tsx +149 -0
- package/src/react/defaultUi.tsx +205 -0
- package/src/react/engine.ts +768 -0
- package/src/react/texts.ts +119 -0
- package/src/react/useIsomorphicLayoutEffect.ts +8 -0
- package/src/react/useStoreVersion.ts +153 -0
- package/src/request.ts +328 -0
- package/src/resolve.ts +388 -0
- package/src/texts/ja.ts +26 -0
- package/src/vendor/compare-versions.ts +128 -0
- package/texts/ja.d.ts +2 -0
- package/texts/ja.js +3 -0
- package/.eslintrc.js +0 -51
- package/.github/FUNDING.yml +0 -3
- package/.github/workflows/pr.yml +0 -48
- package/.github/workflows/release.yml +0 -259
- package/.prettierrc.js +0 -6
- package/babel.config.js +0 -3
- package/dist/android.d.ts +0 -1
- package/dist/android.js +0 -17
- package/dist/ios.d.ts +0 -1
- package/dist/ios.js +0 -18
package/dist/compare.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { StoreVersionError } from './errors.js';
|
|
2
|
+
import { compareSegments, compareVersions, validateAndParse, } from './vendor/compare-versions.js';
|
|
3
|
+
/**
|
|
4
|
+
* v1 互換のバージョン比較。不正な値は compare-versions のエラー
|
|
5
|
+
* (`Invalid argument not valid semver ('x' received)` など)をそのまま投げる。
|
|
6
|
+
* v1 のメッセージを変えないために、ここでは包まない
|
|
7
|
+
*/
|
|
8
|
+
export const compareVersion = (local, remote) => {
|
|
9
|
+
switch (compareVersions(local, remote)) {
|
|
10
|
+
case -1:
|
|
11
|
+
return 'new';
|
|
12
|
+
case 1:
|
|
13
|
+
return 'old';
|
|
14
|
+
default:
|
|
15
|
+
return 'equal';
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
/** `compareVersion` の結果から v1 の `detail` を作る */
|
|
19
|
+
export const toDetail = (result) => {
|
|
20
|
+
switch (result) {
|
|
21
|
+
case 'new':
|
|
22
|
+
return 'remote > local';
|
|
23
|
+
case 'old':
|
|
24
|
+
return 'remote < local';
|
|
25
|
+
default:
|
|
26
|
+
return 'remote === local';
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* remote が local より新しいときの差分の種類。major / minor / patch の数字を比べ、最初に違う所で決める。
|
|
31
|
+
* 3 つとも同じ(4 桁目以降やプレリリースだけの差)なら `patch`。欠けた桁は 0 とみなす。
|
|
32
|
+
* 入力は検証済みであること(不正値は compare-versions のエラーになる)
|
|
33
|
+
*/
|
|
34
|
+
export const diffType = (local, remote) => {
|
|
35
|
+
const l = validateAndParse(local);
|
|
36
|
+
const r = validateAndParse(remote);
|
|
37
|
+
const types = ['major', 'minor', 'patch'];
|
|
38
|
+
for (let i = 0; i < types.length; i++) {
|
|
39
|
+
// ワイルドカード(`1.x`)は compare-versions と同じく同値として扱われる
|
|
40
|
+
if (compareSegments([l[i]], [r[i]]) !== 0) {
|
|
41
|
+
return types[i];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return 'patch';
|
|
45
|
+
};
|
|
46
|
+
/** message に入れる値の表現。例外を投げない(BigInt や循環参照のオブジェクトでも) */
|
|
47
|
+
const describe = (value) => typeof value === 'string' ? JSON.stringify(value) : `(${typeof value})`;
|
|
48
|
+
/**
|
|
49
|
+
* 比較できる値かを確かめる。だめなら `StoreVersionError('INVALID_VERSION')`(cause に元のエラー)を投げる。
|
|
50
|
+
* `label` は message に入れる、どの値かの名前(`local` / `remote` / `minimum`)
|
|
51
|
+
*/
|
|
52
|
+
export const assertValidVersion = (value, label) => {
|
|
53
|
+
try {
|
|
54
|
+
validateAndParse(value);
|
|
55
|
+
}
|
|
56
|
+
catch (cause) {
|
|
57
|
+
throw new StoreVersionError('INVALID_VERSION', `${label} version is invalid: ${describe(value)}`, { cause });
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* v2 の判定用の比較。不正値は `StoreVersionError('INVALID_VERSION')`(cause に元のエラー)で投げる。
|
|
62
|
+
* `labels` は message に入れる、a / b それぞれの名前(既定は `local` / `remote`)
|
|
63
|
+
*/
|
|
64
|
+
export const compareVersionOrThrow = (a, b, labels = ['local', 'remote']) => {
|
|
65
|
+
assertValidVersion(a, labels[0]);
|
|
66
|
+
assertValidVersion(b, labels[1]);
|
|
67
|
+
return compareVersion(a, b);
|
|
68
|
+
};
|
package/dist/detect.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ネイティブのバージョン(expo-application → react-native-device-info の順)。取れなければ null。
|
|
3
|
+
* v1 互換の checkVersion は version と ID を別々に省略できるので、{@link detectApp} を通さずにこれを直接使う
|
|
4
|
+
* (パッケージの入口からは export しない)
|
|
5
|
+
*/
|
|
6
|
+
export declare const nativeVersion: () => string | null;
|
|
7
|
+
/**
|
|
8
|
+
* ネイティブの ID(bundleId / package name。expo-application → react-native-device-info の順)。取れなければ null。
|
|
9
|
+
* checkVersion も直接使う({@link nativeVersion} と同じ理由)
|
|
10
|
+
*/
|
|
11
|
+
export declare const nativeAppId: () => string | null;
|
|
12
|
+
/**
|
|
13
|
+
* Expo Go の applicationId(Expo Go 自体のストア版と比べてしまうのを防ぐ。expo-constants には依存しない)。
|
|
14
|
+
* iOS の bundle ID は `host.exp.Exponent`(E が大文字。@expo/cli の EXPO_GO_BUNDLE_IDENTIFIER)、
|
|
15
|
+
* Android の package name は `host.exp.exponent`
|
|
16
|
+
*/
|
|
17
|
+
export declare const EXPO_GO_APPLICATION_IDS: readonly string[];
|
|
18
|
+
/**
|
|
19
|
+
* 判定をしない理由。
|
|
20
|
+
* - `web`: `Platform.OS === 'web'`(`applicationId` などが取れない)
|
|
21
|
+
* - `unsupported-platform`: ios / android / web 以外(react-native-windows / macOS など。ストアの版と比べる意味が無い)
|
|
22
|
+
* - `expo-go`: Expo Go の中で動いている
|
|
23
|
+
*/
|
|
24
|
+
export type SkipReason = 'web' | 'unsupported-platform' | 'expo-go';
|
|
25
|
+
/**
|
|
26
|
+
* 判定をしない環境なら、その理由を返す。判定する環境なら null。
|
|
27
|
+
* Expo Go はネイティブの ID(expo-application / react-native-device-info)で見る(明示的に渡された ID では見ない)
|
|
28
|
+
*/
|
|
29
|
+
export declare const getSkipReason: (os?: string) => SkipReason | null;
|
|
30
|
+
/** 自動取得の代わりに明示的に渡す値(渡した値が最優先) */
|
|
31
|
+
export type DetectParams = {
|
|
32
|
+
/**
|
|
33
|
+
* アプリのバージョン。必ずネイティブのバージョン(iOS の `CFBundleShortVersionString` / Android の `versionName`)を渡す。
|
|
34
|
+
* `Constants.expoConfig.version` や `package.json` の値は JS バンドルに含まれ、OTA 更新で変わってしまう
|
|
35
|
+
* (ネイティブは古いのに「更新済み」と誤判定し、force が出なくなる)ので使わないこと。
|
|
36
|
+
* 省略すると expo-application の `nativeApplicationVersion` → react-native-device-info の `getVersion()` の順に取る
|
|
37
|
+
*/
|
|
38
|
+
version?: string | null;
|
|
39
|
+
/** iOS の bundleId。省略すると expo-application の `applicationId` → react-native-device-info の `getBundleId()` の順に取る */
|
|
40
|
+
bundleId?: string | null;
|
|
41
|
+
/** Android の package name。省略すると expo-application の `applicationId` → react-native-device-info の `getBundleId()` の順に取る */
|
|
42
|
+
packageName?: string | null;
|
|
43
|
+
};
|
|
44
|
+
/** {@link detectApp} の結果 */
|
|
45
|
+
export type DetectedApp = {
|
|
46
|
+
platform: 'ios' | 'android';
|
|
47
|
+
/** ネイティブのバージョン */
|
|
48
|
+
version: string;
|
|
49
|
+
/** iOS は bundleId、Android は package name */
|
|
50
|
+
appId: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* local version と ID を求める。順に試す: 1. 明示的に渡された値 2. expo-application 3. react-native-device-info。
|
|
54
|
+
* どれでも取れなければ、何を入れればよいかを書いた `LOCAL_VERSION_UNAVAILABLE` を投げる
|
|
55
|
+
*/
|
|
56
|
+
export declare const detectApp: (platform: 'ios' | 'android', params?: DetectParams) => DetectedApp;
|
|
57
|
+
/** {@link detect} の結果。判定しない環境なら `skipped` に理由が入る */
|
|
58
|
+
export type DetectResult = {
|
|
59
|
+
skipped: SkipReason;
|
|
60
|
+
} | ({
|
|
61
|
+
skipped: null;
|
|
62
|
+
} & DetectedApp);
|
|
63
|
+
/**
|
|
64
|
+
* `Platform.OS` から、判定しない環境か({@link getSkipReason})を見て、判定する環境なら local version と ID を求める
|
|
65
|
+
* ({@link detectApp})。取れなければ `LOCAL_VERSION_UNAVAILABLE` を投げる
|
|
66
|
+
*/
|
|
67
|
+
export declare const detect: (params?: DetectParams) => DetectResult;
|
|
68
|
+
//# sourceMappingURL=detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAwDA;;;;GAIG;AACH,eAAO,MAAM,aAAa,QAAO,MAAM,GAAG,IAEZ,CAAC;AAE/B;;;GAGG;AACH,eAAO,MAAM,WAAW,QAAO,MAAM,GAAG,IAC+B,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAGnD,CAAC;AAEH;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,sBAAsB,GAAG,SAAS,CAAC;AAEpE;;;GAGG;AACH,eAAO,MAAM,aAAa,QAAQ,MAAM,KAAiB,UAAU,GAAG,IAYrE,CAAC;AAEF,iCAAiC;AACjC,MAAM,MAAM,YAAY,GAAG;IACzB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iHAAiH;IACjH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,yHAAyH;IACzH,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,4BAA4B;AAC5B,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;IAC5B,kBAAkB;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAKF;;;GAGG;AACH,eAAO,MAAM,SAAS,aACV,KAAK,GAAG,SAAS,WACnB,YAAY,KACnB,WAiBF,CAAC;AAEF,oDAAoD;AACpD,MAAM,MAAM,YAAY,GACpB;IAAE,OAAO,EAAE,UAAU,CAAA;CAAE,GACvB,CAAC;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG,WAAW,CAAC,CAAC;AAEtC;;;GAGG;AACH,eAAO,MAAM,MAAM,YAAY,YAAY,KAAQ,YAOlD,CAAC"}
|
package/dist/detect.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// local version / ID の自動取得と、何もしない環境の判定
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
import { StoreVersionError } from './errors.js';
|
|
4
|
+
/** `default` export の形(CJS / ESM の相互運用でどちらにもなりうる)でも、名前付きの形でも同じものを取り出す */
|
|
5
|
+
const pick = (mod, key) => {
|
|
6
|
+
if (typeof mod !== 'object' || mod === null) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const value = mod[key];
|
|
10
|
+
if (value !== undefined) {
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
const fallback = mod.default;
|
|
14
|
+
return typeof fallback === 'object' && fallback !== null
|
|
15
|
+
? fallback[key]
|
|
16
|
+
: undefined;
|
|
17
|
+
};
|
|
18
|
+
const nonEmpty = (value) => typeof value === 'string' && value !== '' ? value : null;
|
|
19
|
+
const fromExpoApplication = (key) => {
|
|
20
|
+
try {
|
|
21
|
+
return nonEmpty(pick(require('expo-application'), key));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const fromDeviceInfo = (key) => {
|
|
28
|
+
try {
|
|
29
|
+
const fn = pick(require('react-native-device-info'), key);
|
|
30
|
+
// ネイティブモジュールが無い(Expo Go など)と呼び出しで throw するので、ここも catch に入れる
|
|
31
|
+
return typeof fn === 'function' ? nonEmpty(fn()) : null;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* ネイティブのバージョン(expo-application → react-native-device-info の順)。取れなければ null。
|
|
39
|
+
* v1 互換の checkVersion は version と ID を別々に省略できるので、{@link detectApp} を通さずにこれを直接使う
|
|
40
|
+
* (パッケージの入口からは export しない)
|
|
41
|
+
*/
|
|
42
|
+
export const nativeVersion = () => fromExpoApplication('nativeApplicationVersion') ??
|
|
43
|
+
fromDeviceInfo('getVersion');
|
|
44
|
+
/**
|
|
45
|
+
* ネイティブの ID(bundleId / package name。expo-application → react-native-device-info の順)。取れなければ null。
|
|
46
|
+
* checkVersion も直接使う({@link nativeVersion} と同じ理由)
|
|
47
|
+
*/
|
|
48
|
+
export const nativeAppId = () => fromExpoApplication('applicationId') ?? fromDeviceInfo('getBundleId');
|
|
49
|
+
/**
|
|
50
|
+
* Expo Go の applicationId(Expo Go 自体のストア版と比べてしまうのを防ぐ。expo-constants には依存しない)。
|
|
51
|
+
* iOS の bundle ID は `host.exp.Exponent`(E が大文字。@expo/cli の EXPO_GO_BUNDLE_IDENTIFIER)、
|
|
52
|
+
* Android の package name は `host.exp.exponent`
|
|
53
|
+
*/
|
|
54
|
+
export const EXPO_GO_APPLICATION_IDS = Object.freeze([
|
|
55
|
+
'host.exp.Exponent',
|
|
56
|
+
'host.exp.exponent',
|
|
57
|
+
]);
|
|
58
|
+
/**
|
|
59
|
+
* 判定をしない環境なら、その理由を返す。判定する環境なら null。
|
|
60
|
+
* Expo Go はネイティブの ID(expo-application / react-native-device-info)で見る(明示的に渡された ID では見ない)
|
|
61
|
+
*/
|
|
62
|
+
export const getSkipReason = (os = Platform.OS) => {
|
|
63
|
+
if (os === 'web') {
|
|
64
|
+
return 'web';
|
|
65
|
+
}
|
|
66
|
+
if (os !== 'ios' && os !== 'android') {
|
|
67
|
+
return 'unsupported-platform';
|
|
68
|
+
}
|
|
69
|
+
const appId = nativeAppId();
|
|
70
|
+
if (appId !== null && EXPO_GO_APPLICATION_IDS.includes(appId)) {
|
|
71
|
+
return 'expo-go';
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
};
|
|
75
|
+
const INSTALL_HINT = 'install expo-application (Expo) or react-native-device-info (bare React Native)';
|
|
76
|
+
/**
|
|
77
|
+
* local version と ID を求める。順に試す: 1. 明示的に渡された値 2. expo-application 3. react-native-device-info。
|
|
78
|
+
* どれでも取れなければ、何を入れればよいかを書いた `LOCAL_VERSION_UNAVAILABLE` を投げる
|
|
79
|
+
*/
|
|
80
|
+
export const detectApp = (platform, params = {}) => {
|
|
81
|
+
const version = nonEmpty(params.version) ?? nativeVersion();
|
|
82
|
+
if (version === null) {
|
|
83
|
+
throw new StoreVersionError('LOCAL_VERSION_UNAVAILABLE', `could not get the app version: ${INSTALL_HINT}, or pass \`version\` (the native version: ${platform === 'ios' ? 'CFBundleShortVersionString' : 'versionName'}. Do not use Constants.expoConfig.version or package.json, which OTA updates change)`);
|
|
84
|
+
}
|
|
85
|
+
const idName = platform === 'ios' ? 'bundleId' : 'packageName';
|
|
86
|
+
const appId = nonEmpty(params[idName]) ?? nativeAppId();
|
|
87
|
+
if (appId === null) {
|
|
88
|
+
throw new StoreVersionError('LOCAL_VERSION_UNAVAILABLE', `could not get the app ${platform === 'ios' ? 'bundle ID' : 'package name'}: ${INSTALL_HINT}, or pass \`${idName}\``);
|
|
89
|
+
}
|
|
90
|
+
return { platform, version, appId };
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* `Platform.OS` から、判定しない環境か({@link getSkipReason})を見て、判定する環境なら local version と ID を求める
|
|
94
|
+
* ({@link detectApp})。取れなければ `LOCAL_VERSION_UNAVAILABLE` を投げる
|
|
95
|
+
*/
|
|
96
|
+
export const detect = (params = {}) => {
|
|
97
|
+
const os = Platform.OS;
|
|
98
|
+
const skipped = getSkipReason(os);
|
|
99
|
+
if (skipped !== null) {
|
|
100
|
+
return { skipped };
|
|
101
|
+
}
|
|
102
|
+
return { skipped: null, ...detectApp(os, params) };
|
|
103
|
+
};
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {@link StoreVersionError} の `code`。
|
|
3
|
+
*
|
|
4
|
+
* - `LOCAL_VERSION_UNAVAILABLE`: アプリのバージョン / ID が取れない
|
|
5
|
+
* - `TIMEOUT`: タイムアウト
|
|
6
|
+
* - `NETWORK`: HTTP エラー / ネットワークエラー / iTunes Lookup の応答が JSON として読めない・`results[0].version` が文字列でない
|
|
7
|
+
* - `NOT_FOUND`: ストアにアプリが無い(iTunes Lookup の results が空、Google Play の 404)
|
|
8
|
+
* - `PARSE_FAILED`: Play Store の HTML からバージョンを抽出できない
|
|
9
|
+
* - `INVALID_VERSION`: バージョンが比較できない(`Varies with device` など)
|
|
10
|
+
* - `LATEST_VERSION_FAILED`: `getLatestVersion` が throw した / 不正な値を返した
|
|
11
|
+
* - `MINIMUM_VERSION_FAILED`: `getMinimumVersion` が throw した / 不正な値を返した / タイムアウトした(cause は `TIMEOUT`)
|
|
12
|
+
* - `STORE_URL_UNAVAILABLE`: ストアの URL を取れない / 開けない。iOS の URL の lookup の失敗(起動時・ストアを開くとき。
|
|
13
|
+
* `trackViewUrl` が無い場合も)と、ストアを開く `Linking.openURL` 自体の失敗(Android は `market://` と https の両方が失敗したとき)。
|
|
14
|
+
* cause に元のエラー。判定の `status` / `error` は変えない
|
|
15
|
+
* - `POLICY_FAILED`: `policy` の関数が throw した / 不正な値を返した(`'force' | 'warning' | 'none'` 以外)
|
|
16
|
+
*/
|
|
17
|
+
export type StoreVersionErrorCode = 'LOCAL_VERSION_UNAVAILABLE' | 'TIMEOUT' | 'NETWORK' | 'NOT_FOUND' | 'PARSE_FAILED' | 'INVALID_VERSION' | 'LATEST_VERSION_FAILED' | 'MINIMUM_VERSION_FAILED' | 'STORE_URL_UNAVAILABLE' | 'POLICY_FAILED';
|
|
18
|
+
export interface StoreVersionError {
|
|
19
|
+
/** 元になったエラー(あれば) */
|
|
20
|
+
readonly cause?: unknown;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* このライブラリが `onError` などに渡すエラー。`code` で原因を区別できる。
|
|
24
|
+
* 元になったエラーがあれば `cause` に入る。
|
|
25
|
+
*/
|
|
26
|
+
export declare class StoreVersionError extends Error {
|
|
27
|
+
/** エラーの種類 */
|
|
28
|
+
readonly code: StoreVersionErrorCode;
|
|
29
|
+
constructor(code: StoreVersionErrorCode, message: string, options?: {
|
|
30
|
+
cause?: unknown;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,qBAAqB,GAC7B,2BAA2B,GAC3B,SAAS,GACT,SAAS,GACT,WAAW,GACX,cAAc,GACd,iBAAiB,GACjB,uBAAuB,GACvB,wBAAwB,GACxB,uBAAuB,GACvB,eAAe,CAAC;AAIpB,MAAM,WAAW,iBAAiB;IAChC,oBAAoB;IACpB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AAEH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,aAAa;IACb,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IAErC,YACE,IAAI,EAAE,qBAAqB,EAC3B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAc9B;CACF"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* このライブラリが `onError` などに渡すエラー。`code` で原因を区別できる。
|
|
3
|
+
* 元になったエラーがあれば `cause` に入る。
|
|
4
|
+
*/
|
|
5
|
+
// biome-ignore lint/suspicious/noUnsafeDeclarationMerging: cause の型だけを足す意図的な宣言マージ(上の interface)
|
|
6
|
+
export class StoreVersionError extends Error {
|
|
7
|
+
/** エラーの種類 */
|
|
8
|
+
code;
|
|
9
|
+
constructor(code, message, options) {
|
|
10
|
+
super(message, options);
|
|
11
|
+
this.name = 'StoreVersionError';
|
|
12
|
+
this.code = code;
|
|
13
|
+
// 古い Hermes は Error の第 2 引数(cause)を無視するので、残らなければ自分で代入する
|
|
14
|
+
if (options && 'cause' in options && !('cause' in this)) {
|
|
15
|
+
Object.defineProperty(this, 'cause', {
|
|
16
|
+
value: options.cause,
|
|
17
|
+
writable: true,
|
|
18
|
+
configurable: true,
|
|
19
|
+
enumerable: false,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** 取得先。`packageName`(v2 の既定)か、ストア URL そのもの(v1 互換。checkVersion が使う) */
|
|
2
|
+
export type AndroidTarget = {
|
|
3
|
+
packageName: string;
|
|
4
|
+
} | {
|
|
5
|
+
storeUrl: string;
|
|
6
|
+
};
|
|
7
|
+
/** {@link fetchAndroidVersion} のオプション */
|
|
8
|
+
export type AndroidFetchOptions = {
|
|
9
|
+
/** 未指定ならタイムアウトしない(`requestText` の `timeoutMs`) */
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
};
|
|
13
|
+
/** {@link fetchAndroidVersion} の結果 */
|
|
14
|
+
export type AndroidFetchResult = {
|
|
15
|
+
/** 比較できることを確かめた版 */
|
|
16
|
+
version: string;
|
|
17
|
+
/** 取得したページの URL */
|
|
18
|
+
storeUrl: string;
|
|
19
|
+
};
|
|
20
|
+
/** package name からアプリの詳細ページの URL を組み立てる。クエリ(`hl` / `gl`)は付けない */
|
|
21
|
+
export declare const playStoreUrl: (packageName: string) => string;
|
|
22
|
+
/**
|
|
23
|
+
* 抽出に使うパターン。実ページの fixture にどのパターンが当たるか(当たらないか)をテストで確かめるために出している
|
|
24
|
+
* (パッケージの入口からは export しない)
|
|
25
|
+
*/
|
|
26
|
+
export declare const PLAY_VERSION_PATTERNS: Readonly<{
|
|
27
|
+
versionSlot: RegExp;
|
|
28
|
+
emptyVersionSlot: RegExp;
|
|
29
|
+
v1: RegExp;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Play Store の HTML から版を取り出す。
|
|
33
|
+
*
|
|
34
|
+
* 1. 版の位置の文字列(構造に錨を下ろしたパターン)
|
|
35
|
+
* 2. 版の位置が空なら `INVALID_VERSION`(Varies with device)
|
|
36
|
+
* 3. v1 のパターン(数字とドットだけ)
|
|
37
|
+
* 4. どれにも当たらなければ `PARSE_FAILED`
|
|
38
|
+
*
|
|
39
|
+
* 取れた文字列が比較できない(`50616-r-2026-11-03` など)なら `INVALID_VERSION`
|
|
40
|
+
*/
|
|
41
|
+
export declare const extractPlayVersion: (html: string) => string;
|
|
42
|
+
/**
|
|
43
|
+
* Play Store のアプリの詳細ページを取得して版を返す。
|
|
44
|
+
*
|
|
45
|
+
* - 404 → `NOT_FOUND`(Google Play に無い: 未公開・取り下げなど。iOS の results が空と同じ扱い。本文の読み込みに失敗しても同じ)
|
|
46
|
+
* - 通信の失敗 / 200・404 以外 → `NETWORK`
|
|
47
|
+
* - 版の位置が空 / 比較できない版 → `INVALID_VERSION`、抽出できない → `PARSE_FAILED`({@link extractPlayVersion})
|
|
48
|
+
* - タイムアウト → `TIMEOUT`、`signal` の abort → `AbortError`
|
|
49
|
+
*
|
|
50
|
+
* `{ storeUrl }` は URL をそのまま取得する(検証も書き換えもしない。v1 互換の入口で、URL の検証は呼び出し側で行う)
|
|
51
|
+
*/
|
|
52
|
+
export declare const fetchAndroidVersion: (target: AndroidTarget, { timeoutMs, signal }?: AndroidFetchOptions) => Promise<AndroidFetchResult>;
|
|
53
|
+
//# sourceMappingURL=android.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"android.d.ts","sourceRoot":"","sources":["../../src/fetchers/android.ts"],"names":[],"mappings":"AAKA,sEAAsE;AACtE,MAAM,MAAM,aAAa,GAAG;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,yCAAyC;AACzC,MAAM,MAAM,mBAAmB,GAAG;IAChC,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,sCAAsC;AACtC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,iEAAiE;AACjE,eAAO,MAAM,YAAY,gBAAiB,MAAM,KAAG,MACiC,CAAC;AAkBrF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,EAAE,EAAE,MAAM,CAAC;CACZ,CAIC,CAAC;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,SAAU,MAAM,KAAG,MAoBjD,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,WACtB,aAAa,0BACE,mBAAmB,KACzC,OAAO,CAAC,kBAAkB,CAiC5B,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Google Play(アプリの詳細ページの HTML)の取得。react-native を import しない(Node でも動かす)
|
|
2
|
+
import { assertValidVersion } from '../compare.js';
|
|
3
|
+
import { StoreVersionError } from '../errors.js';
|
|
4
|
+
import { failedResponseStatusOf, requestText } from '../request.js';
|
|
5
|
+
/** package name からアプリの詳細ページの URL を組み立てる。クエリ(`hl` / `gl`)は付けない */
|
|
6
|
+
export const playStoreUrl = (packageName) => `https://play.google.com/store/apps/details?id=${encodeURIComponent(packageName)}`;
|
|
7
|
+
// 実ページ(__tests__/__fixtures__/store/)に対して確かめたパターン。
|
|
8
|
+
// 版は `,null,null,null,[[["<versionName>"]],[[[N]],[[[M,"<最低 OS>"]]]]]` に入っている。
|
|
9
|
+
// `ds:N` の番号や hash は取得ごとに変わるので錨にしない。版より前にレビューの要素(古い版を持つ)が来ることがあるので、
|
|
10
|
+
// 数字だけを拾うパターンを先に試すと古い版を返してしまう
|
|
11
|
+
/** 1. 版の位置の文字列を取る(構造に錨を下ろす。数字でない versionName も取れる) */
|
|
12
|
+
const VERSION_SLOT = /\[\[\["([^"\\]{1,64})"\]\],\[\[\[\d+\]\],\[\[\[\d+,"[^"\\]{0,16}"\]\]\]\]\]/;
|
|
13
|
+
/**
|
|
14
|
+
* 版の位置が空(`[null,[]]`。Google Play の「Varies with device」。ページにその文言は無い)。
|
|
15
|
+
* 版の位置の前置き(`,null,null,null,`)と空の版(`[[null,[]],`)だけを錨にする。後ろ(最低 OS など)まで決め打ちにすると、
|
|
16
|
+
* 少し違う形の Varies のページで当たらず、v1 のパターンが別の場所(レビューの古い版など)を拾ってしまうため
|
|
17
|
+
*/
|
|
18
|
+
const EMPTY_VERSION_SLOT = /,null,null,null,\[\[null,\[\]\],/;
|
|
19
|
+
/** 2. v1 のパターン(数字とドットだけの版) */
|
|
20
|
+
const V1_VERSION = /\[\[\[['"]((\d+\.)+\d+)['"]\]\],/;
|
|
21
|
+
/**
|
|
22
|
+
* 抽出に使うパターン。実ページの fixture にどのパターンが当たるか(当たらないか)をテストで確かめるために出している
|
|
23
|
+
* (パッケージの入口からは export しない)
|
|
24
|
+
*/
|
|
25
|
+
export const PLAY_VERSION_PATTERNS = Object.freeze({
|
|
26
|
+
versionSlot: VERSION_SLOT,
|
|
27
|
+
emptyVersionSlot: EMPTY_VERSION_SLOT,
|
|
28
|
+
v1: V1_VERSION,
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* Play Store の HTML から版を取り出す。
|
|
32
|
+
*
|
|
33
|
+
* 1. 版の位置の文字列(構造に錨を下ろしたパターン)
|
|
34
|
+
* 2. 版の位置が空なら `INVALID_VERSION`(Varies with device)
|
|
35
|
+
* 3. v1 のパターン(数字とドットだけ)
|
|
36
|
+
* 4. どれにも当たらなければ `PARSE_FAILED`
|
|
37
|
+
*
|
|
38
|
+
* 取れた文字列が比較できない(`50616-r-2026-11-03` など)なら `INVALID_VERSION`
|
|
39
|
+
*/
|
|
40
|
+
export const extractPlayVersion = (html) => {
|
|
41
|
+
let version = html.match(VERSION_SLOT)?.[1];
|
|
42
|
+
if (version === undefined) {
|
|
43
|
+
// 版の位置が空だと分かるなら、ほかの場所の数字を拾わずに Varies として扱う
|
|
44
|
+
if (EMPTY_VERSION_SLOT.test(html)) {
|
|
45
|
+
throw new StoreVersionError('INVALID_VERSION', 'the Play Store page has no version for this app (Varies with device)');
|
|
46
|
+
}
|
|
47
|
+
version = html.match(V1_VERSION)?.[1];
|
|
48
|
+
}
|
|
49
|
+
if (version === undefined) {
|
|
50
|
+
throw new StoreVersionError('PARSE_FAILED', 'could not find the version in the Play Store page');
|
|
51
|
+
}
|
|
52
|
+
assertValidVersion(version, 'remote');
|
|
53
|
+
return version;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Play Store のアプリの詳細ページを取得して版を返す。
|
|
57
|
+
*
|
|
58
|
+
* - 404 → `NOT_FOUND`(Google Play に無い: 未公開・取り下げなど。iOS の results が空と同じ扱い。本文の読み込みに失敗しても同じ)
|
|
59
|
+
* - 通信の失敗 / 200・404 以外 → `NETWORK`
|
|
60
|
+
* - 版の位置が空 / 比較できない版 → `INVALID_VERSION`、抽出できない → `PARSE_FAILED`({@link extractPlayVersion})
|
|
61
|
+
* - タイムアウト → `TIMEOUT`、`signal` の abort → `AbortError`
|
|
62
|
+
*
|
|
63
|
+
* `{ storeUrl }` は URL をそのまま取得する(検証も書き換えもしない。v1 互換の入口で、URL の検証は呼び出し側で行う)
|
|
64
|
+
*/
|
|
65
|
+
export const fetchAndroidVersion = async (target, { timeoutMs, signal } = {}) => {
|
|
66
|
+
const url = 'packageName' in target
|
|
67
|
+
? playStoreUrl(target.packageName)
|
|
68
|
+
: target.storeUrl;
|
|
69
|
+
const notFound = () => new StoreVersionError('NOT_FOUND', 'packageName' in target
|
|
70
|
+
? `the app (packageName "${target.packageName}") is not found on Google Play`
|
|
71
|
+
: `the app (${url}) is not found on Google Play`);
|
|
72
|
+
let response;
|
|
73
|
+
try {
|
|
74
|
+
response = await requestText(url, { timeoutMs, signal });
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
// 404 の本文の読み込みに失敗しても、status が 404 なら NOT_FOUND(本文の読み方で code が変わらないように)
|
|
78
|
+
if (failedResponseStatusOf(error) === 404) {
|
|
79
|
+
throw notFound();
|
|
80
|
+
}
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
const { status, body } = response;
|
|
84
|
+
if (status === 404) {
|
|
85
|
+
throw notFound();
|
|
86
|
+
}
|
|
87
|
+
if (status !== 200) {
|
|
88
|
+
throw new StoreVersionError('NETWORK', `the Play Store responded with status ${status}`);
|
|
89
|
+
}
|
|
90
|
+
return { version: extractPlayVersion(body), storeUrl: url };
|
|
91
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** {@link VersionGetter} が受け取る値 */
|
|
2
|
+
export type VersionGetterContext = {
|
|
3
|
+
/** 判定しているプラットフォーム */
|
|
4
|
+
platform: 'ios' | 'android';
|
|
5
|
+
/**
|
|
6
|
+
* タイムアウト時(と判定の取り消し時)に abort される。fetch に渡せば通信も止まる。
|
|
7
|
+
* 1 回の判定の `getLatestVersion` と `getMinimumVersion` には同じ signal が渡り、判定ごとに新しいものになる
|
|
8
|
+
* (同じ取得元から両方を取るときは、signal ごとに取得を 1 回にまとめられる)
|
|
9
|
+
*/
|
|
10
|
+
signal: AbortSignal;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* バージョン文字列を 1 つ返す async 関数(`getLatestVersion` / `getMinimumVersion`)。
|
|
14
|
+
* Firebase Remote Config や自前の JSON などから返す想定。失敗したら throw すればよい。
|
|
15
|
+
* `signal` を fetch に渡さないと、タイムアウト後も通信は続く(判定には使われない)
|
|
16
|
+
*/
|
|
17
|
+
export type VersionGetter = (ctx: VersionGetterContext) => Promise<string | null | undefined>;
|
|
18
|
+
/**
|
|
19
|
+
* `getLatestVersion` を呼ぶ。throw した / 空文字・null・undefined / 文字列でない値なら `LATEST_VERSION_FAILED`
|
|
20
|
+
* (throw したときは cause に元のエラー)。比較できない文字列はそのまま返す(判定で `INVALID_VERSION` になる)
|
|
21
|
+
*/
|
|
22
|
+
export declare const callLatestVersion: (getter: VersionGetter, ctx: VersionGetterContext) => Promise<string>;
|
|
23
|
+
/**
|
|
24
|
+
* `getMinimumVersion` を呼ぶ。空文字 / null / undefined は「指定なし」で null を返す。
|
|
25
|
+
* throw した / 文字列でない値なら `MINIMUM_VERSION_FAILED`(throw したときは cause に元のエラー)。
|
|
26
|
+
* 比較できない文字列はそのまま返す(判定で最低バージョンだけを飛ばす)
|
|
27
|
+
*/
|
|
28
|
+
export declare const callMinimumVersion: (getter: VersionGetter, ctx: VersionGetterContext) => Promise<string | null>;
|
|
29
|
+
//# sourceMappingURL=custom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../src/fetchers/custom.ts"],"names":[],"mappings":"AAGA,mCAAmC;AACnC,MAAM,MAAM,oBAAoB,GAAG;IACjC,qBAAqB;IACrB,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;IAC5B;;;;OAIG;IACH,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,GAAG,EAAE,oBAAoB,KACtB,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAKxC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,WACpB,aAAa,OAChB,oBAAoB,KACxB,OAAO,CAAC,MAAM,CAkBhB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,WACrB,aAAa,OAChB,oBAAoB,KACxB,OAAO,CAAC,MAAM,GAAG,IAAI,CAqBvB,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// 開発者が渡す取得元(getLatestVersion / getMinimumVersion)の呼び出し。react-native を import しない
|
|
2
|
+
import { StoreVersionError } from '../errors.js';
|
|
3
|
+
const describe = (value) => value === null ? 'null' : `(${typeof value})`;
|
|
4
|
+
/**
|
|
5
|
+
* `getLatestVersion` を呼ぶ。throw した / 空文字・null・undefined / 文字列でない値なら `LATEST_VERSION_FAILED`
|
|
6
|
+
* (throw したときは cause に元のエラー)。比較できない文字列はそのまま返す(判定で `INVALID_VERSION` になる)
|
|
7
|
+
*/
|
|
8
|
+
export const callLatestVersion = async (getter, ctx) => {
|
|
9
|
+
let value;
|
|
10
|
+
try {
|
|
11
|
+
value = await getter(ctx);
|
|
12
|
+
}
|
|
13
|
+
catch (cause) {
|
|
14
|
+
throw new StoreVersionError('LATEST_VERSION_FAILED', 'getLatestVersion threw', { cause });
|
|
15
|
+
}
|
|
16
|
+
if (typeof value !== 'string' || value === '') {
|
|
17
|
+
throw new StoreVersionError('LATEST_VERSION_FAILED', `getLatestVersion returned ${value === '' ? 'an empty string' : `an invalid value: ${describe(value)}`}`);
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* `getMinimumVersion` を呼ぶ。空文字 / null / undefined は「指定なし」で null を返す。
|
|
23
|
+
* throw した / 文字列でない値なら `MINIMUM_VERSION_FAILED`(throw したときは cause に元のエラー)。
|
|
24
|
+
* 比較できない文字列はそのまま返す(判定で最低バージョンだけを飛ばす)
|
|
25
|
+
*/
|
|
26
|
+
export const callMinimumVersion = async (getter, ctx) => {
|
|
27
|
+
let value;
|
|
28
|
+
try {
|
|
29
|
+
value = await getter(ctx);
|
|
30
|
+
}
|
|
31
|
+
catch (cause) {
|
|
32
|
+
throw new StoreVersionError('MINIMUM_VERSION_FAILED', 'getMinimumVersion threw', { cause });
|
|
33
|
+
}
|
|
34
|
+
if (value == null || value === '') {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (typeof value !== 'string') {
|
|
38
|
+
throw new StoreVersionError('MINIMUM_VERSION_FAILED', `getMinimumVersion returned an invalid value: ${describe(value)}`);
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { StoreVersionError } from '../errors.ts';
|
|
2
|
+
/** lookup の引き先。`bundleId`(v2 の既定)か、ストア URL から抜き出した数字の App ID(v1 互換の URL 方式) */
|
|
3
|
+
export type IOSLookupTarget = {
|
|
4
|
+
bundleId: string;
|
|
5
|
+
} | {
|
|
6
|
+
appId: string;
|
|
7
|
+
};
|
|
8
|
+
/** {@link lookupIOS} のオプション */
|
|
9
|
+
export type IOSLookupOptions = {
|
|
10
|
+
/** 未指定ならタイムアウトしない(`requestText` の `timeoutMs`) */
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
};
|
|
14
|
+
/** {@link lookupIOS} の結果 */
|
|
15
|
+
export type IOSLookupResult = {
|
|
16
|
+
/** `results[0].version` */
|
|
17
|
+
version: string;
|
|
18
|
+
/** `results[0].trackViewUrl`(ストアを開く URL)。応答に無ければ null */
|
|
19
|
+
storeUrl: string | null;
|
|
20
|
+
/** `results[0].releaseNotes`。応答に無ければ undefined */
|
|
21
|
+
releaseNotes?: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* App Store の URL(`https://apps.apple.com/jp/app/github/id1477376905` など)から数字の App ID を抜き出す。
|
|
25
|
+
* v1 と同じ正規表現。取れなければ null
|
|
26
|
+
*/
|
|
27
|
+
export declare const appIdFromStoreUrl: (storeUrl: string) => string | null;
|
|
28
|
+
/** lookup の URL(タイムスタンプを付ける前。in-flight の共有のキーにもなる) */
|
|
29
|
+
export declare const lookupUrl: (target: IOSLookupTarget, country: string) => string;
|
|
30
|
+
/**
|
|
31
|
+
* iTunes Lookup API で App Store の版を引く。v1 と同じく URL にタイムスタンプを付け、`cache-control: no-cache` を送る。
|
|
32
|
+
*
|
|
33
|
+
* - 通信の失敗 / 200 以外 / JSON として読めない / `results[0].version` が文字列でない → `NETWORK`
|
|
34
|
+
* - `results` が空(未リリース・その国で未配信)→ `NOT_FOUND`
|
|
35
|
+
* - タイムアウト → `TIMEOUT`、`signal` の abort → `AbortError`
|
|
36
|
+
*/
|
|
37
|
+
export declare const lookupIOS: (target: IOSLookupTarget, country: string, { timeoutMs, signal }?: IOSLookupOptions) => Promise<IOSLookupResult>;
|
|
38
|
+
/**
|
|
39
|
+
* ストアを開く URL の取得結果。reject しない形にしてある(判定とは別に後から受け取るので、
|
|
40
|
+
* 誰も待っていなくても unhandled rejection にならないように)
|
|
41
|
+
*/
|
|
42
|
+
export type StoreUrlResult = {
|
|
43
|
+
storeUrl: string;
|
|
44
|
+
error: null;
|
|
45
|
+
} | {
|
|
46
|
+
storeUrl: null;
|
|
47
|
+
error: StoreVersionError;
|
|
48
|
+
};
|
|
49
|
+
/** lookup に失敗して URL を取れなかったときの `STORE_URL_UNAVAILABLE`。cause に lookup のエラー */
|
|
50
|
+
export declare const storeUrlUnavailable: (cause: unknown) => {
|
|
51
|
+
storeUrl: null;
|
|
52
|
+
error: StoreVersionError;
|
|
53
|
+
};
|
|
54
|
+
/** lookup の `storeUrl` から {@link StoreUrlResult} を作る(trackViewUrl が無ければ `STORE_URL_UNAVAILABLE`) */
|
|
55
|
+
export declare const toStoreUrlResult: (storeUrl: string | null) => StoreUrlResult;
|
|
56
|
+
/**
|
|
57
|
+
* bundleId で lookup して、ストアを開く URL(`trackViewUrl`)だけを取る。`version` / `releaseNotes` は使わない
|
|
58
|
+
* (`getLatestVersion` の値と食い違いうるため)。失敗は reject せず `STORE_URL_UNAVAILABLE`(cause に lookup のエラー)で返す
|
|
59
|
+
*/
|
|
60
|
+
export declare const lookupStoreUrl: (bundleId: string, country: string, options?: IOSLookupOptions) => Promise<StoreUrlResult>;
|
|
61
|
+
//# sourceMappingURL=ios.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ios.d.ts","sourceRoot":"","sources":["../../src/fetchers/ios.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGjD,+EAA+E;AAC/E,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE,+BAA+B;AAC/B,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,4BAA4B;AAC5B,MAAM,MAAM,eAAe,GAAG;IAC5B,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,aAAc,MAAM,KAAG,MAAM,GAAG,IACd,CAAC;AAEjD,sDAAsD;AACtD,eAAO,MAAM,SAAS,WAAY,eAAe,WAAW,MAAM,KAAG,MAMpE,CAAC;AAOF;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,WACZ,eAAe,WACd,MAAM,0BACQ,gBAAgB,KACtC,OAAO,CAAC,eAAe,CAyDzB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GACjC;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAEjD,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,UACvB,OAAO,KACb;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAO3C,CAAC;AAEH,oGAAoG;AACpG,eAAO,MAAM,gBAAgB,aAAc,MAAM,GAAG,IAAI,KAAG,cAS5B,CAAC;AAEhC;;;GAGG;AACH,eAAO,MAAM,cAAc,aACf,MAAM,WACP,MAAM,YACN,gBAAgB,KACxB,OAAO,CAAC,cAAc,CAItB,CAAC"}
|