@efffrida/gplayapi 0.0.14 → 0.0.16
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 +68 -23
- package/dist/GooglePlayApi.d.ts.map +1 -1
- package/dist/GooglePlayApi.js +84 -51
- package/dist/GooglePlayApi.js.map +1 -1
- package/dist/internal/device.d.ts +61 -0
- package/dist/internal/device.d.ts.map +1 -1
- package/dist/internal/device.js +5 -7
- package/dist/internal/device.js.map +1 -1
- package/package.json +1 -1
- package/src/GooglePlayApi.ts +227 -221
- package/src/internal/device.ts +9 -12
package/dist/GooglePlayApi.d.ts
CHANGED
|
@@ -8,68 +8,109 @@ import type * as PlatformError from "effect/PlatformError";
|
|
|
8
8
|
import type * as Schema from "effect/Schema";
|
|
9
9
|
import type * as Scope from "effect/Scope";
|
|
10
10
|
import type * as HttpClientError from "effect/unstable/http/HttpClientError";
|
|
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 Stream from "effect/Stream";
|
|
13
15
|
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
14
|
-
import type { AndroidDevice } from "./internal/device.ts";
|
|
15
16
|
import { type BulkDetailsResponse, type BuyResponse, type DeliveryResponse, type DetailsResponse } from "./generated/GooglePlay_pb.ts";
|
|
17
|
+
import { Service as AndroidDeviceService } from "./internal/device.ts";
|
|
16
18
|
export {
|
|
17
19
|
/**
|
|
18
20
|
* @since 1.0.0
|
|
19
21
|
* @category Device
|
|
20
22
|
*/
|
|
21
|
-
AndroidDevice,
|
|
23
|
+
AndroidDevice,
|
|
24
|
+
/**
|
|
25
|
+
* @since 1.0.0
|
|
26
|
+
* @category Device
|
|
27
|
+
*/
|
|
28
|
+
Service as AndroidDeviceService, } from "./internal/device.ts";
|
|
22
29
|
/**
|
|
23
30
|
* @since 1.0.0
|
|
24
31
|
* @category API
|
|
25
32
|
*/
|
|
26
|
-
export declare const details: (
|
|
33
|
+
export declare const details: (bundleIdentifier: string) => Effect.Effect<DetailsResponse, HttpClientError.HttpClientError | Schema.SchemaError, HttpClient.HttpClient | AndroidDeviceService>;
|
|
27
34
|
/**
|
|
28
35
|
* @since 1.0.0
|
|
29
36
|
* @category API
|
|
30
37
|
*/
|
|
31
|
-
export declare const bulkDetails: (
|
|
38
|
+
export declare const bulkDetails: (bundleIdentifier: string) => Effect.Effect<BulkDetailsResponse, HttpClientError.HttpClientError | Schema.SchemaError, HttpClient.HttpClient | AndroidDeviceService>;
|
|
32
39
|
/**
|
|
33
40
|
* @since 1.0.0
|
|
34
41
|
* @category API
|
|
35
42
|
*/
|
|
36
|
-
export declare const purchase: (
|
|
43
|
+
export declare const purchase: (bundleIdentifier: string, options: {
|
|
37
44
|
offerType: number;
|
|
38
45
|
versionCode: number | bigint;
|
|
39
46
|
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>);
|
|
47
|
+
}) => Effect.Effect<BuyResponse, HttpClientError.HttpClientError | Schema.SchemaError, HttpClient.HttpClient | AndroidDeviceService>;
|
|
45
48
|
/**
|
|
46
49
|
* @since 1.0.0
|
|
47
50
|
* @category API
|
|
48
51
|
*/
|
|
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: {
|
|
52
|
+
export declare const delivery: (bundleIdentifier: string, options: {
|
|
55
53
|
offerType: number;
|
|
56
54
|
deliveryToken: string;
|
|
57
55
|
versionCode: number | bigint;
|
|
58
56
|
certificateHash?: string | undefined;
|
|
59
|
-
}
|
|
57
|
+
}) => Effect.Effect<DeliveryResponse, HttpClientError.HttpClientError | Schema.SchemaError, HttpClient.HttpClient | AndroidDeviceService>;
|
|
58
|
+
/**
|
|
59
|
+
* @since 1.0.0
|
|
60
|
+
* @category API
|
|
61
|
+
*/
|
|
62
|
+
export declare const downloadToStreams: (bundleIdentifier: string, options?: {
|
|
63
|
+
offerType?: number | undefined;
|
|
64
|
+
versionCode?: number | bigint | undefined;
|
|
65
|
+
} | undefined) => Effect.Effect<readonly [{
|
|
66
|
+
stream: Stream.Stream<Uint8Array, HttpClientError.HttpClientError, never>;
|
|
67
|
+
integrity: {
|
|
68
|
+
"SHA-1": string;
|
|
69
|
+
} | {
|
|
70
|
+
"SHA-256": string;
|
|
71
|
+
} | {
|
|
72
|
+
"SHA-384": string;
|
|
73
|
+
} | {
|
|
74
|
+
"SHA-512": string;
|
|
75
|
+
};
|
|
76
|
+
size: bigint;
|
|
77
|
+
name: string;
|
|
78
|
+
url: string;
|
|
79
|
+
}, ...{
|
|
80
|
+
stream: Stream.Stream<Uint8Array, HttpClientError.HttpClientError, never>;
|
|
81
|
+
integrity: {
|
|
82
|
+
"SHA-1": string;
|
|
83
|
+
} | {
|
|
84
|
+
"SHA-256": string;
|
|
85
|
+
} | {
|
|
86
|
+
"SHA-384": string;
|
|
87
|
+
} | {
|
|
88
|
+
"SHA-512": string;
|
|
89
|
+
};
|
|
90
|
+
size: bigint;
|
|
91
|
+
name: string;
|
|
92
|
+
url: string;
|
|
93
|
+
}[]], HttpClientError.HttpClientError | Schema.SchemaError, HttpClient.HttpClient | AndroidDeviceService>;
|
|
60
94
|
/**
|
|
61
95
|
* @since 1.0.0
|
|
62
96
|
* @category API
|
|
63
97
|
*/
|
|
64
|
-
export declare const
|
|
98
|
+
export declare const downloadToDisk: (bundleIdentifier: string, options?: {
|
|
99
|
+
offerType?: number | undefined;
|
|
100
|
+
versionCode?: number | bigint | undefined;
|
|
101
|
+
} | undefined) => Effect.Effect<readonly [{
|
|
65
102
|
url: string;
|
|
66
103
|
name: string;
|
|
67
104
|
file: string;
|
|
68
105
|
size: bigint;
|
|
69
106
|
integrity: {
|
|
70
|
-
|
|
107
|
+
"SHA-1": string;
|
|
71
108
|
} | {
|
|
72
|
-
|
|
109
|
+
"SHA-256": string;
|
|
110
|
+
} | {
|
|
111
|
+
"SHA-384": string;
|
|
112
|
+
} | {
|
|
113
|
+
"SHA-512": string;
|
|
73
114
|
};
|
|
74
115
|
}, ...{
|
|
75
116
|
url: string;
|
|
@@ -77,9 +118,13 @@ export declare const download: (device: AndroidDevice, bundleIdentifier: string)
|
|
|
77
118
|
file: string;
|
|
78
119
|
size: bigint;
|
|
79
120
|
integrity: {
|
|
80
|
-
|
|
121
|
+
"SHA-1": string;
|
|
122
|
+
} | {
|
|
123
|
+
"SHA-256": string;
|
|
124
|
+
} | {
|
|
125
|
+
"SHA-384": string;
|
|
81
126
|
} | {
|
|
82
|
-
|
|
127
|
+
"SHA-512": string;
|
|
83
128
|
};
|
|
84
|
-
}[]], HttpClientError.HttpClientError | Schema.SchemaError | PlatformError.PlatformError, HttpClient.HttpClient | FileSystem.FileSystem | Scope.Scope>;
|
|
129
|
+
}[]], HttpClientError.HttpClientError | Schema.SchemaError | PlatformError.PlatformError, HttpClient.HttpClient | AndroidDeviceService | FileSystem.FileSystem | Scope.Scope | Crypto.Crypto>;
|
|
85
130
|
//# 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,aAAa,MAAM,sBAAsB,CAAC;AAC3D,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,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"GooglePlayApi.d.ts","sourceRoot":"","sources":["../src/GooglePlayApi.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,KAAK,aAAa,MAAM,sBAAsB,CAAC;AAC3D,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,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,mBAAmB,CAAC;AAEhD,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;yGAqFjB,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;6LAgE5G,CAAC"}
|
package/dist/GooglePlayApi.js
CHANGED
|
@@ -5,14 +5,15 @@
|
|
|
5
5
|
* @since 1.0.0
|
|
6
6
|
*/
|
|
7
7
|
import * as Array from "effect/Array";
|
|
8
|
+
import * as Crypto from "effect/Crypto";
|
|
8
9
|
import * as Effect from "effect/Effect";
|
|
9
10
|
import * as FileSystem from "effect/FileSystem";
|
|
10
|
-
import * as
|
|
11
|
+
import * as Match from "effect/Match";
|
|
11
12
|
import * as Stream from "effect/Stream";
|
|
12
13
|
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
13
14
|
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
14
|
-
import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
|
|
15
15
|
import { BulkDetailsRequestSchema } from "./generated/GooglePlay_pb.js";
|
|
16
|
+
import { Service as AndroidDeviceService } from "./internal/device.js";
|
|
16
17
|
import { decodeResponseFromResponseWrapper, encodeRequest } from "./internal/http.js";
|
|
17
18
|
/** @internal */
|
|
18
19
|
const fdfeBase = "https://android.clients.google.com/fdfe";
|
|
@@ -21,13 +22,19 @@ export {
|
|
|
21
22
|
* @since 1.0.0
|
|
22
23
|
* @category Device
|
|
23
24
|
*/
|
|
24
|
-
AndroidDevice
|
|
25
|
+
AndroidDevice,
|
|
26
|
+
/**
|
|
27
|
+
* @since 1.0.0
|
|
28
|
+
* @category Device
|
|
29
|
+
*/
|
|
30
|
+
Service as AndroidDeviceService } from "./internal/device.js";
|
|
25
31
|
/**
|
|
26
32
|
* @since 1.0.0
|
|
27
33
|
* @category API
|
|
28
34
|
*/
|
|
29
|
-
export const details = /*#__PURE__*/
|
|
35
|
+
export const details = /*#__PURE__*/Effect.fnUntraced(function* (bundleIdentifier) {
|
|
30
36
|
const decoderDetailsResponse = decodeResponseFromResponseWrapper("detailsResponse");
|
|
37
|
+
const device = yield* AndroidDeviceService;
|
|
31
38
|
const httpRequest = HttpClientRequest.get("/details", {
|
|
32
39
|
urlParams: {
|
|
33
40
|
doc: bundleIdentifier
|
|
@@ -37,18 +44,19 @@ export const details = /*#__PURE__*/Function.dual(2, /*#__PURE__*/Effect.fnUntra
|
|
|
37
44
|
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
38
45
|
const pbResponse = yield* decoderDetailsResponse(httpResponse);
|
|
39
46
|
return pbResponse;
|
|
40
|
-
})
|
|
47
|
+
});
|
|
41
48
|
/**
|
|
42
49
|
* @since 1.0.0
|
|
43
50
|
* @category API
|
|
44
51
|
*/
|
|
45
|
-
export const bulkDetails = /*#__PURE__*/
|
|
52
|
+
export const bulkDetails = /*#__PURE__*/Effect.fnUntraced(function* (bundleIdentifier) {
|
|
46
53
|
const decoderBulkDetailsResponse = decodeResponseFromResponseWrapper("bulkDetailsResponse");
|
|
47
54
|
const encoderBulkDetailsRequest = encodeRequest(BulkDetailsRequestSchema, {
|
|
48
55
|
includeDetails: true,
|
|
49
56
|
includeChildDocs: true,
|
|
50
57
|
DocId: [bundleIdentifier]
|
|
51
58
|
});
|
|
59
|
+
const device = yield* AndroidDeviceService;
|
|
52
60
|
const httpRequest = HttpClientRequest.post("/bulkDetails", {
|
|
53
61
|
headers: yield* device.authHeaders
|
|
54
62
|
}).pipe(HttpClientRequest.prependUrl(fdfeBase));
|
|
@@ -56,13 +64,14 @@ export const bulkDetails = /*#__PURE__*/Function.dual(2, /*#__PURE__*/Effect.fnU
|
|
|
56
64
|
const httpResponse = yield* HttpClient.execute(pbRequest);
|
|
57
65
|
const pbResponse = yield* decoderBulkDetailsResponse(httpResponse);
|
|
58
66
|
return pbResponse;
|
|
59
|
-
})
|
|
67
|
+
});
|
|
60
68
|
/**
|
|
61
69
|
* @since 1.0.0
|
|
62
70
|
* @category API
|
|
63
71
|
*/
|
|
64
|
-
export const purchase = /*#__PURE__*/
|
|
72
|
+
export const purchase = /*#__PURE__*/Effect.fnUntraced(function* (bundleIdentifier, options) {
|
|
65
73
|
const decoderBuyResponse = decodeResponseFromResponseWrapper("buyResponse");
|
|
74
|
+
const device = yield* AndroidDeviceService;
|
|
66
75
|
const httpRequest = HttpClientRequest.post("/purchase", {
|
|
67
76
|
headers: yield* device.authHeaders,
|
|
68
77
|
urlParams: {
|
|
@@ -75,13 +84,14 @@ export const purchase = /*#__PURE__*/Function.dual(3, /*#__PURE__*/Effect.fnUntr
|
|
|
75
84
|
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
76
85
|
const pbResponse = yield* decoderBuyResponse(httpResponse);
|
|
77
86
|
return pbResponse;
|
|
78
|
-
})
|
|
87
|
+
});
|
|
79
88
|
/**
|
|
80
89
|
* @since 1.0.0
|
|
81
90
|
* @category API
|
|
82
91
|
*/
|
|
83
|
-
export const delivery = /*#__PURE__*/
|
|
92
|
+
export const delivery = /*#__PURE__*/Effect.fnUntraced(function* (bundleIdentifier, options) {
|
|
84
93
|
const decoderDeliveryResponse = decodeResponseFromResponseWrapper("deliveryResponse");
|
|
94
|
+
const device = yield* AndroidDeviceService;
|
|
85
95
|
const httpRequest = HttpClientRequest.get("/delivery", {
|
|
86
96
|
headers: yield* device.authHeaders,
|
|
87
97
|
urlParams: {
|
|
@@ -95,94 +105,117 @@ export const delivery = /*#__PURE__*/Function.dual(3, /*#__PURE__*/Effect.fnUntr
|
|
|
95
105
|
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
96
106
|
const pbResponse = yield* decoderDeliveryResponse(httpResponse);
|
|
97
107
|
return pbResponse;
|
|
98
|
-
})
|
|
108
|
+
});
|
|
99
109
|
/**
|
|
100
110
|
* @since 1.0.0
|
|
101
111
|
* @category API
|
|
102
112
|
*/
|
|
103
|
-
export const
|
|
104
|
-
const fileSystem = yield* FileSystem.FileSystem;
|
|
113
|
+
export const downloadToStreams = /*#__PURE__*/Effect.fnUntraced(function* (bundleIdentifier, options) {
|
|
105
114
|
const {
|
|
106
115
|
item
|
|
107
|
-
} = yield* details(
|
|
116
|
+
} = yield* details(bundleIdentifier);
|
|
117
|
+
const offerType = options?.offerType ?? item?.offer[0].offerType ?? 1;
|
|
118
|
+
const versionCode = options?.versionCode ?? item?.details?.appDetails?.versionCode ?? 0;
|
|
108
119
|
const {
|
|
109
120
|
encodedDeliveryToken
|
|
110
|
-
} = yield* purchase(
|
|
111
|
-
offerType:
|
|
112
|
-
versionCode:
|
|
121
|
+
} = yield* purchase(bundleIdentifier, {
|
|
122
|
+
offerType: offerType,
|
|
123
|
+
versionCode: versionCode
|
|
113
124
|
});
|
|
114
|
-
const deliveryResult = yield* delivery(
|
|
125
|
+
const deliveryResult = yield* delivery(bundleIdentifier, {
|
|
115
126
|
deliveryToken: encodedDeliveryToken,
|
|
116
|
-
offerType:
|
|
117
|
-
versionCode:
|
|
127
|
+
offerType: offerType,
|
|
128
|
+
versionCode: versionCode
|
|
118
129
|
});
|
|
119
130
|
const mainDeliveryData = deliveryResult?.appDeliveryData;
|
|
120
131
|
if (mainDeliveryData === undefined) {
|
|
121
132
|
return yield* Effect.die("No delivery data available");
|
|
122
133
|
}
|
|
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
134
|
const main = Effect.gen(function* () {
|
|
125
|
-
const file = yield* fileSystem.makeTempFileScoped({
|
|
126
|
-
suffix: ".apk"
|
|
127
|
-
});
|
|
128
135
|
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
|
-
}
|
|
136
|
+
const stream = yield* HttpClient.get(mainDeliveryData.downloadUrl).pipe(Effect.map(response => response.stream));
|
|
137
|
+
yield* Effect.logDebug(`fetched main APK of ${mainDeliveryData.downloadSize}bytes from ${mainDeliveryData.downloadUrl} with integrity ${integrity}(sha256)`);
|
|
134
138
|
return {
|
|
135
|
-
|
|
139
|
+
stream,
|
|
136
140
|
name: `${bundleIdentifier}.apk`,
|
|
137
141
|
url: mainDeliveryData.downloadUrl,
|
|
138
142
|
size: mainDeliveryData.downloadSize,
|
|
139
143
|
integrity: {
|
|
140
|
-
|
|
144
|
+
"SHA-256": integrity
|
|
141
145
|
}
|
|
142
146
|
};
|
|
143
147
|
});
|
|
144
148
|
const splits = Array.map(mainDeliveryData.splitDeliveryData, split => Effect.gen(function* () {
|
|
145
|
-
const file = yield* fileSystem.makeTempFileScoped({
|
|
146
|
-
suffix: ".apk"
|
|
147
|
-
});
|
|
148
149
|
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
|
-
}
|
|
150
|
+
const stream = yield* HttpClient.get(split.downloadUrl).pipe(Effect.map(response => response.stream));
|
|
151
|
+
yield* Effect.logDebug(`fetched split ${split.name} of ${split.downloadSize}bytes from ${split.downloadUrl} with integrity ${integrity}(sha256)`);
|
|
154
152
|
return {
|
|
155
|
-
|
|
153
|
+
stream,
|
|
156
154
|
url: split.downloadUrl,
|
|
157
155
|
size: split.downloadSize,
|
|
158
156
|
name: `${split.name}.apk`,
|
|
159
157
|
integrity: {
|
|
160
|
-
|
|
158
|
+
"SHA-256": integrity
|
|
161
159
|
}
|
|
162
160
|
};
|
|
163
161
|
}));
|
|
164
162
|
const expansions = Array.map(mainDeliveryData.additionalFile, expansion => Effect.gen(function* () {
|
|
165
163
|
const typeStr = expansion.fileType === 1 ? "main" : "patch";
|
|
166
164
|
const name = `${typeStr}.${expansion.versionCode}.${bundleIdentifier}.obb`;
|
|
167
|
-
const file = yield* fileSystem.makeTempFileScoped({
|
|
168
|
-
suffix: ".obb"
|
|
169
|
-
});
|
|
170
165
|
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
|
-
}
|
|
166
|
+
const stream = yield* HttpClient.get(expansion.downloadUrl).pipe(Effect.map(response => response.stream));
|
|
167
|
+
yield* Effect.logDebug(`fetched expansion file ${name} of ${expansion.size}bytes from ${expansion.downloadUrl} with integrity ${integrity}(sha1)`);
|
|
176
168
|
return {
|
|
177
|
-
file,
|
|
178
169
|
name,
|
|
170
|
+
stream,
|
|
179
171
|
size: expansion.size,
|
|
180
172
|
url: expansion.downloadUrl,
|
|
181
173
|
integrity: {
|
|
182
|
-
|
|
174
|
+
"SHA-1": integrity
|
|
183
175
|
}
|
|
184
176
|
};
|
|
185
177
|
}));
|
|
186
178
|
return yield* Effect.all([main, ...splits, ...expansions]);
|
|
187
179
|
});
|
|
180
|
+
/**
|
|
181
|
+
* @since 1.0.0
|
|
182
|
+
* @category API
|
|
183
|
+
*/
|
|
184
|
+
export const downloadToDisk = /*#__PURE__*/Effect.fnUntraced(function* (bundleIdentifier, options) {
|
|
185
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
186
|
+
const streams = yield* downloadToStreams(bundleIdentifier, options);
|
|
187
|
+
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("")));
|
|
188
|
+
const write = Effect.fnUntraced(function* ({
|
|
189
|
+
stream,
|
|
190
|
+
name,
|
|
191
|
+
url,
|
|
192
|
+
size,
|
|
193
|
+
integrity
|
|
194
|
+
}) {
|
|
195
|
+
const file = yield* fileSystem.makeTempFileScoped();
|
|
196
|
+
const digestAlgorithm = Match.value(integrity).pipe(Match.when({
|
|
197
|
+
"SHA-1": Match.string
|
|
198
|
+
}, () => "SHA-1"), Match.when({
|
|
199
|
+
"SHA-256": Match.string
|
|
200
|
+
}, () => "SHA-256"), Match.when({
|
|
201
|
+
"SHA-384": Match.string
|
|
202
|
+
}, () => "SHA-384"), Match.when({
|
|
203
|
+
"SHA-512": Match.string
|
|
204
|
+
}, () => "SHA-512"), Match.exhaustive);
|
|
205
|
+
const expectedIntegrity = integrity[digestAlgorithm];
|
|
206
|
+
const downloadedIntegrity = yield* stream.pipe(Stream.tapSink(fileSystem.sink(file)), digest(digestAlgorithm));
|
|
207
|
+
if (downloadedIntegrity !== expectedIntegrity) {
|
|
208
|
+
return yield* Effect.die(`Downloaded ${name} integrity mismatch: expected ${integrity}, got ${downloadedIntegrity}`);
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
url,
|
|
212
|
+
name,
|
|
213
|
+
file,
|
|
214
|
+
size,
|
|
215
|
+
integrity
|
|
216
|
+
};
|
|
217
|
+
});
|
|
218
|
+
const effects = Array.map(streams, write);
|
|
219
|
+
return yield* Effect.all(effects);
|
|
220
|
+
});
|
|
188
221
|
//# sourceMappingURL=GooglePlayApi.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GooglePlayApi.js","names":["Array","Effect","FileSystem","
|
|
1
|
+
{"version":3,"file":"GooglePlayApi.js","names":["Array","Crypto","Effect","FileSystem","Match","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","die","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","effects"],"sources":["../src/GooglePlayApi.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -1,2 +1,63 @@
|
|
|
1
|
+
import type * as PlatformError from "effect/PlatformError";
|
|
2
|
+
import type * as HttpClient from "effect/unstable/http/HttpClient";
|
|
3
|
+
import type * as HttpClientError from "effect/unstable/http/HttpClientError";
|
|
4
|
+
import * as Context from "effect/Context";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as FileSystem from "effect/FileSystem";
|
|
7
|
+
import * as Layer from "effect/Layer";
|
|
8
|
+
import * as Path from "effect/Path";
|
|
9
|
+
import * as Schema from "effect/Schema";
|
|
10
|
+
export declare const StringArrayFromString: Schema.suspend<Schema.decodeTo<Schema.$Array<Schema.String>, Schema.String, never, never>>;
|
|
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
|
+
}
|
|
15
|
+
declare const AndroidDevice_base: Schema.Class<AndroidDevice, Schema.Struct<{
|
|
16
|
+
readonly UserReadableName: Schema.String;
|
|
17
|
+
readonly "Build.BOOTLOADER": Schema.String;
|
|
18
|
+
readonly "Build.BRAND": Schema.String;
|
|
19
|
+
readonly "Build.DEVICE": Schema.String;
|
|
20
|
+
readonly "Build.FINGERPRINT": Schema.String;
|
|
21
|
+
readonly "Build.HARDWARE": Schema.String;
|
|
22
|
+
readonly "Build.ID": Schema.String;
|
|
23
|
+
readonly "Build.MANUFACTURER": Schema.String;
|
|
24
|
+
readonly "Build.MODEL": Schema.String;
|
|
25
|
+
readonly "Build.PRODUCT": Schema.String;
|
|
26
|
+
readonly "Build.RADIO": Schema.String;
|
|
27
|
+
readonly "Build.VERSION.RELEASE": Schema.String;
|
|
28
|
+
readonly "Build.VERSION.SDK_INT": Schema.NumberFromString;
|
|
29
|
+
readonly CellOperator: Schema.String;
|
|
30
|
+
readonly Client: Schema.String;
|
|
31
|
+
readonly Features: Schema.suspend<Schema.decodeTo<Schema.$Array<Schema.String>, Schema.String, never, never>>;
|
|
32
|
+
readonly "GL.Extensions": Schema.suspend<Schema.decodeTo<Schema.$Array<Schema.String>, Schema.String, never, never>>;
|
|
33
|
+
readonly "GL.Version": Schema.NumberFromString;
|
|
34
|
+
readonly "GSF.version": Schema.NumberFromString;
|
|
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>]>>;
|
|
37
|
+
readonly Keyboard: Schema.NumberFromString;
|
|
38
|
+
readonly Locales: Schema.suspend<Schema.decodeTo<Schema.$Array<Schema.String>, Schema.String, never, never>>;
|
|
39
|
+
readonly Navigation: Schema.NumberFromString;
|
|
40
|
+
readonly Platforms: Schema.suspend<Schema.decodeTo<Schema.$Array<Schema.String>, Schema.String, never, never>>;
|
|
41
|
+
readonly Roaming: Schema.String;
|
|
42
|
+
readonly "Screen.Density": Schema.NumberFromString;
|
|
43
|
+
readonly "Screen.Height": Schema.NumberFromString;
|
|
44
|
+
readonly "Screen.Width": Schema.NumberFromString;
|
|
45
|
+
readonly ScreenLayout: Schema.NumberFromString;
|
|
46
|
+
readonly SharedLibraries: Schema.suspend<Schema.decodeTo<Schema.$Array<Schema.String>, Schema.String, never, never>>;
|
|
47
|
+
readonly SimCountry: Schema.optional<Schema.String>;
|
|
48
|
+
readonly SimOperator: Schema.optional<Schema.String>;
|
|
49
|
+
readonly TimeZone: Schema.String;
|
|
50
|
+
readonly TouchScreen: Schema.NumberFromString;
|
|
51
|
+
readonly "Vending.version": Schema.NumberFromString;
|
|
52
|
+
readonly "Vending.versionString": Schema.String;
|
|
53
|
+
}>, {}>;
|
|
54
|
+
export declare class AndroidDevice extends AndroidDevice_base {
|
|
55
|
+
private authHeadersCache?;
|
|
56
|
+
static fromPropertiesFile: (file: string) => Effect.Effect<AndroidDevice, Schema.SchemaError | PlatformError.PlatformError, FileSystem.FileSystem>;
|
|
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>;
|
|
59
|
+
get userAgent(): string;
|
|
60
|
+
readonly authHeaders: Effect.Effect<Record<string, string>, HttpClientError.HttpClientError | Schema.SchemaError, HttpClient.HttpClient>;
|
|
61
|
+
}
|
|
1
62
|
export {};
|
|
2
63
|
//# sourceMappingURL=device.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/internal/device.ts"],"names":[],"mappings":""}
|
|
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,11 +1,12 @@
|
|
|
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";
|
|
6
8
|
import * as SchemaTransformation from "effect/SchemaTransformation";
|
|
7
9
|
import * as internalAuth from "./auth.js";
|
|
8
|
-
/** @internal */
|
|
9
10
|
export const StringArrayFromString = /*#__PURE__*/Schema.suspend(() => {
|
|
10
11
|
const splitter = SchemaGetter.split({
|
|
11
12
|
separator: ","
|
|
@@ -17,12 +18,8 @@ export const StringArrayFromString = /*#__PURE__*/Schema.suspend(() => {
|
|
|
17
18
|
});
|
|
18
19
|
return Schema.String.pipe(Schema.decodeTo(Schema.Array(Schema.String), transform));
|
|
19
20
|
});
|
|
20
|
-
|
|
21
|
-
export
|
|
22
|
-
decode: str => str === "true",
|
|
23
|
-
encode: bool => bool ? "true" : "false"
|
|
24
|
-
})));
|
|
25
|
-
/** @internal */
|
|
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") {}
|
|
26
23
|
export class AndroidDevice extends /*#__PURE__*/Schema.Class("AndroidDevice")({
|
|
27
24
|
UserReadableName: Schema.String,
|
|
28
25
|
"Build.BOOTLOADER": Schema.String,
|
|
@@ -75,6 +72,7 @@ export class AndroidDevice extends /*#__PURE__*/Schema.Class("AndroidDevice")({
|
|
|
75
72
|
return yield* decodeDevice(properties);
|
|
76
73
|
});
|
|
77
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);
|
|
78
76
|
get userAgent() {
|
|
79
77
|
const deviceProperties = {
|
|
80
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
|
@@ -11,15 +11,13 @@ import type * as Scope from "effect/Scope";
|
|
|
11
11
|
import type * as HttpClientError from "effect/unstable/http/HttpClientError";
|
|
12
12
|
|
|
13
13
|
import * as Array from "effect/Array";
|
|
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";
|
|
17
18
|
import * as Stream from "effect/Stream";
|
|
18
19
|
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
19
20
|
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
21
|
|
|
24
22
|
import {
|
|
25
23
|
BulkDetailsRequestSchema,
|
|
@@ -28,6 +26,7 @@ import {
|
|
|
28
26
|
type DeliveryResponse,
|
|
29
27
|
type DetailsResponse,
|
|
30
28
|
} from "./generated/GooglePlay_pb.ts";
|
|
29
|
+
import { Service as AndroidDeviceService } from "./internal/device.ts";
|
|
31
30
|
import { decodeResponseFromResponseWrapper, encodeRequest } from "./internal/http.ts";
|
|
32
31
|
|
|
33
32
|
/** @internal */
|
|
@@ -39,200 +38,169 @@ export {
|
|
|
39
38
|
* @category Device
|
|
40
39
|
*/
|
|
41
40
|
AndroidDevice,
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @since 1.0.0
|
|
44
|
+
* @category Device
|
|
45
|
+
*/
|
|
46
|
+
Service as AndroidDeviceService,
|
|
42
47
|
} from "./internal/device.ts";
|
|
43
48
|
|
|
44
49
|
/**
|
|
45
50
|
* @since 1.0.0
|
|
46
51
|
* @category API
|
|
47
52
|
*/
|
|
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
|
-
);
|
|
53
|
+
export const details = Effect.fnUntraced(function* (
|
|
54
|
+
bundleIdentifier: string
|
|
55
|
+
): Effect.fn.Return<
|
|
56
|
+
DetailsResponse,
|
|
57
|
+
HttpClientError.HttpClientError | Schema.SchemaError,
|
|
58
|
+
HttpClient.HttpClient | AndroidDeviceService
|
|
59
|
+
> {
|
|
60
|
+
const decoderDetailsResponse = decodeResponseFromResponseWrapper("detailsResponse");
|
|
61
|
+
const device = yield* AndroidDeviceService;
|
|
62
|
+
|
|
63
|
+
const httpRequest = HttpClientRequest.get("/details", {
|
|
64
|
+
urlParams: { doc: bundleIdentifier },
|
|
65
|
+
headers: yield* device.authHeaders,
|
|
66
|
+
}).pipe(HttpClientRequest.prependUrl(fdfeBase));
|
|
67
|
+
|
|
68
|
+
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
69
|
+
const pbResponse = yield* decoderDetailsResponse(httpResponse);
|
|
70
|
+
return pbResponse;
|
|
71
|
+
});
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
74
|
* @since 1.0.0
|
|
75
75
|
* @category API
|
|
76
76
|
*/
|
|
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
|
-
);
|
|
77
|
+
export const bulkDetails = Effect.fnUntraced(function* (
|
|
78
|
+
bundleIdentifier: string
|
|
79
|
+
): Effect.fn.Return<
|
|
80
|
+
BulkDetailsResponse,
|
|
81
|
+
HttpClientError.HttpClientError | Schema.SchemaError,
|
|
82
|
+
HttpClient.HttpClient | AndroidDeviceService
|
|
83
|
+
> {
|
|
84
|
+
const decoderBulkDetailsResponse = decodeResponseFromResponseWrapper("bulkDetailsResponse");
|
|
85
|
+
const encoderBulkDetailsRequest = encodeRequest(BulkDetailsRequestSchema, {
|
|
86
|
+
includeDetails: true,
|
|
87
|
+
includeChildDocs: true,
|
|
88
|
+
DocId: [bundleIdentifier],
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const device = yield* AndroidDeviceService;
|
|
92
|
+
const httpRequest = HttpClientRequest.post("/bulkDetails", {
|
|
93
|
+
headers: yield* device.authHeaders,
|
|
94
|
+
}).pipe(HttpClientRequest.prependUrl(fdfeBase));
|
|
95
|
+
|
|
96
|
+
const pbRequest = yield* encoderBulkDetailsRequest(httpRequest);
|
|
97
|
+
const httpResponse = yield* HttpClient.execute(pbRequest);
|
|
98
|
+
const pbResponse = yield* decoderBulkDetailsResponse(httpResponse);
|
|
99
|
+
return pbResponse;
|
|
100
|
+
});
|
|
111
101
|
|
|
112
102
|
/**
|
|
113
103
|
* @since 1.0.0
|
|
114
104
|
* @category API
|
|
115
105
|
*/
|
|
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
|
-
);
|
|
106
|
+
export const purchase = Effect.fnUntraced(function* (
|
|
107
|
+
bundleIdentifier: string,
|
|
108
|
+
options: { offerType: number; versionCode: number | bigint; certificateHash?: string }
|
|
109
|
+
): Effect.fn.Return<
|
|
110
|
+
BuyResponse,
|
|
111
|
+
HttpClientError.HttpClientError | Schema.SchemaError,
|
|
112
|
+
HttpClient.HttpClient | AndroidDeviceService
|
|
113
|
+
> {
|
|
114
|
+
const decoderBuyResponse = decodeResponseFromResponseWrapper("buyResponse");
|
|
115
|
+
|
|
116
|
+
const device = yield* AndroidDeviceService;
|
|
117
|
+
const httpRequest = HttpClientRequest.post("/purchase", {
|
|
118
|
+
headers: yield* device.authHeaders,
|
|
119
|
+
urlParams: {
|
|
120
|
+
doc: bundleIdentifier,
|
|
121
|
+
ch: options.certificateHash ?? "",
|
|
122
|
+
ot: options.offerType.toString(),
|
|
123
|
+
vc: options.versionCode.toString(),
|
|
124
|
+
},
|
|
125
|
+
}).pipe(HttpClientRequest.prependUrl(fdfeBase));
|
|
126
|
+
|
|
127
|
+
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
128
|
+
const pbResponse = yield* decoderBuyResponse(httpResponse);
|
|
129
|
+
return pbResponse;
|
|
130
|
+
});
|
|
151
131
|
|
|
152
132
|
/**
|
|
153
133
|
* @since 1.0.0
|
|
154
134
|
* @category API
|
|
155
135
|
*/
|
|
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;
|
|
136
|
+
export const delivery = Effect.fnUntraced(function* (
|
|
137
|
+
bundleIdentifier: string,
|
|
138
|
+
options: {
|
|
139
|
+
offerType: number;
|
|
140
|
+
deliveryToken: string;
|
|
141
|
+
versionCode: number | bigint;
|
|
142
|
+
certificateHash?: string | undefined;
|
|
143
|
+
}
|
|
144
|
+
): Effect.fn.Return<
|
|
145
|
+
DeliveryResponse,
|
|
146
|
+
HttpClientError.HttpClientError | Schema.SchemaError,
|
|
147
|
+
HttpClient.HttpClient | AndroidDeviceService
|
|
148
|
+
> {
|
|
149
|
+
const decoderDeliveryResponse = decodeResponseFromResponseWrapper("deliveryResponse");
|
|
150
|
+
|
|
151
|
+
const device = yield* AndroidDeviceService;
|
|
152
|
+
const httpRequest = HttpClientRequest.get("/delivery", {
|
|
153
|
+
headers: yield* device.authHeaders,
|
|
154
|
+
urlParams: {
|
|
155
|
+
doc: bundleIdentifier,
|
|
156
|
+
dtok: options.deliveryToken,
|
|
157
|
+
ch: options.certificateHash ?? "",
|
|
158
|
+
ot: options.offerType.toString(),
|
|
159
|
+
vc: options.versionCode.toString(),
|
|
185
160
|
},
|
|
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
|
-
);
|
|
161
|
+
}).pipe(HttpClientRequest.prependUrl(fdfeBase));
|
|
162
|
+
|
|
163
|
+
const httpResponse = yield* HttpClient.execute(httpRequest);
|
|
164
|
+
const pbResponse = yield* decoderDeliveryResponse(httpResponse);
|
|
165
|
+
return pbResponse;
|
|
166
|
+
});
|
|
205
167
|
|
|
206
168
|
/**
|
|
207
169
|
* @since 1.0.0
|
|
208
170
|
* @category API
|
|
209
171
|
*/
|
|
210
|
-
export const
|
|
211
|
-
|
|
212
|
-
|
|
172
|
+
export const downloadToStreams = Effect.fnUntraced(function* (
|
|
173
|
+
bundleIdentifier: string,
|
|
174
|
+
options?:
|
|
175
|
+
| {
|
|
176
|
+
offerType?: number | undefined;
|
|
177
|
+
versionCode?: number | bigint | undefined;
|
|
178
|
+
}
|
|
179
|
+
| undefined
|
|
213
180
|
): Effect.fn.Return<
|
|
214
181
|
Array.NonEmptyReadonlyArray<{
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
file: string;
|
|
182
|
+
stream: Stream.Stream<Uint8Array, HttpClientError.HttpClientError, never>;
|
|
183
|
+
integrity: { "SHA-1": string } | { "SHA-256": string } | { "SHA-384": string } | { "SHA-512": string };
|
|
218
184
|
size: bigint;
|
|
219
|
-
|
|
185
|
+
name: string;
|
|
186
|
+
url: string;
|
|
220
187
|
}>,
|
|
221
|
-
|
|
222
|
-
|
|
188
|
+
HttpClientError.HttpClientError | Schema.SchemaError,
|
|
189
|
+
AndroidDeviceService | HttpClient.HttpClient
|
|
223
190
|
> {
|
|
224
|
-
const
|
|
191
|
+
const { item } = yield* details(bundleIdentifier);
|
|
192
|
+
const offerType = options?.offerType ?? item?.offer[0].offerType ?? 1;
|
|
193
|
+
const versionCode = options?.versionCode ?? item?.details?.appDetails?.versionCode ?? 0;
|
|
225
194
|
|
|
226
|
-
const {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
versionCode: item?.details?.appDetails?.versionCode ?? 0,
|
|
195
|
+
const { encodedDeliveryToken } = yield* purchase(bundleIdentifier, {
|
|
196
|
+
offerType: offerType,
|
|
197
|
+
versionCode: versionCode,
|
|
230
198
|
});
|
|
231
199
|
|
|
232
|
-
const deliveryResult = yield* delivery(
|
|
200
|
+
const deliveryResult = yield* delivery(bundleIdentifier, {
|
|
233
201
|
deliveryToken: encodedDeliveryToken,
|
|
234
|
-
offerType:
|
|
235
|
-
versionCode:
|
|
202
|
+
offerType: offerType,
|
|
203
|
+
versionCode: versionCode,
|
|
236
204
|
});
|
|
237
205
|
|
|
238
206
|
const mainDeliveryData = deliveryResult?.appDeliveryData;
|
|
@@ -240,75 +208,40 @@ export const download = Effect.fnUntraced(function* (
|
|
|
240
208
|
return yield* Effect.die("No delivery data available");
|
|
241
209
|
}
|
|
242
210
|
|
|
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
211
|
const main = Effect.gen(function* () {
|
|
259
|
-
const file = yield* fileSystem.makeTempFileScoped({ suffix: ".apk" });
|
|
260
212
|
const integrity = Buffer.from(mainDeliveryData.sha256, "base64url").toString("hex");
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
Stream.tapSink(fileSystem.sink(file)),
|
|
264
|
-
digest("SHA-256")
|
|
213
|
+
const stream = yield* HttpClient.get(mainDeliveryData.downloadUrl).pipe(
|
|
214
|
+
Effect.map((response) => response.stream)
|
|
265
215
|
);
|
|
266
216
|
|
|
267
217
|
yield* Effect.logDebug(
|
|
268
|
-
`main APK of ${mainDeliveryData.downloadSize}bytes from ${mainDeliveryData.downloadUrl} with integrity ${integrity}(sha256)
|
|
218
|
+
`fetched main APK of ${mainDeliveryData.downloadSize}bytes from ${mainDeliveryData.downloadUrl} with integrity ${integrity}(sha256)`
|
|
269
219
|
);
|
|
270
220
|
|
|
271
|
-
if (downloadedIntegrity !== integrity) {
|
|
272
|
-
return yield* Effect.die(
|
|
273
|
-
`Downloaded main APK integrity mismatch: expected ${integrity}, got ${downloadedIntegrity}`
|
|
274
|
-
);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
221
|
return {
|
|
278
|
-
|
|
222
|
+
stream,
|
|
279
223
|
name: `${bundleIdentifier}.apk`,
|
|
280
224
|
url: mainDeliveryData.downloadUrl,
|
|
281
225
|
size: mainDeliveryData.downloadSize,
|
|
282
|
-
integrity: {
|
|
226
|
+
integrity: { "SHA-256": integrity },
|
|
283
227
|
};
|
|
284
228
|
});
|
|
285
229
|
|
|
286
230
|
const splits = Array.map(mainDeliveryData.splitDeliveryData, (split) =>
|
|
287
231
|
Effect.gen(function* () {
|
|
288
|
-
const file = yield* fileSystem.makeTempFileScoped({ suffix: ".apk" });
|
|
289
232
|
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
|
-
);
|
|
233
|
+
const stream = yield* HttpClient.get(split.downloadUrl).pipe(Effect.map((response) => response.stream));
|
|
295
234
|
|
|
296
235
|
yield* Effect.logDebug(
|
|
297
|
-
`split ${split.name} of ${split.downloadSize}bytes from ${split.downloadUrl} with integrity ${integrity}(sha256)
|
|
236
|
+
`fetched split ${split.name} of ${split.downloadSize}bytes from ${split.downloadUrl} with integrity ${integrity}(sha256)`
|
|
298
237
|
);
|
|
299
238
|
|
|
300
|
-
if (downloadedIntegrity !== integrity) {
|
|
301
|
-
return yield* Effect.die(
|
|
302
|
-
`Downloaded split ${split.name} integrity mismatch: expected ${integrity}, got ${downloadedIntegrity}`
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
239
|
return {
|
|
307
|
-
|
|
240
|
+
stream,
|
|
308
241
|
url: split.downloadUrl,
|
|
309
242
|
size: split.downloadSize,
|
|
310
243
|
name: `${split.name}.apk`,
|
|
311
|
-
integrity: {
|
|
244
|
+
integrity: { "SHA-256": integrity },
|
|
312
245
|
};
|
|
313
246
|
})
|
|
314
247
|
);
|
|
@@ -317,33 +250,106 @@ export const download = Effect.fnUntraced(function* (
|
|
|
317
250
|
Effect.gen(function* () {
|
|
318
251
|
const typeStr = expansion.fileType === 1 ? "main" : "patch";
|
|
319
252
|
const name = `${typeStr}.${expansion.versionCode}.${bundleIdentifier}.obb`;
|
|
320
|
-
const file = yield* fileSystem.makeTempFileScoped({ suffix: ".obb" });
|
|
321
253
|
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
|
-
);
|
|
254
|
+
const stream = yield* HttpClient.get(expansion.downloadUrl).pipe(Effect.map((response) => response.stream));
|
|
327
255
|
|
|
328
256
|
yield* Effect.logDebug(
|
|
329
|
-
`expansion file ${name} of ${expansion.size}bytes from ${expansion.downloadUrl} with integrity ${integrity}(sha1)
|
|
257
|
+
`fetched expansion file ${name} of ${expansion.size}bytes from ${expansion.downloadUrl} with integrity ${integrity}(sha1)`
|
|
330
258
|
);
|
|
331
259
|
|
|
332
|
-
if (downloadedIntegrity !== integrity) {
|
|
333
|
-
return yield* Effect.die(
|
|
334
|
-
`Downloaded expansion file ${name} integrity mismatch: expected ${integrity}, got ${downloadedIntegrity}`
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
260
|
return {
|
|
339
|
-
file,
|
|
340
261
|
name,
|
|
262
|
+
stream,
|
|
341
263
|
size: expansion.size,
|
|
342
264
|
url: expansion.downloadUrl,
|
|
343
|
-
integrity: {
|
|
265
|
+
integrity: { "SHA-1": integrity },
|
|
344
266
|
};
|
|
345
267
|
})
|
|
346
268
|
);
|
|
347
269
|
|
|
348
270
|
return yield* Effect.all([main, ...splits, ...expansions]);
|
|
349
271
|
});
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* @since 1.0.0
|
|
275
|
+
* @category API
|
|
276
|
+
*/
|
|
277
|
+
export const downloadToDisk = Effect.fnUntraced(function* (
|
|
278
|
+
bundleIdentifier: string,
|
|
279
|
+
options?:
|
|
280
|
+
| {
|
|
281
|
+
offerType?: number | undefined;
|
|
282
|
+
versionCode?: number | bigint | undefined;
|
|
283
|
+
}
|
|
284
|
+
| undefined
|
|
285
|
+
): Effect.fn.Return<
|
|
286
|
+
Array.NonEmptyReadonlyArray<{
|
|
287
|
+
url: string;
|
|
288
|
+
name: string;
|
|
289
|
+
file: string;
|
|
290
|
+
size: bigint;
|
|
291
|
+
integrity: { "SHA-1": string } | { "SHA-256": string } | { "SHA-384": string } | { "SHA-512": string };
|
|
292
|
+
}>,
|
|
293
|
+
PlatformError.PlatformError | HttpClientError.HttpClientError | Schema.SchemaError,
|
|
294
|
+
AndroidDeviceService | Crypto.Crypto | HttpClient.HttpClient | FileSystem.FileSystem | Scope.Scope
|
|
295
|
+
> {
|
|
296
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
297
|
+
const streams = yield* downloadToStreams(bundleIdentifier, options);
|
|
298
|
+
|
|
299
|
+
const digest =
|
|
300
|
+
(algorithm: Crypto.DigestAlgorithm) =>
|
|
301
|
+
<E, R>(
|
|
302
|
+
stream: Stream.Stream<Uint8Array, E, R>
|
|
303
|
+
): Effect.Effect<string, E | PlatformError.PlatformError, R | Crypto.Crypto> =>
|
|
304
|
+
stream.pipe(
|
|
305
|
+
Stream.mkUint8Array,
|
|
306
|
+
Effect.flatMap((buffer) => Crypto.Crypto.use((crypto) => crypto.digest(algorithm, buffer))),
|
|
307
|
+
Effect.map((digestBuffer) =>
|
|
308
|
+
Array.fromIterable(digestBuffer)
|
|
309
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
310
|
+
.join("")
|
|
311
|
+
)
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
const write: (stream: (typeof streams)[number]) => Effect.Effect<
|
|
315
|
+
{
|
|
316
|
+
url: string;
|
|
317
|
+
name: string;
|
|
318
|
+
file: string;
|
|
319
|
+
size: bigint;
|
|
320
|
+
integrity: { "SHA-1": string } | { "SHA-256": string } | { "SHA-384": string } | { "SHA-512": string };
|
|
321
|
+
},
|
|
322
|
+
PlatformError.PlatformError | HttpClientError.HttpClientError,
|
|
323
|
+
Crypto.Crypto | Scope.Scope
|
|
324
|
+
> = Effect.fnUntraced(function* ({ stream, name, url, size, integrity }) {
|
|
325
|
+
const file = yield* fileSystem.makeTempFileScoped();
|
|
326
|
+
|
|
327
|
+
const digestAlgorithm = Match.value(integrity).pipe(
|
|
328
|
+
Match.when({ "SHA-1": Match.string }, () => "SHA-1" as const),
|
|
329
|
+
Match.when({ "SHA-256": Match.string }, () => "SHA-256" as const),
|
|
330
|
+
Match.when({ "SHA-384": Match.string }, () => "SHA-384" as const),
|
|
331
|
+
Match.when({ "SHA-512": Match.string }, () => "SHA-512" as const),
|
|
332
|
+
Match.exhaustive
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
const expectedIntegrity = (integrity as Record<Crypto.DigestAlgorithm, string>)[digestAlgorithm];
|
|
336
|
+
const downloadedIntegrity = yield* stream.pipe(Stream.tapSink(fileSystem.sink(file)), digest(digestAlgorithm));
|
|
337
|
+
|
|
338
|
+
if (downloadedIntegrity !== expectedIntegrity) {
|
|
339
|
+
return yield* Effect.die(
|
|
340
|
+
`Downloaded ${name} integrity mismatch: expected ${integrity}, got ${downloadedIntegrity}`
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
url,
|
|
346
|
+
name,
|
|
347
|
+
file,
|
|
348
|
+
size,
|
|
349
|
+
integrity,
|
|
350
|
+
};
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
const effects = Array.map(streams, write);
|
|
354
|
+
return yield* Effect.all(effects);
|
|
355
|
+
});
|
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";
|
|
@@ -11,7 +13,6 @@ import * as SchemaTransformation from "effect/SchemaTransformation";
|
|
|
11
13
|
|
|
12
14
|
import * as internalAuth from "./auth.ts";
|
|
13
15
|
|
|
14
|
-
/** @internal */
|
|
15
16
|
export const StringArrayFromString = Schema.suspend(() => {
|
|
16
17
|
const splitter = SchemaGetter.split({ separator: "," });
|
|
17
18
|
const joiner = SchemaGetter.transform((arr: ReadonlyArray<string>) => arr.join(","));
|
|
@@ -19,18 +20,12 @@ export const StringArrayFromString = Schema.suspend(() => {
|
|
|
19
20
|
return Schema.String.pipe(Schema.decodeTo(Schema.Array(Schema.String), transform));
|
|
20
21
|
});
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Schema.decodeTo(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
decode: (str) => str === "true",
|
|
28
|
-
encode: (bool) => (bool ? "true" : "false"),
|
|
29
|
-
})
|
|
30
|
-
)
|
|
31
|
-
);
|
|
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") {}
|
|
32
28
|
|
|
33
|
-
/** @internal */
|
|
34
29
|
export class AndroidDevice extends Schema.Class<AndroidDevice>("AndroidDevice")({
|
|
35
30
|
UserReadableName: Schema.String,
|
|
36
31
|
"Build.BOOTLOADER": Schema.String,
|
|
@@ -99,6 +94,8 @@ export class AndroidDevice extends Schema.Class<AndroidDevice>("AndroidDevice")(
|
|
|
99
94
|
Effect.flatMap(AndroidDevice.fromPropertiesFile)
|
|
100
95
|
);
|
|
101
96
|
|
|
97
|
+
public static EmbeddedPixel7aLive = Layer.effect(Service, AndroidDevice.EmbeddedPixel7a);
|
|
98
|
+
|
|
102
99
|
public get userAgent(): string {
|
|
103
100
|
const deviceProperties = {
|
|
104
101
|
api: 3,
|