@efffrida/gplayapi 0.0.15 → 0.0.17
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/dist/GooglePlayApi.d.ts +70 -24
- package/dist/GooglePlayApi.d.ts.map +1 -1
- package/dist/GooglePlayApi.js +92 -52
- package/dist/GooglePlayApi.js.map +1 -1
- package/dist/internal/device.d.ts +9 -3
- package/dist/internal/device.d.ts.map +1 -1
- package/dist/internal/device.js +5 -4
- package/dist/internal/device.js.map +1 -1
- package/package.json +1 -1
- package/src/GooglePlayApi.ts +235 -223
- package/src/internal/device.ts +9 -9
package/dist/GooglePlayApi.d.ts
CHANGED
|
@@ -4,72 +4,114 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @since 1.0.0
|
|
6
6
|
*/
|
|
7
|
-
import type * as PlatformError from "effect/PlatformError";
|
|
8
7
|
import type * as Schema from "effect/Schema";
|
|
9
8
|
import type * as Scope from "effect/Scope";
|
|
10
9
|
import type * as HttpClientError from "effect/unstable/http/HttpClientError";
|
|
10
|
+
import * as Cause from "effect/Cause";
|
|
11
|
+
import * as Crypto from "effect/Crypto";
|
|
11
12
|
import * as Effect from "effect/Effect";
|
|
12
13
|
import * as FileSystem from "effect/FileSystem";
|
|
14
|
+
import * as PlatformError from "effect/PlatformError";
|
|
15
|
+
import * as Stream from "effect/Stream";
|
|
13
16
|
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
14
|
-
import type { AndroidDevice } from "./internal/device.ts";
|
|
15
17
|
import { type BulkDetailsResponse, type BuyResponse, type DeliveryResponse, type DetailsResponse } from "./generated/GooglePlay_pb.ts";
|
|
18
|
+
import { Service as AndroidDeviceService } from "./internal/device.ts";
|
|
16
19
|
export {
|
|
17
20
|
/**
|
|
18
21
|
* @since 1.0.0
|
|
19
22
|
* @category Device
|
|
20
23
|
*/
|
|
21
|
-
AndroidDevice,
|
|
24
|
+
AndroidDevice,
|
|
25
|
+
/**
|
|
26
|
+
* @since 1.0.0
|
|
27
|
+
* @category Device
|
|
28
|
+
*/
|
|
29
|
+
Service as AndroidDeviceService, } from "./internal/device.ts";
|
|
22
30
|
/**
|
|
23
31
|
* @since 1.0.0
|
|
24
32
|
* @category API
|
|
25
33
|
*/
|
|
26
|
-
export declare const details: (
|
|
34
|
+
export declare const details: (bundleIdentifier: string) => Effect.Effect<DetailsResponse, HttpClientError.HttpClientError | Schema.SchemaError, HttpClient.HttpClient | AndroidDeviceService>;
|
|
27
35
|
/**
|
|
28
36
|
* @since 1.0.0
|
|
29
37
|
* @category API
|
|
30
38
|
*/
|
|
31
|
-
export declare const bulkDetails: (
|
|
39
|
+
export declare const bulkDetails: (bundleIdentifier: string) => Effect.Effect<BulkDetailsResponse, HttpClientError.HttpClientError | Schema.SchemaError, HttpClient.HttpClient | AndroidDeviceService>;
|
|
32
40
|
/**
|
|
33
41
|
* @since 1.0.0
|
|
34
42
|
* @category API
|
|
35
43
|
*/
|
|
36
|
-
export declare const purchase: (
|
|
44
|
+
export declare const purchase: (bundleIdentifier: string, options: {
|
|
37
45
|
offerType: number;
|
|
38
46
|
versionCode: number | bigint;
|
|
39
47
|
certificateHash?: string;
|
|
40
|
-
}) => Effect.Effect<BuyResponse, HttpClientError.HttpClientError | Schema.SchemaError, HttpClient.HttpClient
|
|
41
|
-
offerType: number;
|
|
42
|
-
versionCode: number | bigint;
|
|
43
|
-
certificateHash?: string;
|
|
44
|
-
}, device: AndroidDevice) => Effect.Effect<BuyResponse, HttpClientError.HttpClientError | Schema.SchemaError, HttpClient.HttpClient>);
|
|
48
|
+
}) => Effect.Effect<BuyResponse, HttpClientError.HttpClientError | Schema.SchemaError, HttpClient.HttpClient | AndroidDeviceService>;
|
|
45
49
|
/**
|
|
46
50
|
* @since 1.0.0
|
|
47
51
|
* @category API
|
|
48
52
|
*/
|
|
49
|
-
export declare const delivery: (
|
|
50
|
-
offerType: number;
|
|
51
|
-
deliveryToken: string;
|
|
52
|
-
versionCode: number | bigint;
|
|
53
|
-
certificateHash?: string | undefined;
|
|
54
|
-
}) => Effect.Effect<DeliveryResponse, HttpClientError.HttpClientError | Schema.SchemaError, HttpClient.HttpClient>) & ((bundleIdentifier: string, options: {
|
|
53
|
+
export declare const delivery: (bundleIdentifier: string, options: {
|
|
55
54
|
offerType: number;
|
|
56
55
|
deliveryToken: string;
|
|
57
56
|
versionCode: number | bigint;
|
|
58
57
|
certificateHash?: string | undefined;
|
|
59
|
-
}
|
|
58
|
+
}) => Effect.Effect<DeliveryResponse, HttpClientError.HttpClientError | Schema.SchemaError, HttpClient.HttpClient | AndroidDeviceService>;
|
|
59
|
+
/**
|
|
60
|
+
* @since 1.0.0
|
|
61
|
+
* @category API
|
|
62
|
+
*/
|
|
63
|
+
export declare const downloadToStreams: (bundleIdentifier: string, options?: {
|
|
64
|
+
offerType?: number | undefined;
|
|
65
|
+
versionCode?: number | bigint | undefined;
|
|
66
|
+
} | undefined) => Effect.Effect<readonly [{
|
|
67
|
+
stream: Stream.Stream<Uint8Array, HttpClientError.HttpClientError, never>;
|
|
68
|
+
integrity: {
|
|
69
|
+
"SHA-1": string;
|
|
70
|
+
} | {
|
|
71
|
+
"SHA-256": string;
|
|
72
|
+
} | {
|
|
73
|
+
"SHA-384": string;
|
|
74
|
+
} | {
|
|
75
|
+
"SHA-512": string;
|
|
76
|
+
};
|
|
77
|
+
size: bigint;
|
|
78
|
+
name: string;
|
|
79
|
+
url: string;
|
|
80
|
+
}, ...{
|
|
81
|
+
stream: Stream.Stream<Uint8Array, HttpClientError.HttpClientError, never>;
|
|
82
|
+
integrity: {
|
|
83
|
+
"SHA-1": string;
|
|
84
|
+
} | {
|
|
85
|
+
"SHA-256": string;
|
|
86
|
+
} | {
|
|
87
|
+
"SHA-384": string;
|
|
88
|
+
} | {
|
|
89
|
+
"SHA-512": string;
|
|
90
|
+
};
|
|
91
|
+
size: bigint;
|
|
92
|
+
name: string;
|
|
93
|
+
url: string;
|
|
94
|
+
}[]], HttpClientError.HttpClientError | Schema.SchemaError | Cause.NoSuchElementError, HttpClient.HttpClient | AndroidDeviceService>;
|
|
60
95
|
/**
|
|
61
96
|
* @since 1.0.0
|
|
62
97
|
* @category API
|
|
63
98
|
*/
|
|
64
|
-
export declare const
|
|
99
|
+
export declare const downloadToDisk: (bundleIdentifier: string, options?: {
|
|
100
|
+
offerType?: number | undefined;
|
|
101
|
+
versionCode?: number | bigint | undefined;
|
|
102
|
+
} | undefined) => Effect.Effect<readonly [{
|
|
65
103
|
url: string;
|
|
66
104
|
name: string;
|
|
67
105
|
file: string;
|
|
68
106
|
size: bigint;
|
|
69
107
|
integrity: {
|
|
70
|
-
|
|
108
|
+
"SHA-1": string;
|
|
71
109
|
} | {
|
|
72
|
-
|
|
110
|
+
"SHA-256": string;
|
|
111
|
+
} | {
|
|
112
|
+
"SHA-384": string;
|
|
113
|
+
} | {
|
|
114
|
+
"SHA-512": string;
|
|
73
115
|
};
|
|
74
116
|
}, ...{
|
|
75
117
|
url: string;
|
|
@@ -77,9 +119,13 @@ export declare const download: (device: AndroidDevice, bundleIdentifier: string)
|
|
|
77
119
|
file: string;
|
|
78
120
|
size: bigint;
|
|
79
121
|
integrity: {
|
|
80
|
-
|
|
122
|
+
"SHA-1": string;
|
|
123
|
+
} | {
|
|
124
|
+
"SHA-256": string;
|
|
125
|
+
} | {
|
|
126
|
+
"SHA-384": string;
|
|
81
127
|
} | {
|
|
82
|
-
|
|
128
|
+
"SHA-512": string;
|
|
83
129
|
};
|
|
84
|
-
}[]], HttpClientError.HttpClientError | Schema.SchemaError | PlatformError.PlatformError, HttpClient.HttpClient | FileSystem.FileSystem | Scope.Scope>;
|
|
130
|
+
}[]], HttpClientError.HttpClientError | Schema.SchemaError | PlatformError.PlatformError | Cause.NoSuchElementError, HttpClient.HttpClient | AndroidDeviceService | FileSystem.FileSystem | Scope.Scope | Crypto.Crypto>;
|
|
85
131
|
//# sourceMappingURL=GooglePlayApi.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GooglePlayApi.d.ts","sourceRoot":"","sources":["../src/GooglePlayApi.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,KAAK,
|
|
1
|
+
{"version":3,"file":"GooglePlayApi.d.ts","sourceRoot":"","sources":["../src/GooglePlayApi.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,KAAK,eAAe,MAAM,sCAAsC,CAAC;AAG7E,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,aAAa,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAG9D,OAAO,EAEH,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACvB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAMvE,OAAO;AACH;;;GAGG;AACH,aAAa;AAEb;;;GAGG;AACH,OAAO,IAAI,oBAAoB,GAClC,MAAM,sBAAsB,CAAC;AAE9B;;;GAGG;AACH,eAAO,MAAM,OAAO,kKAkBlB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,WAAW,sKAuBtB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,QAAQ;eAEK,MAAM;iBAAe,MAAM,GAAG,MAAM;sBAAoB,MAAM;oIAsBtF,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,QAAQ;eAGF,MAAM;mBACF,MAAM;iBACR,MAAM,GAAG,MAAM;sBACV,MAAM,GAAG,SAAS;yIAwB1C,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,iBAAiB;gBAIJ,MAAM,GAAG,SAAS;kBAChB,MAAM,GAAG,MAAM,GAAG,SAAS;;YAKvC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC,eAAe,EAAE,KAAK,CAAC;eAC9D;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE;UAChG,MAAM;UACN,MAAM;SACP,MAAM;;YAJH,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC,eAAe,EAAE,KAAK,CAAC;eAC9D;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE;UAChG,MAAM;UACN,MAAM;SACP,MAAM;oIAqFjB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,cAAc;gBAID,MAAM,GAAG,SAAS;kBAChB,MAAM,GAAG,MAAM,GAAG,SAAS;;SAK1C,MAAM;UACL,MAAM;UACN,MAAM;UACN,MAAM;eACD;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE;;SAJjG,MAAM;UACL,MAAM;UACN,MAAM;UACN,MAAM;eACD;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE;wNAqE5G,CAAC"}
|
package/dist/GooglePlayApi.js
CHANGED
|
@@ -5,14 +5,17 @@
|
|
|
5
5
|
* @since 1.0.0
|
|
6
6
|
*/
|
|
7
7
|
import * as Array from "effect/Array";
|
|
8
|
+
import * as Cause from "effect/Cause";
|
|
9
|
+
import * as Crypto from "effect/Crypto";
|
|
8
10
|
import * as Effect from "effect/Effect";
|
|
9
11
|
import * as FileSystem from "effect/FileSystem";
|
|
10
|
-
import * as
|
|
12
|
+
import * as Match from "effect/Match";
|
|
13
|
+
import * as PlatformError from "effect/PlatformError";
|
|
11
14
|
import * as Stream from "effect/Stream";
|
|
12
15
|
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
13
16
|
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
14
|
-
import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
|
|
15
17
|
import { BulkDetailsRequestSchema } from "./generated/GooglePlay_pb.js";
|
|
18
|
+
import { Service as AndroidDeviceService } from "./internal/device.js";
|
|
16
19
|
import { decodeResponseFromResponseWrapper, encodeRequest } from "./internal/http.js";
|
|
17
20
|
/** @internal */
|
|
18
21
|
const fdfeBase = "https://android.clients.google.com/fdfe";
|
|
@@ -21,13 +24,19 @@ export {
|
|
|
21
24
|
* @since 1.0.0
|
|
22
25
|
* @category Device
|
|
23
26
|
*/
|
|
24
|
-
AndroidDevice
|
|
27
|
+
AndroidDevice,
|
|
28
|
+
/**
|
|
29
|
+
* @since 1.0.0
|
|
30
|
+
* @category Device
|
|
31
|
+
*/
|
|
32
|
+
Service as AndroidDeviceService } from "./internal/device.js";
|
|
25
33
|
/**
|
|
26
34
|
* @since 1.0.0
|
|
27
35
|
* @category API
|
|
28
36
|
*/
|
|
29
|
-
export const details = /*#__PURE__*/
|
|
37
|
+
export const details = /*#__PURE__*/Effect.fnUntraced(function* (bundleIdentifier) {
|
|
30
38
|
const decoderDetailsResponse = decodeResponseFromResponseWrapper("detailsResponse");
|
|
39
|
+
const device = yield* AndroidDeviceService;
|
|
31
40
|
const httpRequest = HttpClientRequest.get("/details", {
|
|
32
41
|
urlParams: {
|
|
33
42
|
doc: bundleIdentifier
|
|
@@ -37,18 +46,19 @@ export const details = /*#__PURE__*/Function.dual(2, /*#__PURE__*/Effect.fnUntra
|
|
|
37
46
|
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
38
47
|
const pbResponse = yield* decoderDetailsResponse(httpResponse);
|
|
39
48
|
return pbResponse;
|
|
40
|
-
})
|
|
49
|
+
});
|
|
41
50
|
/**
|
|
42
51
|
* @since 1.0.0
|
|
43
52
|
* @category API
|
|
44
53
|
*/
|
|
45
|
-
export const bulkDetails = /*#__PURE__*/
|
|
54
|
+
export const bulkDetails = /*#__PURE__*/Effect.fnUntraced(function* (bundleIdentifier) {
|
|
46
55
|
const decoderBulkDetailsResponse = decodeResponseFromResponseWrapper("bulkDetailsResponse");
|
|
47
56
|
const encoderBulkDetailsRequest = encodeRequest(BulkDetailsRequestSchema, {
|
|
48
57
|
includeDetails: true,
|
|
49
58
|
includeChildDocs: true,
|
|
50
59
|
DocId: [bundleIdentifier]
|
|
51
60
|
});
|
|
61
|
+
const device = yield* AndroidDeviceService;
|
|
52
62
|
const httpRequest = HttpClientRequest.post("/bulkDetails", {
|
|
53
63
|
headers: yield* device.authHeaders
|
|
54
64
|
}).pipe(HttpClientRequest.prependUrl(fdfeBase));
|
|
@@ -56,13 +66,14 @@ export const bulkDetails = /*#__PURE__*/Function.dual(2, /*#__PURE__*/Effect.fnU
|
|
|
56
66
|
const httpResponse = yield* HttpClient.execute(pbRequest);
|
|
57
67
|
const pbResponse = yield* decoderBulkDetailsResponse(httpResponse);
|
|
58
68
|
return pbResponse;
|
|
59
|
-
})
|
|
69
|
+
});
|
|
60
70
|
/**
|
|
61
71
|
* @since 1.0.0
|
|
62
72
|
* @category API
|
|
63
73
|
*/
|
|
64
|
-
export const purchase = /*#__PURE__*/
|
|
74
|
+
export const purchase = /*#__PURE__*/Effect.fnUntraced(function* (bundleIdentifier, options) {
|
|
65
75
|
const decoderBuyResponse = decodeResponseFromResponseWrapper("buyResponse");
|
|
76
|
+
const device = yield* AndroidDeviceService;
|
|
66
77
|
const httpRequest = HttpClientRequest.post("/purchase", {
|
|
67
78
|
headers: yield* device.authHeaders,
|
|
68
79
|
urlParams: {
|
|
@@ -75,13 +86,14 @@ export const purchase = /*#__PURE__*/Function.dual(3, /*#__PURE__*/Effect.fnUntr
|
|
|
75
86
|
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
76
87
|
const pbResponse = yield* decoderBuyResponse(httpResponse);
|
|
77
88
|
return pbResponse;
|
|
78
|
-
})
|
|
89
|
+
});
|
|
79
90
|
/**
|
|
80
91
|
* @since 1.0.0
|
|
81
92
|
* @category API
|
|
82
93
|
*/
|
|
83
|
-
export const delivery = /*#__PURE__*/
|
|
94
|
+
export const delivery = /*#__PURE__*/Effect.fnUntraced(function* (bundleIdentifier, options) {
|
|
84
95
|
const decoderDeliveryResponse = decodeResponseFromResponseWrapper("deliveryResponse");
|
|
96
|
+
const device = yield* AndroidDeviceService;
|
|
85
97
|
const httpRequest = HttpClientRequest.get("/delivery", {
|
|
86
98
|
headers: yield* device.authHeaders,
|
|
87
99
|
urlParams: {
|
|
@@ -95,94 +107,122 @@ export const delivery = /*#__PURE__*/Function.dual(3, /*#__PURE__*/Effect.fnUntr
|
|
|
95
107
|
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
96
108
|
const pbResponse = yield* decoderDeliveryResponse(httpResponse);
|
|
97
109
|
return pbResponse;
|
|
98
|
-
})
|
|
110
|
+
});
|
|
99
111
|
/**
|
|
100
112
|
* @since 1.0.0
|
|
101
113
|
* @category API
|
|
102
114
|
*/
|
|
103
|
-
export const
|
|
104
|
-
const fileSystem = yield* FileSystem.FileSystem;
|
|
115
|
+
export const downloadToStreams = /*#__PURE__*/Effect.fnUntraced(function* (bundleIdentifier, options) {
|
|
105
116
|
const {
|
|
106
117
|
item
|
|
107
|
-
} = yield* details(
|
|
118
|
+
} = yield* details(bundleIdentifier);
|
|
119
|
+
const offerType = options?.offerType ?? item?.offer[0].offerType ?? 1;
|
|
120
|
+
const versionCode = options?.versionCode ?? item?.details?.appDetails?.versionCode ?? 0;
|
|
108
121
|
const {
|
|
109
122
|
encodedDeliveryToken
|
|
110
|
-
} = yield* purchase(
|
|
111
|
-
offerType:
|
|
112
|
-
versionCode:
|
|
123
|
+
} = yield* purchase(bundleIdentifier, {
|
|
124
|
+
offerType: offerType,
|
|
125
|
+
versionCode: versionCode
|
|
113
126
|
});
|
|
114
|
-
const deliveryResult = yield* delivery(
|
|
127
|
+
const deliveryResult = yield* delivery(bundleIdentifier, {
|
|
115
128
|
deliveryToken: encodedDeliveryToken,
|
|
116
|
-
offerType:
|
|
117
|
-
versionCode:
|
|
129
|
+
offerType: offerType,
|
|
130
|
+
versionCode: versionCode
|
|
118
131
|
});
|
|
119
132
|
const mainDeliveryData = deliveryResult?.appDeliveryData;
|
|
120
133
|
if (mainDeliveryData === undefined) {
|
|
121
|
-
return yield*
|
|
134
|
+
return yield* new Cause.NoSuchElementError();
|
|
122
135
|
}
|
|
123
|
-
const digest = algorithm => stream => stream.pipe(Stream.mkUint8Array, Effect.flatMap(buffer => Effect.promise(() => crypto.subtle.digest(algorithm, buffer))), Effect.map(digestBuffer => Array.fromIterable(new Uint8Array(digestBuffer)).map(b => b.toString(16).padStart(2, "0")).join("")));
|
|
124
136
|
const main = Effect.gen(function* () {
|
|
125
|
-
const file = yield* fileSystem.makeTempFileScoped({
|
|
126
|
-
suffix: ".apk"
|
|
127
|
-
});
|
|
128
137
|
const integrity = Buffer.from(mainDeliveryData.sha256, "base64url").toString("hex");
|
|
129
|
-
const
|
|
130
|
-
yield* Effect.logDebug(`main APK of ${mainDeliveryData.downloadSize}bytes from ${mainDeliveryData.downloadUrl} with integrity ${integrity}(sha256)
|
|
131
|
-
if (downloadedIntegrity !== integrity) {
|
|
132
|
-
return yield* Effect.die(`Downloaded main APK integrity mismatch: expected ${integrity}, got ${downloadedIntegrity}`);
|
|
133
|
-
}
|
|
138
|
+
const stream = yield* HttpClient.get(mainDeliveryData.downloadUrl).pipe(Effect.map(response => response.stream));
|
|
139
|
+
yield* Effect.logDebug(`fetched main APK of ${mainDeliveryData.downloadSize}bytes from ${mainDeliveryData.downloadUrl} with integrity ${integrity}(sha256)`);
|
|
134
140
|
return {
|
|
135
|
-
|
|
141
|
+
stream,
|
|
136
142
|
name: `${bundleIdentifier}.apk`,
|
|
137
143
|
url: mainDeliveryData.downloadUrl,
|
|
138
144
|
size: mainDeliveryData.downloadSize,
|
|
139
145
|
integrity: {
|
|
140
|
-
|
|
146
|
+
"SHA-256": integrity
|
|
141
147
|
}
|
|
142
148
|
};
|
|
143
149
|
});
|
|
144
150
|
const splits = Array.map(mainDeliveryData.splitDeliveryData, split => Effect.gen(function* () {
|
|
145
|
-
const file = yield* fileSystem.makeTempFileScoped({
|
|
146
|
-
suffix: ".apk"
|
|
147
|
-
});
|
|
148
151
|
const integrity = Buffer.from(split.sha256, "base64url").toString("hex");
|
|
149
|
-
const
|
|
150
|
-
yield* Effect.logDebug(`split ${split.name} of ${split.downloadSize}bytes from ${split.downloadUrl} with integrity ${integrity}(sha256)
|
|
151
|
-
if (downloadedIntegrity !== integrity) {
|
|
152
|
-
return yield* Effect.die(`Downloaded split ${split.name} integrity mismatch: expected ${integrity}, got ${downloadedIntegrity}`);
|
|
153
|
-
}
|
|
152
|
+
const stream = yield* HttpClient.get(split.downloadUrl).pipe(Effect.map(response => response.stream));
|
|
153
|
+
yield* Effect.logDebug(`fetched split ${split.name} of ${split.downloadSize}bytes from ${split.downloadUrl} with integrity ${integrity}(sha256)`);
|
|
154
154
|
return {
|
|
155
|
-
|
|
155
|
+
stream,
|
|
156
156
|
url: split.downloadUrl,
|
|
157
157
|
size: split.downloadSize,
|
|
158
158
|
name: `${split.name}.apk`,
|
|
159
159
|
integrity: {
|
|
160
|
-
|
|
160
|
+
"SHA-256": integrity
|
|
161
161
|
}
|
|
162
162
|
};
|
|
163
163
|
}));
|
|
164
164
|
const expansions = Array.map(mainDeliveryData.additionalFile, expansion => Effect.gen(function* () {
|
|
165
165
|
const typeStr = expansion.fileType === 1 ? "main" : "patch";
|
|
166
166
|
const name = `${typeStr}.${expansion.versionCode}.${bundleIdentifier}.obb`;
|
|
167
|
-
const file = yield* fileSystem.makeTempFileScoped({
|
|
168
|
-
suffix: ".obb"
|
|
169
|
-
});
|
|
170
167
|
const integrity = Buffer.from(expansion.sha1, "base64url").toString("hex");
|
|
171
|
-
const
|
|
172
|
-
yield* Effect.logDebug(`expansion file ${name} of ${expansion.size}bytes from ${expansion.downloadUrl} with integrity ${integrity}(sha1)
|
|
173
|
-
if (downloadedIntegrity !== integrity) {
|
|
174
|
-
return yield* Effect.die(`Downloaded expansion file ${name} integrity mismatch: expected ${integrity}, got ${downloadedIntegrity}`);
|
|
175
|
-
}
|
|
168
|
+
const stream = yield* HttpClient.get(expansion.downloadUrl).pipe(Effect.map(response => response.stream));
|
|
169
|
+
yield* Effect.logDebug(`fetched expansion file ${name} of ${expansion.size}bytes from ${expansion.downloadUrl} with integrity ${integrity}(sha1)`);
|
|
176
170
|
return {
|
|
177
|
-
file,
|
|
178
171
|
name,
|
|
172
|
+
stream,
|
|
179
173
|
size: expansion.size,
|
|
180
174
|
url: expansion.downloadUrl,
|
|
181
175
|
integrity: {
|
|
182
|
-
|
|
176
|
+
"SHA-1": integrity
|
|
183
177
|
}
|
|
184
178
|
};
|
|
185
179
|
}));
|
|
186
180
|
return yield* Effect.all([main, ...splits, ...expansions]);
|
|
187
181
|
});
|
|
182
|
+
/**
|
|
183
|
+
* @since 1.0.0
|
|
184
|
+
* @category API
|
|
185
|
+
*/
|
|
186
|
+
export const downloadToDisk = /*#__PURE__*/Effect.fnUntraced(function* (bundleIdentifier, options) {
|
|
187
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
188
|
+
const streams = yield* downloadToStreams(bundleIdentifier, options);
|
|
189
|
+
const digest = algorithm => stream => stream.pipe(Stream.mkUint8Array, Effect.flatMap(buffer => Crypto.Crypto.use(crypto => crypto.digest(algorithm, buffer))), Effect.map(digestBuffer => Array.fromIterable(digestBuffer).map(b => b.toString(16).padStart(2, "0")).join("")));
|
|
190
|
+
const write = Effect.fnUntraced(function* ({
|
|
191
|
+
stream,
|
|
192
|
+
name,
|
|
193
|
+
url,
|
|
194
|
+
size,
|
|
195
|
+
integrity
|
|
196
|
+
}) {
|
|
197
|
+
const file = yield* fileSystem.makeTempFileScoped();
|
|
198
|
+
const digestAlgorithm = Match.value(integrity).pipe(Match.when({
|
|
199
|
+
"SHA-1": Match.string
|
|
200
|
+
}, () => "SHA-1"), Match.when({
|
|
201
|
+
"SHA-256": Match.string
|
|
202
|
+
}, () => "SHA-256"), Match.when({
|
|
203
|
+
"SHA-384": Match.string
|
|
204
|
+
}, () => "SHA-384"), Match.when({
|
|
205
|
+
"SHA-512": Match.string
|
|
206
|
+
}, () => "SHA-512"), Match.exhaustive);
|
|
207
|
+
const expectedIntegrity = integrity[digestAlgorithm];
|
|
208
|
+
const downloadedIntegrity = yield* stream.pipe(Stream.tapSink(fileSystem.sink(file)), digest(digestAlgorithm));
|
|
209
|
+
if (downloadedIntegrity !== expectedIntegrity) {
|
|
210
|
+
return yield* new PlatformError.PlatformError(new PlatformError.SystemError({
|
|
211
|
+
_tag: "InvalidData",
|
|
212
|
+
module: "GooglePlayApi",
|
|
213
|
+
method: "downloadToDisk",
|
|
214
|
+
description: `Downloaded ${name} integrity mismatch: expected ${integrity}, got ${downloadedIntegrity}`
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
url,
|
|
219
|
+
name,
|
|
220
|
+
file,
|
|
221
|
+
size,
|
|
222
|
+
integrity
|
|
223
|
+
};
|
|
224
|
+
});
|
|
225
|
+
const effects = Array.map(streams, write);
|
|
226
|
+
return yield* Effect.all(effects);
|
|
227
|
+
});
|
|
188
228
|
//# sourceMappingURL=GooglePlayApi.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GooglePlayApi.js","names":["Array","Effect","FileSystem","
|
|
1
|
+
{"version":3,"file":"GooglePlayApi.js","names":["Array","Cause","Crypto","Effect","FileSystem","Match","PlatformError","Stream","HttpClient","HttpClientRequest","BulkDetailsRequestSchema","Service","AndroidDeviceService","decodeResponseFromResponseWrapper","encodeRequest","fdfeBase","AndroidDevice","details","fnUntraced","bundleIdentifier","decoderDetailsResponse","device","httpRequest","get","urlParams","doc","headers","authHeaders","pipe","prependUrl","httpResponse","execute","pbResponse","bulkDetails","decoderBulkDetailsResponse","encoderBulkDetailsRequest","includeDetails","includeChildDocs","DocId","post","pbRequest","purchase","options","decoderBuyResponse","ch","certificateHash","ot","offerType","toString","vc","versionCode","delivery","decoderDeliveryResponse","dtok","deliveryToken","downloadToStreams","item","offer","appDetails","encodedDeliveryToken","deliveryResult","mainDeliveryData","appDeliveryData","undefined","NoSuchElementError","main","gen","integrity","Buffer","from","sha256","stream","downloadUrl","map","response","logDebug","downloadSize","name","url","size","splits","splitDeliveryData","split","expansions","additionalFile","expansion","typeStr","fileType","sha1","all","downloadToDisk","fileSystem","streams","digest","algorithm","mkUint8Array","flatMap","buffer","use","crypto","digestBuffer","fromIterable","b","padStart","join","write","file","makeTempFileScoped","digestAlgorithm","value","when","string","exhaustive","expectedIntegrity","downloadedIntegrity","tapSink","sink","SystemError","_tag","module","method","description","effects"],"sources":["../src/GooglePlayApi.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import type * as PlatformError from "effect/PlatformError";
|
|
2
2
|
import type * as HttpClient from "effect/unstable/http/HttpClient";
|
|
3
3
|
import type * as HttpClientError from "effect/unstable/http/HttpClientError";
|
|
4
|
+
import * as Context from "effect/Context";
|
|
4
5
|
import * as Effect from "effect/Effect";
|
|
5
6
|
import * as FileSystem from "effect/FileSystem";
|
|
7
|
+
import * as Layer from "effect/Layer";
|
|
6
8
|
import * as Path from "effect/Path";
|
|
7
9
|
import * as Schema from "effect/Schema";
|
|
8
10
|
export declare const StringArrayFromString: Schema.suspend<Schema.decodeTo<Schema.$Array<Schema.String>, Schema.String, never, never>>;
|
|
9
|
-
export declare const BooleanFromString: Schema.
|
|
11
|
+
export declare const BooleanFromString: Schema.compose<Schema.Boolean, Schema.Union<readonly [Schema.decodeTo<Schema.Literal<true>, Schema.Literal<"true">, never, never>, Schema.decodeTo<Schema.Literal<false>, Schema.Literal<"false">, never, never>]>>;
|
|
12
|
+
declare const Service_base: Context.ServiceClass<Service, "@efffrida/gplayapi/device", AndroidDevice>;
|
|
13
|
+
export declare class Service extends Service_base {
|
|
14
|
+
}
|
|
10
15
|
declare const AndroidDevice_base: Schema.Class<AndroidDevice, Schema.Struct<{
|
|
11
16
|
readonly UserReadableName: Schema.String;
|
|
12
17
|
readonly "Build.BOOTLOADER": Schema.String;
|
|
@@ -27,8 +32,8 @@ declare const AndroidDevice_base: Schema.Class<AndroidDevice, Schema.Struct<{
|
|
|
27
32
|
readonly "GL.Extensions": Schema.suspend<Schema.decodeTo<Schema.$Array<Schema.String>, Schema.String, never, never>>;
|
|
28
33
|
readonly "GL.Version": Schema.NumberFromString;
|
|
29
34
|
readonly "GSF.version": Schema.NumberFromString;
|
|
30
|
-
readonly HasFiveWayNavigation: Schema.
|
|
31
|
-
readonly HasHardKeyboard: Schema.
|
|
35
|
+
readonly HasFiveWayNavigation: Schema.compose<Schema.Boolean, Schema.Union<readonly [Schema.decodeTo<Schema.Literal<true>, Schema.Literal<"true">, never, never>, Schema.decodeTo<Schema.Literal<false>, Schema.Literal<"false">, never, never>]>>;
|
|
36
|
+
readonly HasHardKeyboard: Schema.compose<Schema.Boolean, Schema.Union<readonly [Schema.decodeTo<Schema.Literal<true>, Schema.Literal<"true">, never, never>, Schema.decodeTo<Schema.Literal<false>, Schema.Literal<"false">, never, never>]>>;
|
|
32
37
|
readonly Keyboard: Schema.NumberFromString;
|
|
33
38
|
readonly Locales: Schema.suspend<Schema.decodeTo<Schema.$Array<Schema.String>, Schema.String, never, never>>;
|
|
34
39
|
readonly Navigation: Schema.NumberFromString;
|
|
@@ -50,6 +55,7 @@ export declare class AndroidDevice extends AndroidDevice_base {
|
|
|
50
55
|
private authHeadersCache?;
|
|
51
56
|
static fromPropertiesFile: (file: string) => Effect.Effect<AndroidDevice, Schema.SchemaError | PlatformError.PlatformError, FileSystem.FileSystem>;
|
|
52
57
|
static EmbeddedPixel7a: Effect.Effect<AndroidDevice, Schema.SchemaError | PlatformError.PlatformError | PlatformError.BadArgument, FileSystem.FileSystem | Path.Path>;
|
|
58
|
+
static EmbeddedPixel7aLive: Layer.Layer<Service, Schema.SchemaError | PlatformError.PlatformError | PlatformError.BadArgument, FileSystem.FileSystem | Path.Path>;
|
|
53
59
|
get userAgent(): string;
|
|
54
60
|
readonly authHeaders: Effect.Effect<Record<string, string>, HttpClientError.HttpClientError | Schema.SchemaError, HttpClient.HttpClient>;
|
|
55
61
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/internal/device.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,aAAa,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,KAAK,KAAK,eAAe,MAAM,sCAAsC,CAAC;AAE7E,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAMxC,eAAO,MAAM,qBAAqB,4FAKhC,CAAC;AAEH,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/internal/device.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,aAAa,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,KAAK,KAAK,eAAe,MAAM,sCAAsC,CAAC;AAE7E,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAMxC,eAAO,MAAM,qBAAqB,4FAKhC,CAAC;AAEH,eAAO,MAAM,iBAAiB,qNAEY,CAAC;;AAE3C,qBAAa,OAAQ,SAAQ,YAAsE;CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEtG,qBAAa,aAAc,SAAQ,kBAsCjC;IACE,OAAO,CAAC,gBAAgB,CAAC,CAAiD;IAE1E,OAAc,kBAAkB,0HAoB7B;IAEH,OAAc,eAAe,gJAG3B;IAEF,OAAc,mBAAmB,wIAAwD;IAEzF,IAAW,SAAS,IAAI,MAAM,CAoB7B;IAED,SAAgB,WAAW,EAAE,MAAM,CAAC,MAAM,CACtC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACtB,eAAe,CAAC,eAAe,GAAG,MAAM,CAAC,WAAW,EACpD,UAAU,CAAC,UAAU,CACxB,CAQE;CACN"}
|
package/dist/internal/device.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import * as Context from "effect/Context";
|
|
1
2
|
import * as Effect from "effect/Effect";
|
|
2
3
|
import * as FileSystem from "effect/FileSystem";
|
|
4
|
+
import * as Layer from "effect/Layer";
|
|
3
5
|
import * as Path from "effect/Path";
|
|
4
6
|
import * as Schema from "effect/Schema";
|
|
5
7
|
import * as SchemaGetter from "effect/SchemaGetter";
|
|
@@ -16,10 +18,8 @@ export const StringArrayFromString = /*#__PURE__*/Schema.suspend(() => {
|
|
|
16
18
|
});
|
|
17
19
|
return Schema.String.pipe(Schema.decodeTo(Schema.Array(Schema.String), transform));
|
|
18
20
|
});
|
|
19
|
-
export const BooleanFromString = /*#__PURE__*/Schema.Literals(["true", "false"]).pipe(/*#__PURE__*/Schema.decodeTo(Schema.Boolean
|
|
20
|
-
|
|
21
|
-
encode: bool => bool ? "true" : "false"
|
|
22
|
-
})));
|
|
21
|
+
export const BooleanFromString = /*#__PURE__*/Schema.Literals(["true", "false"]).transform([true, false]).pipe(/*#__PURE__*/Schema.decodeTo(Schema.Boolean));
|
|
22
|
+
export class Service extends /*#__PURE__*/Context.Service()("@efffrida/gplayapi/device") {}
|
|
23
23
|
export class AndroidDevice extends /*#__PURE__*/Schema.Class("AndroidDevice")({
|
|
24
24
|
UserReadableName: Schema.String,
|
|
25
25
|
"Build.BOOTLOADER": Schema.String,
|
|
@@ -72,6 +72,7 @@ export class AndroidDevice extends /*#__PURE__*/Schema.Class("AndroidDevice")({
|
|
|
72
72
|
return yield* decodeDevice(properties);
|
|
73
73
|
});
|
|
74
74
|
static EmbeddedPixel7a = /*#__PURE__*/Path.Path.pipe(/*#__PURE__*/Effect.flatMap(path => path.fromFileUrl(new URL("../../devices/arm64_xxhdpi.properties", import.meta.url))), /*#__PURE__*/Effect.flatMap(AndroidDevice.fromPropertiesFile));
|
|
75
|
+
static EmbeddedPixel7aLive = /*#__PURE__*/Layer.effect(Service, AndroidDevice.EmbeddedPixel7a);
|
|
75
76
|
get userAgent() {
|
|
76
77
|
const deviceProperties = {
|
|
77
78
|
api: 3,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device.js","names":["Effect","FileSystem","Path","Schema","SchemaGetter","SchemaTransformation","internalAuth","StringArrayFromString","suspend","splitter","split","separator","joiner","transform","arr","join","make","encode","decode","String","pipe","decodeTo","Array","BooleanFromString","Literals","Boolean","
|
|
1
|
+
{"version":3,"file":"device.js","names":["Context","Effect","FileSystem","Layer","Path","Schema","SchemaGetter","SchemaTransformation","internalAuth","StringArrayFromString","suspend","splitter","split","separator","joiner","transform","arr","join","make","encode","decode","String","pipe","decodeTo","Array","BooleanFromString","Literals","Boolean","Service","AndroidDevice","Class","UserReadableName","NumberFromString","CellOperator","Client","Features","HasFiveWayNavigation","HasHardKeyboard","Keyboard","Locales","Navigation","Platforms","Roaming","ScreenLayout","SharedLibraries","SimCountry","optional","SimOperator","TimeZone","TouchScreen","authHeadersCache","undefined","fromPropertiesFile","fnUntraced","file","decodeDevice","decodeUnknownEffect","decodePropertiesFile","decodeEffect","Record","splitKeyValue","keyValueSeparator","fileSystem","content","readFileString","properties","EmbeddedPixel7a","flatMap","path","fromFileUrl","URL","import","meta","url","EmbeddedPixel7aLive","effect","userAgent","deviceProperties","api","versionCode","sdk","device","hardware","product","platformVersionRelease","model","buildId","isWideScreen","supportedAbis","devicePropertiesString","Object","entries","map","k","v","authHeaders","gen","self"],"sources":["../../src/internal/device.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
package/package.json
CHANGED
package/src/GooglePlayApi.ts
CHANGED
|
@@ -5,21 +5,20 @@
|
|
|
5
5
|
* @since 1.0.0
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type * as PlatformError from "effect/PlatformError";
|
|
9
8
|
import type * as Schema from "effect/Schema";
|
|
10
9
|
import type * as Scope from "effect/Scope";
|
|
11
10
|
import type * as HttpClientError from "effect/unstable/http/HttpClientError";
|
|
12
11
|
|
|
13
12
|
import * as Array from "effect/Array";
|
|
13
|
+
import * as Cause from "effect/Cause";
|
|
14
|
+
import * as Crypto from "effect/Crypto";
|
|
14
15
|
import * as Effect from "effect/Effect";
|
|
15
16
|
import * as FileSystem from "effect/FileSystem";
|
|
16
|
-
import * as
|
|
17
|
+
import * as Match from "effect/Match";
|
|
18
|
+
import * as PlatformError from "effect/PlatformError";
|
|
17
19
|
import * as Stream from "effect/Stream";
|
|
18
20
|
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
19
21
|
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
20
|
-
import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
|
|
21
|
-
|
|
22
|
-
import type { AndroidDevice } from "./internal/device.ts";
|
|
23
22
|
|
|
24
23
|
import {
|
|
25
24
|
BulkDetailsRequestSchema,
|
|
@@ -28,6 +27,7 @@ import {
|
|
|
28
27
|
type DeliveryResponse,
|
|
29
28
|
type DetailsResponse,
|
|
30
29
|
} from "./generated/GooglePlay_pb.ts";
|
|
30
|
+
import { Service as AndroidDeviceService } from "./internal/device.ts";
|
|
31
31
|
import { decodeResponseFromResponseWrapper, encodeRequest } from "./internal/http.ts";
|
|
32
32
|
|
|
33
33
|
/** @internal */
|
|
@@ -39,276 +39,210 @@ export {
|
|
|
39
39
|
* @category Device
|
|
40
40
|
*/
|
|
41
41
|
AndroidDevice,
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @since 1.0.0
|
|
45
|
+
* @category Device
|
|
46
|
+
*/
|
|
47
|
+
Service as AndroidDeviceService,
|
|
42
48
|
} from "./internal/device.ts";
|
|
43
49
|
|
|
44
50
|
/**
|
|
45
51
|
* @since 1.0.0
|
|
46
52
|
* @category API
|
|
47
53
|
*/
|
|
48
|
-
export const details =
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
68
|
-
const pbResponse = yield* decoderDetailsResponse(httpResponse);
|
|
69
|
-
return pbResponse;
|
|
70
|
-
})
|
|
71
|
-
);
|
|
54
|
+
export const details = Effect.fnUntraced(function* (
|
|
55
|
+
bundleIdentifier: string
|
|
56
|
+
): Effect.fn.Return<
|
|
57
|
+
DetailsResponse,
|
|
58
|
+
HttpClientError.HttpClientError | Schema.SchemaError,
|
|
59
|
+
HttpClient.HttpClient | AndroidDeviceService
|
|
60
|
+
> {
|
|
61
|
+
const decoderDetailsResponse = decodeResponseFromResponseWrapper("detailsResponse");
|
|
62
|
+
const device = yield* AndroidDeviceService;
|
|
63
|
+
|
|
64
|
+
const httpRequest = HttpClientRequest.get("/details", {
|
|
65
|
+
urlParams: { doc: bundleIdentifier },
|
|
66
|
+
headers: yield* device.authHeaders,
|
|
67
|
+
}).pipe(HttpClientRequest.prependUrl(fdfeBase));
|
|
68
|
+
|
|
69
|
+
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
70
|
+
const pbResponse = yield* decoderDetailsResponse(httpResponse);
|
|
71
|
+
return pbResponse;
|
|
72
|
+
});
|
|
72
73
|
|
|
73
74
|
/**
|
|
74
75
|
* @since 1.0.0
|
|
75
76
|
* @category API
|
|
76
77
|
*/
|
|
77
|
-
export const bulkDetails =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const httpRequest = HttpClientRequest.post("/bulkDetails", {
|
|
102
|
-
headers: yield* device.authHeaders,
|
|
103
|
-
}).pipe(HttpClientRequest.prependUrl(fdfeBase));
|
|
104
|
-
|
|
105
|
-
const pbRequest = yield* encoderBulkDetailsRequest(httpRequest);
|
|
106
|
-
const httpResponse = yield* HttpClient.execute(pbRequest);
|
|
107
|
-
const pbResponse = yield* decoderBulkDetailsResponse(httpResponse);
|
|
108
|
-
return pbResponse;
|
|
109
|
-
})
|
|
110
|
-
);
|
|
78
|
+
export const bulkDetails = Effect.fnUntraced(function* (
|
|
79
|
+
bundleIdentifier: string
|
|
80
|
+
): Effect.fn.Return<
|
|
81
|
+
BulkDetailsResponse,
|
|
82
|
+
HttpClientError.HttpClientError | Schema.SchemaError,
|
|
83
|
+
HttpClient.HttpClient | AndroidDeviceService
|
|
84
|
+
> {
|
|
85
|
+
const decoderBulkDetailsResponse = decodeResponseFromResponseWrapper("bulkDetailsResponse");
|
|
86
|
+
const encoderBulkDetailsRequest = encodeRequest(BulkDetailsRequestSchema, {
|
|
87
|
+
includeDetails: true,
|
|
88
|
+
includeChildDocs: true,
|
|
89
|
+
DocId: [bundleIdentifier],
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const device = yield* AndroidDeviceService;
|
|
93
|
+
const httpRequest = HttpClientRequest.post("/bulkDetails", {
|
|
94
|
+
headers: yield* device.authHeaders,
|
|
95
|
+
}).pipe(HttpClientRequest.prependUrl(fdfeBase));
|
|
96
|
+
|
|
97
|
+
const pbRequest = yield* encoderBulkDetailsRequest(httpRequest);
|
|
98
|
+
const httpResponse = yield* HttpClient.execute(pbRequest);
|
|
99
|
+
const pbResponse = yield* decoderBulkDetailsResponse(httpResponse);
|
|
100
|
+
return pbResponse;
|
|
101
|
+
});
|
|
111
102
|
|
|
112
103
|
/**
|
|
113
104
|
* @since 1.0.0
|
|
114
105
|
* @category API
|
|
115
106
|
*/
|
|
116
|
-
export const purchase =
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
ot: options.offerType.toString(),
|
|
142
|
-
vc: options.versionCode.toString(),
|
|
143
|
-
},
|
|
144
|
-
}).pipe(HttpClientRequest.prependUrl(fdfeBase));
|
|
145
|
-
|
|
146
|
-
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
147
|
-
const pbResponse = yield* decoderBuyResponse(httpResponse);
|
|
148
|
-
return pbResponse;
|
|
149
|
-
})
|
|
150
|
-
);
|
|
107
|
+
export const purchase = Effect.fnUntraced(function* (
|
|
108
|
+
bundleIdentifier: string,
|
|
109
|
+
options: { offerType: number; versionCode: number | bigint; certificateHash?: string }
|
|
110
|
+
): Effect.fn.Return<
|
|
111
|
+
BuyResponse,
|
|
112
|
+
HttpClientError.HttpClientError | Schema.SchemaError,
|
|
113
|
+
HttpClient.HttpClient | AndroidDeviceService
|
|
114
|
+
> {
|
|
115
|
+
const decoderBuyResponse = decodeResponseFromResponseWrapper("buyResponse");
|
|
116
|
+
|
|
117
|
+
const device = yield* AndroidDeviceService;
|
|
118
|
+
const httpRequest = HttpClientRequest.post("/purchase", {
|
|
119
|
+
headers: yield* device.authHeaders,
|
|
120
|
+
urlParams: {
|
|
121
|
+
doc: bundleIdentifier,
|
|
122
|
+
ch: options.certificateHash ?? "",
|
|
123
|
+
ot: options.offerType.toString(),
|
|
124
|
+
vc: options.versionCode.toString(),
|
|
125
|
+
},
|
|
126
|
+
}).pipe(HttpClientRequest.prependUrl(fdfeBase));
|
|
127
|
+
|
|
128
|
+
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
129
|
+
const pbResponse = yield* decoderBuyResponse(httpResponse);
|
|
130
|
+
return pbResponse;
|
|
131
|
+
});
|
|
151
132
|
|
|
152
133
|
/**
|
|
153
134
|
* @since 1.0.0
|
|
154
135
|
* @category API
|
|
155
136
|
*/
|
|
156
|
-
export const delivery =
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
options: {
|
|
181
|
-
offerType: number;
|
|
182
|
-
deliveryToken: string;
|
|
183
|
-
versionCode: number | bigint;
|
|
184
|
-
certificateHash?: string | undefined;
|
|
137
|
+
export const delivery = Effect.fnUntraced(function* (
|
|
138
|
+
bundleIdentifier: string,
|
|
139
|
+
options: {
|
|
140
|
+
offerType: number;
|
|
141
|
+
deliveryToken: string;
|
|
142
|
+
versionCode: number | bigint;
|
|
143
|
+
certificateHash?: string | undefined;
|
|
144
|
+
}
|
|
145
|
+
): Effect.fn.Return<
|
|
146
|
+
DeliveryResponse,
|
|
147
|
+
HttpClientError.HttpClientError | Schema.SchemaError,
|
|
148
|
+
HttpClient.HttpClient | AndroidDeviceService
|
|
149
|
+
> {
|
|
150
|
+
const decoderDeliveryResponse = decodeResponseFromResponseWrapper("deliveryResponse");
|
|
151
|
+
|
|
152
|
+
const device = yield* AndroidDeviceService;
|
|
153
|
+
const httpRequest = HttpClientRequest.get("/delivery", {
|
|
154
|
+
headers: yield* device.authHeaders,
|
|
155
|
+
urlParams: {
|
|
156
|
+
doc: bundleIdentifier,
|
|
157
|
+
dtok: options.deliveryToken,
|
|
158
|
+
ch: options.certificateHash ?? "",
|
|
159
|
+
ot: options.offerType.toString(),
|
|
160
|
+
vc: options.versionCode.toString(),
|
|
185
161
|
},
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
doc: bundleIdentifier,
|
|
193
|
-
dtok: options.deliveryToken,
|
|
194
|
-
ch: options.certificateHash ?? "",
|
|
195
|
-
ot: options.offerType.toString(),
|
|
196
|
-
vc: options.versionCode.toString(),
|
|
197
|
-
},
|
|
198
|
-
}).pipe(HttpClientRequest.prependUrl(fdfeBase));
|
|
199
|
-
|
|
200
|
-
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
201
|
-
const pbResponse = yield* decoderDeliveryResponse(httpResponse);
|
|
202
|
-
return pbResponse;
|
|
203
|
-
})
|
|
204
|
-
);
|
|
162
|
+
}).pipe(HttpClientRequest.prependUrl(fdfeBase));
|
|
163
|
+
|
|
164
|
+
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
165
|
+
const pbResponse = yield* decoderDeliveryResponse(httpResponse);
|
|
166
|
+
return pbResponse;
|
|
167
|
+
});
|
|
205
168
|
|
|
206
169
|
/**
|
|
207
170
|
* @since 1.0.0
|
|
208
171
|
* @category API
|
|
209
172
|
*/
|
|
210
|
-
export const
|
|
211
|
-
|
|
212
|
-
|
|
173
|
+
export const downloadToStreams = Effect.fnUntraced(function* (
|
|
174
|
+
bundleIdentifier: string,
|
|
175
|
+
options?:
|
|
176
|
+
| {
|
|
177
|
+
offerType?: number | undefined;
|
|
178
|
+
versionCode?: number | bigint | undefined;
|
|
179
|
+
}
|
|
180
|
+
| undefined
|
|
213
181
|
): Effect.fn.Return<
|
|
214
182
|
Array.NonEmptyReadonlyArray<{
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
file: string;
|
|
183
|
+
stream: Stream.Stream<Uint8Array, HttpClientError.HttpClientError, never>;
|
|
184
|
+
integrity: { "SHA-1": string } | { "SHA-256": string } | { "SHA-384": string } | { "SHA-512": string };
|
|
218
185
|
size: bigint;
|
|
219
|
-
|
|
186
|
+
name: string;
|
|
187
|
+
url: string;
|
|
220
188
|
}>,
|
|
221
|
-
|
|
222
|
-
|
|
189
|
+
Cause.NoSuchElementError | HttpClientError.HttpClientError | Schema.SchemaError,
|
|
190
|
+
AndroidDeviceService | HttpClient.HttpClient
|
|
223
191
|
> {
|
|
224
|
-
const
|
|
192
|
+
const { item } = yield* details(bundleIdentifier);
|
|
193
|
+
const offerType = options?.offerType ?? item?.offer[0].offerType ?? 1;
|
|
194
|
+
const versionCode = options?.versionCode ?? item?.details?.appDetails?.versionCode ?? 0;
|
|
225
195
|
|
|
226
|
-
const {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
versionCode: item?.details?.appDetails?.versionCode ?? 0,
|
|
196
|
+
const { encodedDeliveryToken } = yield* purchase(bundleIdentifier, {
|
|
197
|
+
offerType: offerType,
|
|
198
|
+
versionCode: versionCode,
|
|
230
199
|
});
|
|
231
200
|
|
|
232
|
-
const deliveryResult = yield* delivery(
|
|
201
|
+
const deliveryResult = yield* delivery(bundleIdentifier, {
|
|
233
202
|
deliveryToken: encodedDeliveryToken,
|
|
234
|
-
offerType:
|
|
235
|
-
versionCode:
|
|
203
|
+
offerType: offerType,
|
|
204
|
+
versionCode: versionCode,
|
|
236
205
|
});
|
|
237
206
|
|
|
238
207
|
const mainDeliveryData = deliveryResult?.appDeliveryData;
|
|
239
208
|
if (mainDeliveryData === undefined) {
|
|
240
|
-
return yield*
|
|
209
|
+
return yield* new Cause.NoSuchElementError();
|
|
241
210
|
}
|
|
242
211
|
|
|
243
|
-
const digest =
|
|
244
|
-
(algorithm: "SHA-1" | "SHA-256" | "SHA-384" | "SHA-512") =>
|
|
245
|
-
<E, R>(stream: Stream.Stream<Uint8Array, E, R>): Effect.Effect<string, E, R> =>
|
|
246
|
-
stream.pipe(
|
|
247
|
-
Stream.mkUint8Array,
|
|
248
|
-
Effect.flatMap((buffer) =>
|
|
249
|
-
Effect.promise(() => crypto.subtle.digest(algorithm, buffer as BufferSource))
|
|
250
|
-
),
|
|
251
|
-
Effect.map((digestBuffer) =>
|
|
252
|
-
Array.fromIterable(new Uint8Array(digestBuffer))
|
|
253
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
254
|
-
.join("")
|
|
255
|
-
)
|
|
256
|
-
);
|
|
257
|
-
|
|
258
212
|
const main = Effect.gen(function* () {
|
|
259
|
-
const file = yield* fileSystem.makeTempFileScoped({ suffix: ".apk" });
|
|
260
213
|
const integrity = Buffer.from(mainDeliveryData.sha256, "base64url").toString("hex");
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
Stream.tapSink(fileSystem.sink(file)),
|
|
264
|
-
digest("SHA-256")
|
|
214
|
+
const stream = yield* HttpClient.get(mainDeliveryData.downloadUrl).pipe(
|
|
215
|
+
Effect.map((response) => response.stream)
|
|
265
216
|
);
|
|
266
217
|
|
|
267
218
|
yield* Effect.logDebug(
|
|
268
|
-
`main APK of ${mainDeliveryData.downloadSize}bytes from ${mainDeliveryData.downloadUrl} with integrity ${integrity}(sha256)
|
|
219
|
+
`fetched main APK of ${mainDeliveryData.downloadSize}bytes from ${mainDeliveryData.downloadUrl} with integrity ${integrity}(sha256)`
|
|
269
220
|
);
|
|
270
221
|
|
|
271
|
-
if (downloadedIntegrity !== integrity) {
|
|
272
|
-
return yield* Effect.die(
|
|
273
|
-
`Downloaded main APK integrity mismatch: expected ${integrity}, got ${downloadedIntegrity}`
|
|
274
|
-
);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
222
|
return {
|
|
278
|
-
|
|
223
|
+
stream,
|
|
279
224
|
name: `${bundleIdentifier}.apk`,
|
|
280
225
|
url: mainDeliveryData.downloadUrl,
|
|
281
226
|
size: mainDeliveryData.downloadSize,
|
|
282
|
-
integrity: {
|
|
227
|
+
integrity: { "SHA-256": integrity },
|
|
283
228
|
};
|
|
284
229
|
});
|
|
285
230
|
|
|
286
231
|
const splits = Array.map(mainDeliveryData.splitDeliveryData, (split) =>
|
|
287
232
|
Effect.gen(function* () {
|
|
288
|
-
const file = yield* fileSystem.makeTempFileScoped({ suffix: ".apk" });
|
|
289
233
|
const integrity = Buffer.from(split.sha256, "base64url").toString("hex");
|
|
290
|
-
const
|
|
291
|
-
HttpClientResponse.stream,
|
|
292
|
-
Stream.tapSink(fileSystem.sink(file)),
|
|
293
|
-
digest("SHA-256")
|
|
294
|
-
);
|
|
234
|
+
const stream = yield* HttpClient.get(split.downloadUrl).pipe(Effect.map((response) => response.stream));
|
|
295
235
|
|
|
296
236
|
yield* Effect.logDebug(
|
|
297
|
-
`split ${split.name} of ${split.downloadSize}bytes from ${split.downloadUrl} with integrity ${integrity}(sha256)
|
|
237
|
+
`fetched split ${split.name} of ${split.downloadSize}bytes from ${split.downloadUrl} with integrity ${integrity}(sha256)`
|
|
298
238
|
);
|
|
299
239
|
|
|
300
|
-
if (downloadedIntegrity !== integrity) {
|
|
301
|
-
return yield* Effect.die(
|
|
302
|
-
`Downloaded split ${split.name} integrity mismatch: expected ${integrity}, got ${downloadedIntegrity}`
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
240
|
return {
|
|
307
|
-
|
|
241
|
+
stream,
|
|
308
242
|
url: split.downloadUrl,
|
|
309
243
|
size: split.downloadSize,
|
|
310
244
|
name: `${split.name}.apk`,
|
|
311
|
-
integrity: {
|
|
245
|
+
integrity: { "SHA-256": integrity },
|
|
312
246
|
};
|
|
313
247
|
})
|
|
314
248
|
);
|
|
@@ -317,33 +251,111 @@ export const download = Effect.fnUntraced(function* (
|
|
|
317
251
|
Effect.gen(function* () {
|
|
318
252
|
const typeStr = expansion.fileType === 1 ? "main" : "patch";
|
|
319
253
|
const name = `${typeStr}.${expansion.versionCode}.${bundleIdentifier}.obb`;
|
|
320
|
-
const file = yield* fileSystem.makeTempFileScoped({ suffix: ".obb" });
|
|
321
254
|
const integrity = Buffer.from(expansion.sha1, "base64url").toString("hex");
|
|
322
|
-
const
|
|
323
|
-
HttpClientResponse.stream,
|
|
324
|
-
Stream.tapSink(fileSystem.sink(file)),
|
|
325
|
-
digest("SHA-1")
|
|
326
|
-
);
|
|
255
|
+
const stream = yield* HttpClient.get(expansion.downloadUrl).pipe(Effect.map((response) => response.stream));
|
|
327
256
|
|
|
328
257
|
yield* Effect.logDebug(
|
|
329
|
-
`expansion file ${name} of ${expansion.size}bytes from ${expansion.downloadUrl} with integrity ${integrity}(sha1)
|
|
258
|
+
`fetched expansion file ${name} of ${expansion.size}bytes from ${expansion.downloadUrl} with integrity ${integrity}(sha1)`
|
|
330
259
|
);
|
|
331
260
|
|
|
332
|
-
if (downloadedIntegrity !== integrity) {
|
|
333
|
-
return yield* Effect.die(
|
|
334
|
-
`Downloaded expansion file ${name} integrity mismatch: expected ${integrity}, got ${downloadedIntegrity}`
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
261
|
return {
|
|
339
|
-
file,
|
|
340
262
|
name,
|
|
263
|
+
stream,
|
|
341
264
|
size: expansion.size,
|
|
342
265
|
url: expansion.downloadUrl,
|
|
343
|
-
integrity: {
|
|
266
|
+
integrity: { "SHA-1": integrity },
|
|
344
267
|
};
|
|
345
268
|
})
|
|
346
269
|
);
|
|
347
270
|
|
|
348
271
|
return yield* Effect.all([main, ...splits, ...expansions]);
|
|
349
272
|
});
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* @since 1.0.0
|
|
276
|
+
* @category API
|
|
277
|
+
*/
|
|
278
|
+
export const downloadToDisk = Effect.fnUntraced(function* (
|
|
279
|
+
bundleIdentifier: string,
|
|
280
|
+
options?:
|
|
281
|
+
| {
|
|
282
|
+
offerType?: number | undefined;
|
|
283
|
+
versionCode?: number | bigint | undefined;
|
|
284
|
+
}
|
|
285
|
+
| undefined
|
|
286
|
+
): Effect.fn.Return<
|
|
287
|
+
Array.NonEmptyReadonlyArray<{
|
|
288
|
+
url: string;
|
|
289
|
+
name: string;
|
|
290
|
+
file: string;
|
|
291
|
+
size: bigint;
|
|
292
|
+
integrity: { "SHA-1": string } | { "SHA-256": string } | { "SHA-384": string } | { "SHA-512": string };
|
|
293
|
+
}>,
|
|
294
|
+
Cause.NoSuchElementError | PlatformError.PlatformError | HttpClientError.HttpClientError | Schema.SchemaError,
|
|
295
|
+
AndroidDeviceService | Crypto.Crypto | HttpClient.HttpClient | FileSystem.FileSystem | Scope.Scope
|
|
296
|
+
> {
|
|
297
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
298
|
+
const streams = yield* downloadToStreams(bundleIdentifier, options);
|
|
299
|
+
|
|
300
|
+
const digest =
|
|
301
|
+
(algorithm: Crypto.DigestAlgorithm) =>
|
|
302
|
+
<E, R>(
|
|
303
|
+
stream: Stream.Stream<Uint8Array, E, R>
|
|
304
|
+
): Effect.Effect<string, E | PlatformError.PlatformError, R | Crypto.Crypto> =>
|
|
305
|
+
stream.pipe(
|
|
306
|
+
Stream.mkUint8Array,
|
|
307
|
+
Effect.flatMap((buffer) => Crypto.Crypto.use((crypto) => crypto.digest(algorithm, buffer))),
|
|
308
|
+
Effect.map((digestBuffer) =>
|
|
309
|
+
Array.fromIterable(digestBuffer)
|
|
310
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
311
|
+
.join("")
|
|
312
|
+
)
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
const write: (stream: (typeof streams)[number]) => Effect.Effect<
|
|
316
|
+
{
|
|
317
|
+
url: string;
|
|
318
|
+
name: string;
|
|
319
|
+
file: string;
|
|
320
|
+
size: bigint;
|
|
321
|
+
integrity: { "SHA-1": string } | { "SHA-256": string } | { "SHA-384": string } | { "SHA-512": string };
|
|
322
|
+
},
|
|
323
|
+
PlatformError.PlatformError | HttpClientError.HttpClientError,
|
|
324
|
+
Crypto.Crypto | Scope.Scope
|
|
325
|
+
> = Effect.fnUntraced(function* ({ stream, name, url, size, integrity }) {
|
|
326
|
+
const file = yield* fileSystem.makeTempFileScoped();
|
|
327
|
+
|
|
328
|
+
const digestAlgorithm = Match.value(integrity).pipe(
|
|
329
|
+
Match.when({ "SHA-1": Match.string }, () => "SHA-1" as const),
|
|
330
|
+
Match.when({ "SHA-256": Match.string }, () => "SHA-256" as const),
|
|
331
|
+
Match.when({ "SHA-384": Match.string }, () => "SHA-384" as const),
|
|
332
|
+
Match.when({ "SHA-512": Match.string }, () => "SHA-512" as const),
|
|
333
|
+
Match.exhaustive
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
const expectedIntegrity = (integrity as Record<Crypto.DigestAlgorithm, string>)[digestAlgorithm];
|
|
337
|
+
const downloadedIntegrity = yield* stream.pipe(Stream.tapSink(fileSystem.sink(file)), digest(digestAlgorithm));
|
|
338
|
+
|
|
339
|
+
if (downloadedIntegrity !== expectedIntegrity) {
|
|
340
|
+
return yield* new PlatformError.PlatformError(
|
|
341
|
+
new PlatformError.SystemError({
|
|
342
|
+
_tag: "InvalidData",
|
|
343
|
+
module: "GooglePlayApi",
|
|
344
|
+
method: "downloadToDisk",
|
|
345
|
+
description: `Downloaded ${name} integrity mismatch: expected ${integrity}, got ${downloadedIntegrity}`,
|
|
346
|
+
})
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
url,
|
|
352
|
+
name,
|
|
353
|
+
file,
|
|
354
|
+
size,
|
|
355
|
+
integrity,
|
|
356
|
+
};
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
const effects = Array.map(streams, write);
|
|
360
|
+
return yield* Effect.all(effects);
|
|
361
|
+
});
|
package/src/internal/device.ts
CHANGED
|
@@ -2,8 +2,10 @@ import type * as PlatformError from "effect/PlatformError";
|
|
|
2
2
|
import type * as HttpClient from "effect/unstable/http/HttpClient";
|
|
3
3
|
import type * as HttpClientError from "effect/unstable/http/HttpClientError";
|
|
4
4
|
|
|
5
|
+
import * as Context from "effect/Context";
|
|
5
6
|
import * as Effect from "effect/Effect";
|
|
6
7
|
import * as FileSystem from "effect/FileSystem";
|
|
8
|
+
import * as Layer from "effect/Layer";
|
|
7
9
|
import * as Path from "effect/Path";
|
|
8
10
|
import * as Schema from "effect/Schema";
|
|
9
11
|
import * as SchemaGetter from "effect/SchemaGetter";
|
|
@@ -18,15 +20,11 @@ export const StringArrayFromString = Schema.suspend(() => {
|
|
|
18
20
|
return Schema.String.pipe(Schema.decodeTo(Schema.Array(Schema.String), transform));
|
|
19
21
|
});
|
|
20
22
|
|
|
21
|
-
export const BooleanFromString = Schema.Literals(["true", "false"])
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
encode: (bool) => (bool ? "true" : "false"),
|
|
27
|
-
})
|
|
28
|
-
)
|
|
29
|
-
);
|
|
23
|
+
export const BooleanFromString = Schema.Literals(["true", "false"])
|
|
24
|
+
.transform([true, false])
|
|
25
|
+
.pipe(Schema.decodeTo(Schema.Boolean));
|
|
26
|
+
|
|
27
|
+
export class Service extends Context.Service<Service, AndroidDevice>()("@efffrida/gplayapi/device") {}
|
|
30
28
|
|
|
31
29
|
export class AndroidDevice extends Schema.Class<AndroidDevice>("AndroidDevice")({
|
|
32
30
|
UserReadableName: Schema.String,
|
|
@@ -96,6 +94,8 @@ export class AndroidDevice extends Schema.Class<AndroidDevice>("AndroidDevice")(
|
|
|
96
94
|
Effect.flatMap(AndroidDevice.fromPropertiesFile)
|
|
97
95
|
);
|
|
98
96
|
|
|
97
|
+
public static EmbeddedPixel7aLive = Layer.effect(Service, AndroidDevice.EmbeddedPixel7a);
|
|
98
|
+
|
|
99
99
|
public get userAgent(): string {
|
|
100
100
|
const deviceProperties = {
|
|
101
101
|
api: 3,
|