@efffrida/gplayapi 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+
5
+ /**
6
+ * Unofficial Google Play Store API for downloading APKs directly from Google
7
+ * Play Store.
8
+ *
9
+ * @since 1.0.0
10
+ */
11
+ export * as GooglePlayApi from "./GooglePlayApi.ts"
@@ -0,0 +1,220 @@
1
+ import type * as HttpClientError from "@effect/platform/HttpClientError";
2
+ import type * as ParseResult from "effect/ParseResult";
3
+
4
+ import * as HttpClient from "@effect/platform/HttpClient";
5
+ import * as HttpClientRequest from "@effect/platform/HttpClientRequest";
6
+ import * as HttpClientResponse from "@effect/platform/HttpClientResponse";
7
+ import * as Effect from "effect/Effect";
8
+ import * as Function from "effect/Function";
9
+ import * as Layer from "effect/Layer";
10
+ import * as Schema from "effect/Schema";
11
+
12
+ import {
13
+ AndroidCheckinRequestSchema,
14
+ AndroidCheckinResponseSchema,
15
+ UploadDeviceConfigRequestSchema,
16
+ } from "../generated/GooglePlay_pb.js";
17
+ import { type Device } from "./device.js";
18
+ import { decodeResponse, decodeResponseFromResponseWrapper, encodeRequest } from "./http.js";
19
+
20
+ /** @internal */
21
+ export const makeHttpClient: (
22
+ device: Device
23
+ ) => Layer.Layer<
24
+ HttpClient.HttpClient,
25
+ HttpClientError.HttpClientError | ParseResult.ParseError,
26
+ HttpClient.HttpClient
27
+ > = Effect.fnUntraced(function* (device: Device) {
28
+ // curl \
29
+ // --request GET \
30
+ // --header "Accept: application/json" \
31
+ // --header "User-Agent: com.aurora.store-4.7.5-71" \
32
+ // "https://auroraoss.com/api/auth"
33
+ const account = yield* Function.pipe(
34
+ HttpClientRequest.get("https://auroraoss.com/api/auth"),
35
+ HttpClientRequest.setHeader("User-Agent", "com.aurora.store-4.7.5-71"),
36
+ HttpClientRequest.acceptJson,
37
+ HttpClient.execute,
38
+ Effect.flatMap(HttpClientResponse.filterStatusOk),
39
+ Effect.flatMap(
40
+ HttpClientResponse.schemaBodyJson(
41
+ Schema.Struct({
42
+ email: Schema.String,
43
+ auth: Schema.String,
44
+ })
45
+ )
46
+ )
47
+ );
48
+
49
+ const checkinResponse = yield* Function.pipe(
50
+ HttpClientRequest.post("https://android.clients.google.com/checkin"),
51
+ HttpClientRequest.setHeaders({
52
+ "User-Agent": device.userAgent,
53
+ app: "com.google.android.gms",
54
+ Host: "android.clients.google.com",
55
+ }),
56
+ encodeRequest(AndroidCheckinRequestSchema, {
57
+ id: 0n,
58
+ version: 3,
59
+ fragment: 0,
60
+ locale: "en",
61
+ timeZone: device.TimeZone,
62
+ checkin: {
63
+ userNumber: 0,
64
+ lastCheckinMsec: 0n,
65
+ roaming: device.Roaming,
66
+ cellOperator: device.CellOperator,
67
+ simOperator: device.SimOperator ?? "",
68
+ build: {
69
+ id: device["Build.FINGERPRINT"],
70
+ product: device["Build.HARDWARE"],
71
+ carrier: device["Build.BRAND"],
72
+ radio: device["Build.RADIO"],
73
+ bootloader: device["Build.BOOTLOADER"],
74
+ device: device["Build.DEVICE"],
75
+ sdkVersion: device["Build.VERSION.SDK_INT"],
76
+ model: device["Build.MODEL"],
77
+ manufacturer: device["Build.MANUFACTURER"],
78
+ buildProduct: device["Build.PRODUCT"],
79
+ client: device["Client"],
80
+ timestamp: BigInt(Date.now()),
81
+ googleServices: device["GSF.version"],
82
+ },
83
+ },
84
+ deviceConfiguration: {
85
+ touchScreen: device["TouchScreen"],
86
+ keyboard: device["Keyboard"],
87
+ navigation: device["Navigation"],
88
+ screenLayout: device["ScreenLayout"],
89
+ hasHardKeyboard: device["HasHardKeyboard"],
90
+ hasFiveWayNavigation: device["HasFiveWayNavigation"],
91
+ glEsVersion: device["GL.Version"],
92
+ glExtension: device["GL.Extensions"] as Array<string>,
93
+ systemSharedLibrary: device["SharedLibraries"] as Array<string>,
94
+ systemAvailableFeature: device["Features"] as Array<string>,
95
+ nativePlatform: device["Platforms"] as Array<string>,
96
+ screenDensity: device["Screen.Density"],
97
+ screenWidth: device["Screen.Width"],
98
+ screenHeight: device["Screen.Height"],
99
+ systemSupportedLocale: device["Locales"] as Array<string>,
100
+ deviceClass: 0,
101
+ deviceFeature: device["Features"].map((val) => ({ name: val, value: 0 })),
102
+ },
103
+ }),
104
+ Effect.flatMap(HttpClient.execute),
105
+ Effect.flatMap(HttpClientResponse.filterStatusOk),
106
+ Effect.flatMap(decodeResponse(AndroidCheckinResponseSchema))
107
+ );
108
+
109
+ const uploadDeviceConfigResponse = yield* Function.pipe(
110
+ HttpClientRequest.post("https://android.clients.google.com/fdfe/uploadDeviceConfig"),
111
+ HttpClientRequest.setHeaders({
112
+ "X-DFE-Encoded-Targets": "",
113
+ "User-Agent": device.userAgent,
114
+ "X-DFE-Cookie": "",
115
+ "X-DFE-Content-Filters": "",
116
+ "X-DFE-Device-Checkin-Consistency-Token": checkinResponse.deviceCheckinConsistencyToken,
117
+ "X-DFE-Device-Config-Token": "",
118
+ "X-DFE-MCCMNC": "21601",
119
+ "X-DFE-Client-Id": "am-android-google",
120
+ "X-DFE-UserLanguages": "en",
121
+ "X-DFE-Phenotype": "",
122
+ "X-DFE-Device-Id": checkinResponse.androidId.toString(16),
123
+ "X-DFE-Network-Type": "4",
124
+ "Accept-Language": "en",
125
+ "X-DFE-Request-Params": "timeoutMs=4000",
126
+ "X-DFE-Enabled-Experiments": "cl:billing.select_add_instrument_by_default",
127
+ "X-DFE-Unsupported-Experiments":
128
+ "nocache:billing.use_charging_poller,market_emails,buyer_currency,prod_baseline,checkin.set_asset_paid_app_field,shekel_test,content_ratings,buyer_currency_in_app,nocache:encrypted_apk,recent_changes",
129
+ Host: "android.clients.google.com",
130
+ }),
131
+ encodeRequest(UploadDeviceConfigRequestSchema, {
132
+ deviceConfiguration: {
133
+ touchScreen: Number(device["TouchScreen"]),
134
+ keyboard: Number(device["Keyboard"]),
135
+ navigation: Number(device["Navigation"]),
136
+ screenLayout: Number(device["ScreenLayout"]),
137
+ hasHardKeyboard: Boolean(device["HasHardKeyboard"]),
138
+ hasFiveWayNavigation: Boolean(device["HasFiveWayNavigation"]),
139
+ glEsVersion: Number(device["GL.Version"]),
140
+ glExtension: device["GL.Extensions"] as Array<string>,
141
+ systemSharedLibrary: device["SharedLibraries"] as Array<string>,
142
+ systemAvailableFeature: device["Features"] as Array<string>,
143
+ nativePlatform: device["Platforms"] as Array<string>,
144
+ screenDensity: Number(device["Screen.Density"]),
145
+ screenWidth: Number(device["Screen.Width"]),
146
+ screenHeight: Number(device["Screen.Height"]),
147
+ systemSupportedLocale: device["Locales"] as Array<string>,
148
+ deviceClass: 0,
149
+ deviceFeature: device["Features"].map((val) => ({ name: val, value: 0 })),
150
+ },
151
+ }),
152
+ Effect.flatMap(HttpClient.execute),
153
+ Effect.flatMap(HttpClientResponse.filterStatusOk),
154
+ Effect.flatMap(decodeResponseFromResponseWrapper("uploadDeviceConfigResponse"))
155
+ );
156
+
157
+ // const authResponse = yield* Function.pipe(
158
+ // HttpClientRequest.post("https://android.clients.google.com/auth"),
159
+ // HttpClientRequest.setHeaders({
160
+ // app: "com.google.android.gms",
161
+ // device: checkinResponse.androidId.toString(16),
162
+ // "User-Agent": `GoogleAuth/1.4 (${device["Build.DEVICE"]} ${device["Build.ID"]})`,
163
+ // }),
164
+ // HttpClientRequest.setUrlParams({
165
+ // app: "com.android.vending",
166
+ // oauth2_foreground: "1",
167
+ // Email: account.email,
168
+ // token_request_options: "CAA4AVAB",
169
+ // client_sig: "38918a453d07199354f8b19af05ec6562ced5788",
170
+ // Token: account.auth,
171
+ // google_play_services_version: `${device["GSF.version"]}`,
172
+ // check_email: "1",
173
+ // system_partition: "1",
174
+ // sdk_version: `${device["Build.VERSION.SDK_INT"]}`,
175
+ // callerPkg: "com.google.android.gms",
176
+ // device_country: "IN",
177
+ // lang: "en",
178
+ // androidId: checkinResponse.androidId.toString(16),
179
+ // callerSig: "38918a453d07199354f8b19af05ec6562ced5788",
180
+ // service: "oauth2:https://www.googleapis.com/auth/googleplay",
181
+ // }),
182
+ // HttpClient.execute,
183
+ // Effect.flatMap(HttpClientResponse.filterStatusOk),
184
+ // Effect.flatMap((response) => response.text)
185
+ // );
186
+
187
+ return Layer.function(
188
+ HttpClient.HttpClient,
189
+ HttpClient.HttpClient,
190
+ Function.flow(
191
+ HttpClient.filterStatusOk,
192
+ HttpClient.mapRequest(
193
+ HttpClientRequest.updateUrl((url) =>
194
+ url.startsWith("/fdfe/") ? `https://android.clients.google.com${url}` : url
195
+ )
196
+ ),
197
+ HttpClient.mapRequest(
198
+ HttpClientRequest.setHeaders({
199
+ Authorization: "Bearer " + account.auth,
200
+ "User-Agent": device.userAgent,
201
+ "X-DFE-Device-Id": checkinResponse.androidId.toString(16),
202
+ "Accept-Language": "en",
203
+ "X-DFE-Encoded-Targets":
204
+ "CAESN/qigQYC2AMBFfUbyA7SM5Ij/CvfBoIDgxHqGP8R3xzIBvoQtBKFDZ4HAY4FrwSVMasHBO0O2Q8akgYRAQECAQO7AQEpKZ0CnwECAwRrAQYBr9PPAoK7sQMBAQMCBAkIDAgBAwEDBAICBAUZEgMEBAMLAQEBBQEBAcYBARYED+cBfS8CHQEKkAEMMxcBIQoUDwYHIjd3DQ4MFk0JWGYZEREYAQOLAYEBFDMIEYMBAgICAgICOxkCD18LGQKEAcgDBIQBAgGLARkYCy8oBTJlBCUocxQn0QUBDkkGxgNZQq0BZSbeAmIDgAEBOgGtAaMCDAOQAZ4BBIEBKUtQUYYBQscDDxPSARA1oAEHAWmnAsMB2wFyywGLAxol+wImlwOOA80CtwN26A0WjwJVbQEJPAH+BRDeAfkHK/ABASEBCSAaHQemAzkaRiu2Ad8BdXeiAwEBGBUBBN4LEIABK4gB2AFLfwECAdoENq0CkQGMBsIBiQEtiwGgA1zyAUQ4uwS8AwhsvgPyAcEDF27vApsBHaICGhl3GSKxAR8MC6cBAgItmQYG9QIeywLvAeYBDArLAh8HASI4ELICDVmVBgsY/gHWARtcAsMBpALiAdsBA7QBpAJmIArpByn0AyAKBwHTARIHAX8D+AMBcRIBBbEDmwUBMacCHAciNp0BAQF0OgQLJDuSAh54kwFSP0eeAQQ4M5EBQgMEmwFXywFo0gFyWwMcapQBBugBPUW2AVgBKmy3AR6PAbMBGQxrUJECvQR+8gFoWDsYgQNwRSczBRXQAgtRswEW0ALMAREYAUEBIG6yATYCRE8OxgER8gMBvQEDRkwLc8MBTwHZAUOnAXiiBakDIbYBNNcCIUmuArIBSakBrgFHKs0EgwV/G3AD0wE6LgECtQJ4xQFwFbUCjQPkBS6vAQqEAUZF3QIM9wEhCoYCQhXsBCyZArQDugIziALWAdIBlQHwBdUErQE6qQaSA4EEIvYBHir9AQVLmgMCApsCKAwHuwgrENsBAjNYswEVmgIt7QJnN4wDEnta+wGfAcUBxgEtEFXQAQWdAUAeBcwBAQM7rAEJATJ0LENrdh73A6UBhAE+qwEeASxLZUMhDREuH0CGARbd7K0GlQo",
205
+ "X-DFE-Phenotype":
206
+ "H4sIAAAAAAAAAB3OO3KjMAAA0KRNuWXukBkBQkAJ2MhgAZb5u2GCwQZbCH_EJ77QHmgvtDtbv-Z9_H63zXXU0NVPB1odlyGy7751Q3CitlPDvFd8lxhz3tpNmz7P92CFw73zdHU2Ie0Ad2kmR8lxhiErTFLt3RPGfJQHSDy7Clw10bg8kqf2owLokN4SecJTLoSwBnzQSd652_MOf2d1vKBNVedzg4ciPoLz2mQ8efGAgYeLou-l-PXn_7Sna1MfhHuySxt-4esulEDp8Sbq54CPPKjpANW-lkU2IZ0F92LBI-ukCKSptqeq1eXU96LD9nZfhKHdtjSWwJqUm_2r6pMHOxk01saVanmNopjX3YxQafC4iC6T55aRbC8nTI98AF_kItIQAJb5EQxnKTO7TZDWnr01HVPxelb9A2OWX6poidMWl16K54kcu_jhXw-JSBQkVcD_fPsLSZu6joIBAAA",
207
+ "X-DFE-Client-Id": "am-android-google",
208
+ "X-DFE-Network-Type": "4",
209
+ "X-DFE-Content-Filters": "",
210
+ "X-Limit-Ad-Tracking-Enabled": "false",
211
+ "X-Ad-Id": "",
212
+ "X-DFE-UserLanguages": "en",
213
+ "X-DFE-Request-Params": "timeoutMs=4000",
214
+ "X-DFE-Device-Checkin-Consistency-Token": checkinResponse.deviceCheckinConsistencyToken,
215
+ "X-DFE-Device-Config-Token": uploadDeviceConfigResponse.uploadDeviceConfigToken,
216
+ })
217
+ )
218
+ )
219
+ );
220
+ }, Layer.unwrapEffect);
@@ -0,0 +1,96 @@
1
+ import type * as PlatformError from "@effect/platform/Error";
2
+ import type * as ParseResult from "effect/ParseResult";
3
+
4
+ import * as FileSystem from "@effect/platform/FileSystem";
5
+ import * as Effect from "effect/Effect";
6
+ import * as Schema from "effect/Schema";
7
+ import * as PropertiesFile from "properties-file";
8
+
9
+ /** @internal */
10
+ export class Device extends Schema.Class<Device>("Device")({
11
+ UserReadableName: Schema.String,
12
+ "Build.BOOTLOADER": Schema.String,
13
+ "Build.BRAND": Schema.String,
14
+ "Build.DEVICE": Schema.String,
15
+ "Build.FINGERPRINT": Schema.String,
16
+ "Build.HARDWARE": Schema.String,
17
+ "Build.ID": Schema.String,
18
+ "Build.MANUFACTURER": Schema.String,
19
+ "Build.MODEL": Schema.String,
20
+ "Build.PRODUCT": Schema.String,
21
+ "Build.RADIO": Schema.String,
22
+ "Build.VERSION.RELEASE": Schema.String,
23
+ "Build.VERSION.SDK_INT": Schema.NumberFromString,
24
+ CellOperator: Schema.String,
25
+ Client: Schema.String,
26
+ Features: Schema.transform(Schema.String, Schema.Array(Schema.String), {
27
+ encode: (features) => features.join(","),
28
+ decode: (str) => str.split(","),
29
+ }),
30
+ "GL.Extensions": Schema.transform(Schema.String, Schema.Array(Schema.String), {
31
+ encode: (extensions) => extensions.join(","),
32
+ decode: (str) => str.split(","),
33
+ }),
34
+ "GL.Version": Schema.NumberFromString,
35
+ "GSF.version": Schema.NumberFromString,
36
+ HasFiveWayNavigation: Schema.BooleanFromString,
37
+ HasHardKeyboard: Schema.BooleanFromString,
38
+ Keyboard: Schema.NumberFromString,
39
+ Locales: Schema.transform(Schema.String, Schema.Array(Schema.String), {
40
+ encode: (locales) => locales.join(","),
41
+ decode: (str) => str.split(","),
42
+ }),
43
+ Navigation: Schema.NumberFromString,
44
+ Platforms: Schema.transform(Schema.String, Schema.Array(Schema.String), {
45
+ encode: (libs) => libs.join(","),
46
+ decode: (str) => str.split(","),
47
+ }),
48
+ Roaming: Schema.String,
49
+ "Screen.Density": Schema.NumberFromString,
50
+ "Screen.Height": Schema.NumberFromString,
51
+ "Screen.Width": Schema.NumberFromString,
52
+ ScreenLayout: Schema.NumberFromString,
53
+ SharedLibraries: Schema.transform(Schema.String, Schema.Array(Schema.String), {
54
+ encode: (libs) => libs.join(","),
55
+ decode: (str) => str.split(","),
56
+ }),
57
+ SimCountry: Schema.String.pipe(Schema.optional),
58
+ SimOperator: Schema.String.pipe(Schema.optional),
59
+ TimeZone: Schema.String,
60
+ TouchScreen: Schema.NumberFromString,
61
+ "Vending.version": Schema.NumberFromString,
62
+ "Vending.versionString": Schema.String,
63
+ }) {
64
+ public static fromPropertiesFile = (
65
+ path: string
66
+ ): Effect.Effect<Device, PlatformError.PlatformError | ParseResult.ParseError, FileSystem.FileSystem> =>
67
+ Effect.gen(function* () {
68
+ const fileSystem = yield* FileSystem.FileSystem;
69
+ const content = yield* fileSystem.readFileString(path);
70
+ const properties = PropertiesFile.getProperties(content);
71
+ const device = yield* Schema.decodeUnknown(Device)(properties);
72
+ return device;
73
+ });
74
+
75
+ public get userAgent(): string {
76
+ const deviceProperties = {
77
+ api: 3,
78
+ versionCode: this["Vending.version"],
79
+ sdk: this["Build.VERSION.SDK_INT"],
80
+ device: this["Build.DEVICE"],
81
+ hardware: this["Build.HARDWARE"],
82
+ product: this["Build.PRODUCT"],
83
+ platformVersionRelease: this["Build.VERSION.RELEASE"],
84
+ model: this["Build.MODEL"],
85
+ buildId: this["Build.ID"],
86
+ isWideScreen: 0,
87
+ supportedAbis: this["Platforms"].join(";"),
88
+ };
89
+
90
+ const devicePropertiesString = Object.entries(deviceProperties)
91
+ .map(([k, v]) => `${k}=${v}`)
92
+ .join(",");
93
+
94
+ return `Android-Finsky/${this["Vending.versionString"]} (${devicePropertiesString})`;
95
+ }
96
+ }
@@ -0,0 +1,194 @@
1
+ import * as Protobuf from "@bufbuild/protobuf";
2
+ import * as HttpClientError from "@effect/platform/HttpClientError";
3
+ import * as HttpClientRequest from "@effect/platform/HttpClientRequest";
4
+ import * as HttpClientResponse from "@effect/platform/HttpClientResponse";
5
+ import * as Effect from "effect/Effect";
6
+ import * as Function from "effect/Function";
7
+ import * as Predicate from "effect/Predicate";
8
+
9
+ import { type PayloadSchema, ResponseWrapperSchema } from "../generated/GooglePlay_pb.js";
10
+
11
+ /** @internal */
12
+ export const encodeRequest = Function.dual<
13
+ // Data-last signature
14
+ <Description extends Protobuf.DescMessage>(
15
+ description: Description,
16
+ body: Protobuf.MessageInitShape<Description>,
17
+ options?: Partial<Protobuf.BinaryWriteOptions> | undefined
18
+ ) => (
19
+ request: HttpClientRequest.HttpClientRequest
20
+ ) => Effect.Effect<HttpClientRequest.HttpClientRequest, HttpClientError.HttpClientError, never>,
21
+ // Data-first signature
22
+ <Description extends Protobuf.DescMessage>(
23
+ request: HttpClientRequest.HttpClientRequest,
24
+ description: Description,
25
+ body: Protobuf.MessageInitShape<Description>,
26
+ options?: Partial<Protobuf.BinaryWriteOptions> | undefined
27
+ ) => Effect.Effect<HttpClientRequest.HttpClientRequest, HttpClientError.HttpClientError, never>
28
+ >(
29
+ // Data first if the first argument is an http request
30
+ (arguments_) => Predicate.hasProperty(arguments_[0], HttpClientRequest.TypeId),
31
+
32
+ // Body implementation
33
+ Effect.fn("encodeRequest")(function* <Description extends Protobuf.DescMessage>(
34
+ request: HttpClientRequest.HttpClientRequest,
35
+ description: Description,
36
+ body: Protobuf.MessageInitShape<Description>,
37
+ options?: Partial<Protobuf.BinaryWriteOptions> | undefined
38
+ ) {
39
+ const messageType = description.name;
40
+ yield* Effect.annotateCurrentSpan("messageType", messageType);
41
+
42
+ const bytes = yield* Effect.try({
43
+ try: () =>
44
+ Protobuf.toBinary(
45
+ description,
46
+ Protobuf.create(description, body),
47
+ options ?? { writeUnknownFields: true }
48
+ ),
49
+ catch: (cause) =>
50
+ new HttpClientError.RequestError({
51
+ cause,
52
+ request,
53
+ reason: "Encode",
54
+ description: `Could not encode message of type ${messageType}`,
55
+ }),
56
+ });
57
+
58
+ const applyBody = HttpClientRequest.bodyUint8Array(bytes, "application/x-protobuf");
59
+ return applyBody(request);
60
+ })
61
+ );
62
+
63
+ /** @internal */
64
+ export const decodeResponse = Function.dual<
65
+ // Data-last signature
66
+ <Desc extends Protobuf.DescMessage>(
67
+ protobufDescription: Desc,
68
+ options?: Partial<Protobuf.BinaryReadOptions> | undefined
69
+ ) => (
70
+ response: HttpClientResponse.HttpClientResponse
71
+ ) => Effect.Effect<Exclude<Protobuf.MessageShape<Desc>, undefined>, HttpClientError.HttpClientError, never>,
72
+ // Data-first signature
73
+ <Desc extends Protobuf.DescMessage>(
74
+ response: HttpClientResponse.HttpClientResponse,
75
+ protobufDescription: Desc,
76
+ options?: Partial<Protobuf.BinaryReadOptions> | undefined
77
+ ) => Effect.Effect<Exclude<Protobuf.MessageShape<Desc>, undefined>, HttpClientError.HttpClientError, never>
78
+ >(
79
+ // Data first if the first argument is an http response
80
+ (arguments_) => Predicate.hasProperty(arguments_[0], HttpClientResponse.TypeId),
81
+
82
+ // Body implementation
83
+ Effect.fn("decodeResponse")(function* <Desc extends Protobuf.DescMessage>(
84
+ response: HttpClientResponse.HttpClientResponse,
85
+ protobufDescription: Desc,
86
+ options?: Partial<Protobuf.BinaryReadOptions> | undefined
87
+ ) {
88
+ const messageType = protobufDescription.name;
89
+ yield* Effect.annotateCurrentSpan("messageType", messageType);
90
+ const arrayBuffer = yield* response.arrayBuffer;
91
+
92
+ const message = yield* Effect.try({
93
+ try: () =>
94
+ Protobuf.fromBinary(
95
+ protobufDescription,
96
+ new Uint8Array(arrayBuffer),
97
+ options ?? { readUnknownFields: true }
98
+ ),
99
+ catch: (cause) =>
100
+ new HttpClientError.ResponseError({
101
+ cause,
102
+ response,
103
+ reason: "Decode",
104
+ request: response.request,
105
+ description: `Could not decode message of type ${messageType}`,
106
+ }),
107
+ });
108
+
109
+ if (Predicate.isUndefined(message)) {
110
+ return yield* new HttpClientError.ResponseError({
111
+ response,
112
+ reason: "Decode",
113
+ request: response.request,
114
+ description: `Message of type ${messageType} is missing`,
115
+ });
116
+ }
117
+
118
+ return message as Exclude<Protobuf.MessageShape<Desc>, undefined>;
119
+ })
120
+ );
121
+
122
+ /** @internal */
123
+ export const decodeResponseFromResponseWrapper = Function.dual<
124
+ // Data-last signature
125
+ <
126
+ ExtractPayload extends Exclude<
127
+ keyof Protobuf.MessageShape<typeof PayloadSchema>,
128
+ keyof Protobuf.Message<"Payload">
129
+ >,
130
+ >(
131
+ extractPayload: ExtractPayload,
132
+ options?: Partial<Protobuf.BinaryReadOptions> | undefined
133
+ ) => (
134
+ response: HttpClientResponse.HttpClientResponse
135
+ ) => Effect.Effect<
136
+ Exclude<Protobuf.MessageShape<typeof PayloadSchema>[ExtractPayload], undefined>,
137
+ HttpClientError.HttpClientError,
138
+ never
139
+ >,
140
+ // Data-first signature
141
+ <
142
+ ExtractPayload extends Exclude<
143
+ keyof Protobuf.MessageShape<typeof PayloadSchema>,
144
+ keyof Protobuf.Message<"Payload">
145
+ >,
146
+ >(
147
+ response: HttpClientResponse.HttpClientResponse,
148
+ extractPayload: ExtractPayload,
149
+ options?: Partial<Protobuf.BinaryReadOptions> | undefined
150
+ ) => Effect.Effect<
151
+ Exclude<Protobuf.MessageShape<typeof PayloadSchema>[ExtractPayload], undefined>,
152
+ HttpClientError.HttpClientError,
153
+ never
154
+ >
155
+ >(
156
+ // Data first if the first argument is an http response
157
+ (arguments_) => Predicate.hasProperty(arguments_[0], HttpClientResponse.TypeId),
158
+
159
+ // Body implementation
160
+ Effect.fn("decodeResponse")(function* <
161
+ ExtractPayload extends Exclude<
162
+ keyof Protobuf.MessageShape<typeof PayloadSchema>,
163
+ keyof Protobuf.Message<"Payload">
164
+ >,
165
+ >(
166
+ response: HttpClientResponse.HttpClientResponse,
167
+ extractPayload: ExtractPayload,
168
+ options?: Partial<Protobuf.BinaryReadOptions> | undefined
169
+ ) {
170
+ type Out = Exclude<Protobuf.MessageShape<typeof PayloadSchema>[ExtractPayload], undefined>;
171
+ const responseWrapper = yield* decodeResponse(response, ResponseWrapperSchema, options);
172
+
173
+ if (Predicate.isNotUndefined(responseWrapper.commands?.displayErrorMessage)) {
174
+ return yield* new HttpClientError.ResponseError({
175
+ response,
176
+ reason: "Decode",
177
+ request: response.request,
178
+ description: responseWrapper.commands.displayErrorMessage,
179
+ });
180
+ }
181
+
182
+ const extracted = responseWrapper.payload?.[extractPayload];
183
+ if (Predicate.isUndefined(extracted)) {
184
+ return yield* new HttpClientError.ResponseError({
185
+ response,
186
+ reason: "Decode",
187
+ request: response.request,
188
+ description: `ResponseWrapper has no payload of type ${extractPayload}`,
189
+ });
190
+ }
191
+
192
+ return extracted as Out;
193
+ })
194
+ );