@tinyburg/bitcity-sdk 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +8 -2
- package/dist/BitCity.d.ts +83 -0
- package/dist/BitCity.d.ts.map +1 -0
- package/dist/BitCity.js +172 -0
- package/dist/BitCity.js.map +1 -0
- package/dist/Endpoints.d.ts +177 -0
- package/dist/Endpoints.d.ts.map +1 -0
- package/dist/Endpoints.js +259 -0
- package/dist/Endpoints.js.map +1 -0
- package/dist/Scopes.d.ts +70 -0
- package/dist/Scopes.d.ts.map +1 -0
- package/dist/Scopes.js +79 -0
- package/dist/Scopes.js.map +1 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -3
- package/src/BitCity.ts +186 -0
- package/src/Endpoints.ts +287 -0
- package/src/Scopes.ts +88 -0
- package/src/index.ts +58 -0
package/README.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @tinyburg/bitcity-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The Bit City api, for the authproxy.
|
|
4
|
+
|
|
5
|
+
Bit City speaks Nimblebit's NBSync protocol under the game code `bc`. Requests
|
|
6
|
+
are signed with `NimblebitAuth` from `@tinyburg/nimblebit-sdk`, using the
|
|
7
|
+
game's own secret salt, and the endpoints carry the `bitcity` scope tree
|
|
8
|
+
built by `defineGame`. Game-specific save and meta payloads are still
|
|
9
|
+
`Schema.Unknown`.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bit City SDK: signed calls against Nimblebit's NBSync service.
|
|
3
|
+
*
|
|
4
|
+
* Signing reuses `@tinyburg/nimblebit-sdk` (`NimblebitAuth`): the game secret
|
|
5
|
+
* salt (from `NBSync._ss`) is provided as the auth key, and each request is
|
|
6
|
+
* `md5(<preimage> + <secret salt>)` with a random uint32 `salt` in the path.
|
|
7
|
+
* All method preimages below are the Tiny Tower formula with the `bc` game code.
|
|
8
|
+
*
|
|
9
|
+
* The methods that were exercised live against sync.nimblebit.com are marked
|
|
10
|
+
* VERIFIED; save-bearing / write paths follow the same pattern but were not
|
|
11
|
+
* exercised. Save bytes are returned raw (base64-decoded); inflating (pako) and
|
|
12
|
+
* parsing into a save schema is left to the caller.
|
|
13
|
+
*
|
|
14
|
+
* @since 1.0.0
|
|
15
|
+
* @category SDK
|
|
16
|
+
*/
|
|
17
|
+
import type * as Schema from "effect/Schema";
|
|
18
|
+
import * as Effect from "effect/Effect";
|
|
19
|
+
import { HttpClient } from "effect/http";
|
|
20
|
+
import * as Redacted from "effect/Redacted";
|
|
21
|
+
import { NimblebitAuth } from "@tinyburg/nimblebit-sdk";
|
|
22
|
+
/**
|
|
23
|
+
* Requests a new player ("burnbot") from the Nimblebit servers. VERIFIED.
|
|
24
|
+
*
|
|
25
|
+
* @since 1.0.0
|
|
26
|
+
* @category SDK
|
|
27
|
+
*/
|
|
28
|
+
export declare const device_newPlayer: Effect.Effect<{
|
|
29
|
+
readonly playerId: string & import("effect/Brand").Brand<"PlayerId">;
|
|
30
|
+
readonly playerSs: Redacted.Redacted<string> & import("effect/Brand").Brand<"PlayerAuthKey">;
|
|
31
|
+
}, import("effect/http-api/HttpApiError").BadRequest | import("effect/http-api/HttpApiError").Forbidden | import("effect/http/HttpClientError").HttpClientError | import("effect/http-api/HttpApiError").InternalServerError | import("@tinyburg/nimblebit-sdk/internal/Error").NimblebitError | import("effect/PlatformError").PlatformError | Schema.SchemaError | import("effect/http-api/HttpApiError").Unauthorized, HttpClient.HttpClient | NimblebitAuth.NimblebitAuth>;
|
|
32
|
+
/**
|
|
33
|
+
* Retrieves player details. VERIFIED.
|
|
34
|
+
*
|
|
35
|
+
* @since 1.0.0
|
|
36
|
+
* @category SDK
|
|
37
|
+
*/
|
|
38
|
+
export declare const device_playerDetails: (args_0: {
|
|
39
|
+
readonly playerAuthKey: Redacted.Redacted<string> & import("effect/Brand").Brand<"PlayerAuthKey">;
|
|
40
|
+
readonly playerId: string & import("effect/Brand").Brand<"PlayerId">;
|
|
41
|
+
}) => Effect.Effect<{
|
|
42
|
+
readonly playerId: string & import("effect/Brand").Brand<"PlayerId">;
|
|
43
|
+
readonly playerEmail: Redacted.Redacted<string> & import("effect/Brand").Brand<"PlayerEmail">;
|
|
44
|
+
readonly registered: boolean;
|
|
45
|
+
readonly blacklisted: boolean;
|
|
46
|
+
readonly saveVersion: number;
|
|
47
|
+
readonly checksum: string;
|
|
48
|
+
}, import("effect/http-api/HttpApiError").BadRequest | import("effect/http-api/HttpApiError").Forbidden | import("effect/http/HttpClientError").HttpClientError | import("effect/http-api/HttpApiError").InternalServerError | import("@tinyburg/nimblebit-sdk/internal/Error").NimblebitError | import("effect/PlatformError").PlatformError | Schema.SchemaError | import("effect/http-api/HttpApiError").Unauthorized, HttpClient.HttpClient | NimblebitAuth.NimblebitAuth>;
|
|
49
|
+
/**
|
|
50
|
+
* Pulls the player's cloud save. Returns raw base64-decoded bytes (inflate +
|
|
51
|
+
* parse separately). Success envelope DERIVED from Tiny Tower.
|
|
52
|
+
*
|
|
53
|
+
* @since 1.0.0
|
|
54
|
+
* @category SDK
|
|
55
|
+
*/
|
|
56
|
+
export declare const sync_pullSave: (args_0: {
|
|
57
|
+
readonly playerAuthKey: Redacted.Redacted<string> & import("effect/Brand").Brand<"PlayerAuthKey">;
|
|
58
|
+
readonly playerId: string & import("effect/Brand").Brand<"PlayerId">;
|
|
59
|
+
}) => Effect.Effect<{
|
|
60
|
+
readonly success: "NotFound";
|
|
61
|
+
} | {
|
|
62
|
+
readonly success: "Found";
|
|
63
|
+
readonly data: Uint8Array<ArrayBufferLike>;
|
|
64
|
+
readonly checksum: string;
|
|
65
|
+
readonly saveId: number;
|
|
66
|
+
}, import("effect/http-api/HttpApiError").BadRequest | import("effect/http-api/HttpApiError").Forbidden | import("effect/http/HttpClientError").HttpClientError | import("effect/http-api/HttpApiError").InternalServerError | import("@tinyburg/nimblebit-sdk/internal/Error").NimblebitError | import("effect/PlatformError").PlatformError | Schema.SchemaError | import("effect/http-api/HttpApiError").Unauthorized, HttpClient.HttpClient | NimblebitAuth.NimblebitAuth>;
|
|
67
|
+
/**
|
|
68
|
+
* Lists items (parts/gifts) waiting for the player. VERIFIED
|
|
69
|
+
* (`/get_items/pt/...` -> `{ success:"Found", gifts:[...] }`, no total).
|
|
70
|
+
*
|
|
71
|
+
* @since 1.0.0
|
|
72
|
+
* @category SDK
|
|
73
|
+
*/
|
|
74
|
+
export declare const social_getItems: (args_0: {
|
|
75
|
+
readonly playerAuthKey: Redacted.Redacted<string> & import("effect/Brand").Brand<"PlayerAuthKey">;
|
|
76
|
+
readonly playerId: string & import("effect/Brand").Brand<"PlayerId">;
|
|
77
|
+
}) => Effect.Effect<{
|
|
78
|
+
readonly success: "NotFound";
|
|
79
|
+
} | {
|
|
80
|
+
readonly success: "Found";
|
|
81
|
+
readonly gifts: readonly unknown[];
|
|
82
|
+
}, import("effect/http-api/HttpApiError").BadRequest | import("effect/http-api/HttpApiError").Forbidden | import("effect/http/HttpClientError").HttpClientError | import("effect/http-api/HttpApiError").InternalServerError | import("@tinyburg/nimblebit-sdk/internal/Error").NimblebitError | import("effect/PlatformError").PlatformError | Schema.SchemaError | import("effect/http-api/HttpApiError").Unauthorized, HttpClient.HttpClient | NimblebitAuth.NimblebitAuth>;
|
|
83
|
+
//# sourceMappingURL=BitCity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BitCity.d.ts","sourceRoot":"","sources":["../src/BitCity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAC;AAE7C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,UAAU,EAAqB,MAAM,aAAa,CAAC;AAE5D,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAI5C,OAAO,EAAE,aAAa,EAAkB,MAAM,yBAAyB,CAAC;AAIxE;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB;;;8cA6B3B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;8cA+B/B,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;8cA+BxB,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;;;;;;8cA+B1B,CAAC"}
|
package/dist/BitCity.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bit City SDK: signed calls against Nimblebit's NBSync service.
|
|
3
|
+
*
|
|
4
|
+
* Signing reuses `@tinyburg/nimblebit-sdk` (`NimblebitAuth`): the game secret
|
|
5
|
+
* salt (from `NBSync._ss`) is provided as the auth key, and each request is
|
|
6
|
+
* `md5(<preimage> + <secret salt>)` with a random uint32 `salt` in the path.
|
|
7
|
+
* All method preimages below are the Tiny Tower formula with the `bc` game code.
|
|
8
|
+
*
|
|
9
|
+
* The methods that were exercised live against sync.nimblebit.com are marked
|
|
10
|
+
* VERIFIED; save-bearing / write paths follow the same pattern but were not
|
|
11
|
+
* exercised. Save bytes are returned raw (base64-decoded); inflating (pako) and
|
|
12
|
+
* parsing into a save schema is left to the caller.
|
|
13
|
+
*
|
|
14
|
+
* @since 1.0.0
|
|
15
|
+
* @category SDK
|
|
16
|
+
*/
|
|
17
|
+
import * as Effect from "effect/Effect";
|
|
18
|
+
import * as Function from "effect/Function";
|
|
19
|
+
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
20
|
+
import { HttpApiClient } from "effect/http-api";
|
|
21
|
+
import * as Redacted from "effect/Redacted";
|
|
22
|
+
import { NimblebitAuth, NimblebitError } from "@tinyburg/nimblebit-sdk";
|
|
23
|
+
import * as Endpoints from "./Endpoints.js";
|
|
24
|
+
/**
|
|
25
|
+
* Requests a new player ("burnbot") from the Nimblebit servers. VERIFIED.
|
|
26
|
+
*
|
|
27
|
+
* @since 1.0.0
|
|
28
|
+
* @category SDK
|
|
29
|
+
*/
|
|
30
|
+
export const device_newPlayer = /*#__PURE__*/Effect.gen(function* () {
|
|
31
|
+
const nimblebitAuth = yield* NimblebitAuth.NimblebitAuth;
|
|
32
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
33
|
+
const maybeAddBearerToken = nimblebitAuth.host !== "https://sync.nimblebit.com" ? HttpClientRequest.bearerToken(nimblebitAuth.authKey) : Function.identity;
|
|
34
|
+
const endpoint = yield* HttpApiClient.endpoint(Endpoints.Api, {
|
|
35
|
+
baseUrl: nimblebitAuth.host,
|
|
36
|
+
group: "DeviceManagementGroup",
|
|
37
|
+
endpoint: "DeviceNewPlayer",
|
|
38
|
+
httpClient: HttpClient.mapRequest(httpClient, maybeAddBearerToken)
|
|
39
|
+
});
|
|
40
|
+
const salt1 = yield* nimblebitAuth.salt;
|
|
41
|
+
const salt2 = yield* nimblebitAuth.salt;
|
|
42
|
+
const hash = yield* nimblebitAuth.sign(`bc/${salt1}/${salt2}`);
|
|
43
|
+
const response = yield* endpoint({
|
|
44
|
+
params: {
|
|
45
|
+
salt1,
|
|
46
|
+
salt2,
|
|
47
|
+
hash
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
if ("error" in response) {
|
|
51
|
+
return yield* new NimblebitError.NimblebitError({
|
|
52
|
+
method: "DeviceNewPlayer",
|
|
53
|
+
module: "DeviceManagementGroup",
|
|
54
|
+
cause: response.error
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return response;
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* Retrieves player details. VERIFIED.
|
|
61
|
+
*
|
|
62
|
+
* @since 1.0.0
|
|
63
|
+
* @category SDK
|
|
64
|
+
*/
|
|
65
|
+
export const device_playerDetails = /*#__PURE__*/Effect.fn("device_playerDetails")(function* ({
|
|
66
|
+
playerAuthKey,
|
|
67
|
+
playerId
|
|
68
|
+
}) {
|
|
69
|
+
const nimblebitAuth = yield* NimblebitAuth.NimblebitAuth;
|
|
70
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
71
|
+
const maybeAddBearerToken = nimblebitAuth.host !== "https://sync.nimblebit.com" ? HttpClientRequest.bearerToken(nimblebitAuth.authKey) : Function.identity;
|
|
72
|
+
const endpoint = yield* HttpApiClient.endpoint(Endpoints.Api, {
|
|
73
|
+
baseUrl: nimblebitAuth.host,
|
|
74
|
+
group: "DeviceManagementGroup",
|
|
75
|
+
endpoint: "DevicePlayerDetails",
|
|
76
|
+
httpClient: HttpClient.mapRequest(httpClient, maybeAddBearerToken)
|
|
77
|
+
});
|
|
78
|
+
const salt = yield* nimblebitAuth.salt;
|
|
79
|
+
const hash = yield* nimblebitAuth.sign(`bc/${playerId}/${salt}${Redacted.value(playerAuthKey)}`);
|
|
80
|
+
const response = yield* endpoint({
|
|
81
|
+
params: {
|
|
82
|
+
playerId,
|
|
83
|
+
salt,
|
|
84
|
+
hash
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
if ("error" in response) {
|
|
88
|
+
return yield* new NimblebitError.NimblebitError({
|
|
89
|
+
method: "DevicePlayerDetails",
|
|
90
|
+
module: "DeviceManagementGroup",
|
|
91
|
+
cause: response.error
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return response.player;
|
|
95
|
+
});
|
|
96
|
+
/**
|
|
97
|
+
* Pulls the player's cloud save. Returns raw base64-decoded bytes (inflate +
|
|
98
|
+
* parse separately). Success envelope DERIVED from Tiny Tower.
|
|
99
|
+
*
|
|
100
|
+
* @since 1.0.0
|
|
101
|
+
* @category SDK
|
|
102
|
+
*/
|
|
103
|
+
export const sync_pullSave = /*#__PURE__*/Effect.fn("sync_pullSave")(function* ({
|
|
104
|
+
playerAuthKey,
|
|
105
|
+
playerId
|
|
106
|
+
}) {
|
|
107
|
+
const nimblebitAuth = yield* NimblebitAuth.NimblebitAuth;
|
|
108
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
109
|
+
const maybeAddBearerToken = nimblebitAuth.host !== "https://sync.nimblebit.com" ? HttpClientRequest.bearerToken(nimblebitAuth.authKey) : Function.identity;
|
|
110
|
+
const endpoint = yield* HttpApiClient.endpoint(Endpoints.Api, {
|
|
111
|
+
baseUrl: nimblebitAuth.host,
|
|
112
|
+
group: "SyncManagementGroup",
|
|
113
|
+
endpoint: "SyncPullSave",
|
|
114
|
+
httpClient: HttpClient.mapRequest(httpClient, maybeAddBearerToken)
|
|
115
|
+
});
|
|
116
|
+
const salt = yield* nimblebitAuth.salt;
|
|
117
|
+
const hash = yield* nimblebitAuth.sign(`bc/${playerId}/${salt}${Redacted.value(playerAuthKey)}`);
|
|
118
|
+
const response = yield* endpoint({
|
|
119
|
+
params: {
|
|
120
|
+
playerId,
|
|
121
|
+
salt,
|
|
122
|
+
hash
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
if ("error" in response) {
|
|
126
|
+
return yield* new NimblebitError.NimblebitError({
|
|
127
|
+
method: "SyncPullSave",
|
|
128
|
+
module: "SyncManagementGroup",
|
|
129
|
+
cause: response.error
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return response;
|
|
133
|
+
});
|
|
134
|
+
/**
|
|
135
|
+
* Lists items (parts/gifts) waiting for the player. VERIFIED
|
|
136
|
+
* (`/get_items/pt/...` -> `{ success:"Found", gifts:[...] }`, no total).
|
|
137
|
+
*
|
|
138
|
+
* @since 1.0.0
|
|
139
|
+
* @category SDK
|
|
140
|
+
*/
|
|
141
|
+
export const social_getItems = /*#__PURE__*/Effect.fn("social_getItems")(function* ({
|
|
142
|
+
playerAuthKey,
|
|
143
|
+
playerId
|
|
144
|
+
}) {
|
|
145
|
+
const nimblebitAuth = yield* NimblebitAuth.NimblebitAuth;
|
|
146
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
147
|
+
const maybeAddBearerToken = nimblebitAuth.host !== "https://sync.nimblebit.com" ? HttpClientRequest.bearerToken(nimblebitAuth.authKey) : Function.identity;
|
|
148
|
+
const endpoint = yield* HttpApiClient.endpoint(Endpoints.Api, {
|
|
149
|
+
baseUrl: nimblebitAuth.host,
|
|
150
|
+
group: "SocialGroup",
|
|
151
|
+
endpoint: "SocialGetItems",
|
|
152
|
+
httpClient: HttpClient.mapRequest(httpClient, maybeAddBearerToken)
|
|
153
|
+
});
|
|
154
|
+
const salt = yield* nimblebitAuth.salt;
|
|
155
|
+
const hash = yield* nimblebitAuth.sign(`bc/${playerId}/${salt}${Redacted.value(playerAuthKey)}`);
|
|
156
|
+
const response = yield* endpoint({
|
|
157
|
+
params: {
|
|
158
|
+
playerId,
|
|
159
|
+
salt,
|
|
160
|
+
hash
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
if ("error" in response) {
|
|
164
|
+
return yield* new NimblebitError.NimblebitError({
|
|
165
|
+
method: "SocialGetItems",
|
|
166
|
+
module: "SocialGroup",
|
|
167
|
+
cause: response.error
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return response;
|
|
171
|
+
});
|
|
172
|
+
//# sourceMappingURL=BitCity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BitCity.js","names":[],"sources":["../src/BitCity.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bit City (game code `bc`) NBSync api endpoints.
|
|
3
|
+
*
|
|
4
|
+
* Signing: a random `salt` (signed int32) goes in the path, and `hash` is
|
|
5
|
+
* `md5(<preimage> + <game secret salt>)` with the preimage
|
|
6
|
+
* `bc/<playerId>/<salt><playerAuthKey>` (variants noted per endpoint). The
|
|
7
|
+
* secret salt is the value extracted from `NBSync._ss`.
|
|
8
|
+
*
|
|
9
|
+
* Verification (live server + Frida DTO inspection of the game, 2026-08-26):
|
|
10
|
+
* - Signing is MD5, server-validated (same scheme as Tiny Tower / Pocket
|
|
11
|
+
* Planes; the game computes SHA-256 only for the unauthenticated `register`).
|
|
12
|
+
* - Response envelopes VERIFIED by reading the game's DTO classes at runtime:
|
|
13
|
+
* register, player_details, sync/pull (NBSyncGameSaveResponse), current_version,
|
|
14
|
+
* verify_device, friend/pull_game, friend/pull_meta (meta is a flat
|
|
15
|
+
* Dictionary<String,Object> on pt), send_item (NBSyncItemsSendResponse),
|
|
16
|
+
* get_items (NBSyncItemsGetResponse: success/gifts, NO `total` unlike pp).
|
|
17
|
+
* `get_items` was also exercised live with a burnbot.
|
|
18
|
+
* - Minimal NBSync endpoint set (Bit City is a lightweight city builder).
|
|
19
|
+
*
|
|
20
|
+
* Game-specific save/meta schemas are left as `Schema.Unknown` placeholders.
|
|
21
|
+
*
|
|
22
|
+
* @since 1.0.0
|
|
23
|
+
* @category Endpoints
|
|
24
|
+
*/
|
|
25
|
+
import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup } from "effect/http-api";
|
|
26
|
+
import * as Schema from "effect/Schema";
|
|
27
|
+
/**
|
|
28
|
+
* A gift/item in the general item channel (get_items / send_item). Verified DTO
|
|
29
|
+
* NBSyncItemsGift on pt: `{gift_id, gift_to, gift_from, gift_type, item_type,
|
|
30
|
+
* gift_str, h}` (no `c`, unlike pp). Left open for the data-schema work.
|
|
31
|
+
*
|
|
32
|
+
* @since 1.0.0
|
|
33
|
+
* @category Schemas
|
|
34
|
+
*/
|
|
35
|
+
export declare const Gift: Schema.Unknown;
|
|
36
|
+
/**
|
|
37
|
+
* The Bit City NBSync api.
|
|
38
|
+
*
|
|
39
|
+
* @since 1.0.0
|
|
40
|
+
* @category Api
|
|
41
|
+
*/
|
|
42
|
+
export declare const Api: HttpApi.HttpApi<"BitCityApi", HttpApiGroup.HttpApiGroup<"DeviceManagementGroup", HttpApiEndpoint.HttpApiEndpoint<"DeviceNewPlayer", "GET", "/register/bc/:salt1/:salt2/:hash", Schema.toCodecStringTree<Schema.Struct<{
|
|
43
|
+
salt1: Schema.Int;
|
|
44
|
+
salt2: Schema.Int;
|
|
45
|
+
hash: Schema.String;
|
|
46
|
+
}>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
47
|
+
readonly error: Schema.String;
|
|
48
|
+
}> | Schema.encodeKeys<Schema.Struct<{
|
|
49
|
+
readonly playerId: Schema.brand<Schema.String, "PlayerId">;
|
|
50
|
+
readonly playerSs: Schema.brand<Schema.RedactedFromValue<Schema.String>, "PlayerAuthKey">;
|
|
51
|
+
}>, {
|
|
52
|
+
readonly playerId: "player_id";
|
|
53
|
+
readonly playerSs: "player_ss";
|
|
54
|
+
}>>, Schema.toCodecJson<typeof HttpApiError.BadRequest | typeof HttpApiError.Unauthorized | typeof HttpApiError.Forbidden | typeof HttpApiError.InternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"DevicePlayerDetails", "GET", "/player_details/bc/:playerId/:salt/:hash", Schema.toCodecStringTree<Schema.Struct<{
|
|
55
|
+
playerId: Schema.brand<Schema.String, "PlayerId">;
|
|
56
|
+
salt: Schema.Int;
|
|
57
|
+
hash: Schema.String;
|
|
58
|
+
}>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
59
|
+
readonly error: Schema.String;
|
|
60
|
+
}> | Schema.Struct<{
|
|
61
|
+
readonly success: Schema.Literal<"Found">;
|
|
62
|
+
readonly player: Schema.encodeKeys<Schema.Struct<{
|
|
63
|
+
readonly playerId: Schema.brand<Schema.String, "PlayerId">;
|
|
64
|
+
readonly playerEmail: Schema.brand<Schema.RedactedFromValue<Schema.String>, "PlayerEmail">;
|
|
65
|
+
readonly registered: Schema.compose<Schema.BooleanFromBit, Schema.NumberFromString>;
|
|
66
|
+
readonly blacklisted: Schema.compose<Schema.BooleanFromBit, Schema.NumberFromString>;
|
|
67
|
+
readonly saveVersion: Schema.compose<Schema.Int, Schema.NumberFromString>;
|
|
68
|
+
readonly checksum: Schema.String;
|
|
69
|
+
}>, {
|
|
70
|
+
readonly playerId: "player_id";
|
|
71
|
+
readonly playerEmail: "email";
|
|
72
|
+
readonly checksum: "h";
|
|
73
|
+
}>;
|
|
74
|
+
}>>, Schema.toCodecJson<typeof HttpApiError.BadRequest | typeof HttpApiError.Unauthorized | typeof HttpApiError.Forbidden | typeof HttpApiError.InternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"DeviceVerifyDevice", "GET", "/verify_device/bc/:playerId/:verificationCode", Schema.toCodecStringTree<Schema.Struct<{
|
|
75
|
+
playerId: Schema.brand<Schema.String, "PlayerId">;
|
|
76
|
+
verificationCode: Schema.String;
|
|
77
|
+
}>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
78
|
+
readonly error: Schema.String;
|
|
79
|
+
}> | Schema.encodeKeys<Schema.Struct<{
|
|
80
|
+
readonly success: Schema.Literal<"NewDevice">;
|
|
81
|
+
readonly playerId: Schema.brand<Schema.String, "PlayerId">;
|
|
82
|
+
readonly playerAuthKey: Schema.brand<Schema.RedactedFromValue<Schema.String>, "PlayerAuthKey">;
|
|
83
|
+
readonly playerEmail: Schema.brand<Schema.RedactedFromValue<Schema.String>, "PlayerEmail">;
|
|
84
|
+
}>, {
|
|
85
|
+
readonly playerId: "player_id";
|
|
86
|
+
readonly playerAuthKey: "player_ss";
|
|
87
|
+
readonly playerEmail: "player_email";
|
|
88
|
+
}>>, Schema.toCodecJson<typeof HttpApiError.BadRequest | typeof HttpApiError.Unauthorized | typeof HttpApiError.Forbidden | typeof HttpApiError.InternalServerError>, never, never>, false> | HttpApiGroup.HttpApiGroup<"SocialGroup", HttpApiEndpoint.HttpApiEndpoint<"SocialGetItems", "GET", "/get_items/bc/:playerId/:salt/:hash", Schema.toCodecStringTree<Schema.Struct<{
|
|
89
|
+
playerId: Schema.brand<Schema.String, "PlayerId">;
|
|
90
|
+
salt: Schema.Int;
|
|
91
|
+
hash: Schema.String;
|
|
92
|
+
}>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
93
|
+
readonly error: Schema.String;
|
|
94
|
+
}> | Schema.Struct<{
|
|
95
|
+
readonly success: Schema.Literal<"NotFound">;
|
|
96
|
+
}> | Schema.Struct<{
|
|
97
|
+
readonly success: Schema.Literal<"Found">;
|
|
98
|
+
readonly gifts: Schema.$Array<Schema.Unknown>;
|
|
99
|
+
}>>, Schema.toCodecJson<typeof HttpApiError.BadRequest | typeof HttpApiError.Unauthorized | typeof HttpApiError.Forbidden | typeof HttpApiError.InternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"SocialPullFriendGame", "GET", "/friend/pull_game/bc/:playerId/:friendId/:salt/:hash", Schema.toCodecStringTree<Schema.Struct<{
|
|
100
|
+
playerId: Schema.brand<Schema.String, "PlayerId">;
|
|
101
|
+
friendId: Schema.brand<Schema.String, "PlayerId">;
|
|
102
|
+
salt: Schema.Int;
|
|
103
|
+
hash: Schema.String;
|
|
104
|
+
}>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
105
|
+
readonly error: Schema.String;
|
|
106
|
+
}> | Schema.Struct<{
|
|
107
|
+
readonly success: Schema.Literal<"NotFound">;
|
|
108
|
+
}> | Schema.encodeKeys<Schema.Struct<{
|
|
109
|
+
readonly success: Schema.Literal<"Found">;
|
|
110
|
+
readonly data: Schema.Uint8ArrayFromBase64;
|
|
111
|
+
readonly checksum: Schema.String;
|
|
112
|
+
readonly saveId: Schema.NumberFromString;
|
|
113
|
+
readonly playerId: Schema.brand<Schema.String, "PlayerId">;
|
|
114
|
+
}>, {
|
|
115
|
+
readonly checksum: "h";
|
|
116
|
+
readonly saveId: "id";
|
|
117
|
+
readonly playerId: "player_id";
|
|
118
|
+
}>>, Schema.toCodecJson<typeof HttpApiError.BadRequest | typeof HttpApiError.Unauthorized | typeof HttpApiError.Forbidden | typeof HttpApiError.InternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"SocialPullFriendMeta", "POST", "/friend/pull_meta/bc/:playerId/:salt/:hash", Schema.toCodecStringTree<Schema.Struct<{
|
|
119
|
+
playerId: Schema.brand<Schema.String, "PlayerId">;
|
|
120
|
+
salt: Schema.Int;
|
|
121
|
+
hash: Schema.String;
|
|
122
|
+
}>>, never, Schema.toCodecJson<Schema.Struct<{
|
|
123
|
+
readonly friends: Schema.brand<Schema.String, "PlayerId">;
|
|
124
|
+
}>>, never, Schema.toCodecJson<Schema.Struct<{
|
|
125
|
+
readonly error: Schema.String;
|
|
126
|
+
}> | Schema.Struct<{
|
|
127
|
+
readonly success: Schema.Literal<"NotFound">;
|
|
128
|
+
}> | Schema.Struct<{
|
|
129
|
+
readonly success: Schema.Literal<"Found">;
|
|
130
|
+
readonly meta: Schema.$Record<Schema.brand<Schema.String, "PlayerId">, Schema.Unknown>;
|
|
131
|
+
}>>, Schema.toCodecJson<typeof HttpApiError.BadRequest | typeof HttpApiError.Unauthorized | typeof HttpApiError.Forbidden | typeof HttpApiError.InternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"SocialSendItem", "POST", "/send_item/bc/:itemType/:playerId/:friendId/:salt/:hash", Schema.toCodecStringTree<Schema.Struct<{
|
|
132
|
+
itemType: Schema.String;
|
|
133
|
+
playerId: Schema.brand<Schema.String, "PlayerId">;
|
|
134
|
+
friendId: Schema.brand<Schema.String, "PlayerId">;
|
|
135
|
+
salt: Schema.Int;
|
|
136
|
+
hash: Schema.String;
|
|
137
|
+
}>>, never, Schema.toCodecJson<Schema.Struct<{
|
|
138
|
+
readonly itemStr: Schema.String;
|
|
139
|
+
}>>, never, Schema.toCodecJson<Schema.Struct<{
|
|
140
|
+
readonly error: Schema.String;
|
|
141
|
+
}> | Schema.Struct<{
|
|
142
|
+
readonly success: Schema.Literal<"Sent">;
|
|
143
|
+
}> | Schema.Struct<{
|
|
144
|
+
readonly success: Schema.Literal<"NotSent">;
|
|
145
|
+
}>>, Schema.toCodecJson<typeof HttpApiError.BadRequest | typeof HttpApiError.Unauthorized | typeof HttpApiError.Forbidden | typeof HttpApiError.InternalServerError>, never, never>, false> | HttpApiGroup.HttpApiGroup<"SyncManagementGroup", HttpApiEndpoint.HttpApiEndpoint<"SyncCheckForNewerSave", "GET", "/sync/current_version/bc/:playerId/:salt/:hash", Schema.toCodecStringTree<Schema.Struct<{
|
|
146
|
+
playerId: Schema.brand<Schema.String, "PlayerId">;
|
|
147
|
+
salt: Schema.Int;
|
|
148
|
+
hash: Schema.String;
|
|
149
|
+
}>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
150
|
+
readonly error: Schema.String;
|
|
151
|
+
}> | Schema.Struct<{
|
|
152
|
+
readonly success: Schema.Literal<"NotFound">;
|
|
153
|
+
}> | Schema.encodeKeys<Schema.Struct<{
|
|
154
|
+
readonly success: Schema.Literal<"Found">;
|
|
155
|
+
readonly checksum: Schema.String;
|
|
156
|
+
readonly saveId: Schema.NumberFromString;
|
|
157
|
+
}>, {
|
|
158
|
+
readonly checksum: "h";
|
|
159
|
+
readonly saveId: "id";
|
|
160
|
+
}>>, Schema.toCodecJson<typeof HttpApiError.BadRequest | typeof HttpApiError.Unauthorized | typeof HttpApiError.Forbidden | typeof HttpApiError.InternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"SyncPullSave", "GET", "/sync/pull/bc/:playerId/:salt/:hash", Schema.toCodecStringTree<Schema.Struct<{
|
|
161
|
+
playerId: Schema.brand<Schema.String, "PlayerId">;
|
|
162
|
+
salt: Schema.Int;
|
|
163
|
+
hash: Schema.String;
|
|
164
|
+
}>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
165
|
+
readonly error: Schema.String;
|
|
166
|
+
}> | Schema.Struct<{
|
|
167
|
+
readonly success: Schema.Literal<"NotFound">;
|
|
168
|
+
}> | Schema.encodeKeys<Schema.Struct<{
|
|
169
|
+
readonly success: Schema.Literal<"Found">;
|
|
170
|
+
readonly data: Schema.Uint8ArrayFromBase64;
|
|
171
|
+
readonly checksum: Schema.String;
|
|
172
|
+
readonly saveId: Schema.NumberFromString;
|
|
173
|
+
}>, {
|
|
174
|
+
readonly checksum: "h";
|
|
175
|
+
readonly saveId: "id";
|
|
176
|
+
}>>, Schema.toCodecJson<typeof HttpApiError.BadRequest | typeof HttpApiError.Unauthorized | typeof HttpApiError.Forbidden | typeof HttpApiError.InternalServerError>, never, never>, false>>;
|
|
177
|
+
//# sourceMappingURL=Endpoints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Endpoints.d.ts","sourceRoot":"","sources":["../src/Endpoints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACvF,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AA8BxC;;;;;;;GAOG;AACH,eAAO,MAAM,IAAI,gBAAiB,CAAC;AAwNnC;;;;;GAKG;AACH,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4LAAkG,CAAC"}
|