@openleaderboard/sdk 0.2.0 → 0.4.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/dist/index.d.ts +36 -0
- package/dist/index.js +35 -0
- package/package.json +22 -7
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,14 @@ export interface RankEntry {
|
|
|
16
16
|
score: number;
|
|
17
17
|
rank: number;
|
|
18
18
|
exact: boolean;
|
|
19
|
+
nickname?: string;
|
|
20
|
+
}
|
|
21
|
+
/** A registered player: server-minted id + nickname unique per app (case-insensitive). */
|
|
22
|
+
export interface User {
|
|
23
|
+
user_id: string;
|
|
24
|
+
nickname: string;
|
|
25
|
+
created_at?: string;
|
|
26
|
+
updated_at?: string;
|
|
19
27
|
}
|
|
20
28
|
export interface SubmitResult {
|
|
21
29
|
accepted: boolean;
|
|
@@ -30,6 +38,13 @@ export interface QueryOpts {
|
|
|
30
38
|
export interface SubmitOpts {
|
|
31
39
|
segments?: string[];
|
|
32
40
|
idem?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Event time of the score. Determines which time-window bucket it lands in
|
|
43
|
+
* (e.g. the daily board) — set it to the session start so a run that crosses
|
|
44
|
+
* midnight counts for the day it began, rather than when it was submitted.
|
|
45
|
+
* Accepts a Date or an ISO-8601 string; defaults to server receive time.
|
|
46
|
+
*/
|
|
47
|
+
time?: Date | string;
|
|
33
48
|
}
|
|
34
49
|
export interface WindowDef {
|
|
35
50
|
kind: string;
|
|
@@ -71,6 +86,10 @@ export declare class LeaderboardError extends Error {
|
|
|
71
86
|
export declare class NotFoundError extends LeaderboardError {
|
|
72
87
|
constructor(message: string);
|
|
73
88
|
}
|
|
89
|
+
/** Thrown when a nickname is already claimed in this app (HTTP 409). */
|
|
90
|
+
export declare class NicknameTakenError extends LeaderboardError {
|
|
91
|
+
constructor(message: string);
|
|
92
|
+
}
|
|
74
93
|
export declare class LeaderboardClient {
|
|
75
94
|
private readonly apiKey;
|
|
76
95
|
private readonly opts;
|
|
@@ -98,6 +117,23 @@ export declare class LeaderboardClient {
|
|
|
98
117
|
getNeighbors(board: string, member: string, k: number, q?: QueryOpts): Promise<RankEntry[]>;
|
|
99
118
|
/** Rank an explicit set of members against each other (a friend leaderboard). */
|
|
100
119
|
getFriends(board: string, members: string[], q?: QueryOpts): Promise<RankEntry[]>;
|
|
120
|
+
/**
|
|
121
|
+
* Register a player: mints a `plr_...` user id and claims a nickname
|
|
122
|
+
* (unique per app, case-insensitive). Submit scores with `user_id` as the
|
|
123
|
+
* member; reads then include the nickname. Throws {@link NicknameTakenError}
|
|
124
|
+
* if the name is claimed.
|
|
125
|
+
*/
|
|
126
|
+
registerUser(nickname: string): Promise<User>;
|
|
127
|
+
/** Fetch a registered player by id. Throws {@link NotFoundError} if absent. */
|
|
128
|
+
getUser(userId: string): Promise<User>;
|
|
129
|
+
/** Resolve a nickname (case-insensitive) to its player. */
|
|
130
|
+
getUserByNickname(nickname: string): Promise<User>;
|
|
131
|
+
/**
|
|
132
|
+
* Change a player's nickname. The user id — and therefore board data and
|
|
133
|
+
* HMAC signatures — is unaffected. Throws {@link NicknameTakenError} on
|
|
134
|
+
* conflict.
|
|
135
|
+
*/
|
|
136
|
+
renameUser(userId: string, nickname: string): Promise<User>;
|
|
101
137
|
private send;
|
|
102
138
|
}
|
|
103
139
|
/**
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,13 @@ export class NotFoundError extends LeaderboardError {
|
|
|
26
26
|
this.name = "NotFoundError";
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
+
/** Thrown when a nickname is already claimed in this app (HTTP 409). */
|
|
30
|
+
export class NicknameTakenError extends LeaderboardError {
|
|
31
|
+
constructor(message) {
|
|
32
|
+
super(409, message);
|
|
33
|
+
this.name = "NicknameTakenError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
29
36
|
export class LeaderboardClient {
|
|
30
37
|
constructor(baseUrl, apiKey, opts = {}) {
|
|
31
38
|
this.apiKey = apiKey;
|
|
@@ -58,6 +65,7 @@ export class LeaderboardClient {
|
|
|
58
65
|
score,
|
|
59
66
|
segments: opts.segments,
|
|
60
67
|
idem: opts.idem,
|
|
68
|
+
time: opts.time instanceof Date ? opts.time.toISOString() : opts.time,
|
|
61
69
|
};
|
|
62
70
|
if (this.opts.signingSecret) {
|
|
63
71
|
const ts = Math.floor(Date.now() / 1000);
|
|
@@ -102,6 +110,31 @@ export class LeaderboardClient {
|
|
|
102
110
|
const r = await this.send("POST", `/v1/boards/${enc(board)}/friends${qs({ ...q })}`, { members });
|
|
103
111
|
return r.entries ?? [];
|
|
104
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Register a player: mints a `plr_...` user id and claims a nickname
|
|
115
|
+
* (unique per app, case-insensitive). Submit scores with `user_id` as the
|
|
116
|
+
* member; reads then include the nickname. Throws {@link NicknameTakenError}
|
|
117
|
+
* if the name is claimed.
|
|
118
|
+
*/
|
|
119
|
+
async registerUser(nickname) {
|
|
120
|
+
return this.send("POST", "/v1/users", { nickname });
|
|
121
|
+
}
|
|
122
|
+
/** Fetch a registered player by id. Throws {@link NotFoundError} if absent. */
|
|
123
|
+
async getUser(userId) {
|
|
124
|
+
return this.send("GET", `/v1/users/${enc(userId)}`);
|
|
125
|
+
}
|
|
126
|
+
/** Resolve a nickname (case-insensitive) to its player. */
|
|
127
|
+
async getUserByNickname(nickname) {
|
|
128
|
+
return this.send("GET", `/v1/users${qs({ nickname })}`);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Change a player's nickname. The user id — and therefore board data and
|
|
132
|
+
* HMAC signatures — is unaffected. Throws {@link NicknameTakenError} on
|
|
133
|
+
* conflict.
|
|
134
|
+
*/
|
|
135
|
+
async renameUser(userId, nickname) {
|
|
136
|
+
return this.send("PATCH", `/v1/users/${enc(userId)}`, { nickname });
|
|
137
|
+
}
|
|
105
138
|
async send(method, path, body) {
|
|
106
139
|
const headers = { Authorization: `Bearer ${this.apiKey}` };
|
|
107
140
|
let bodyStr;
|
|
@@ -113,6 +146,8 @@ export class LeaderboardClient {
|
|
|
113
146
|
const text = await resp.text();
|
|
114
147
|
if (resp.status === 404)
|
|
115
148
|
throw new NotFoundError(text);
|
|
149
|
+
if (resp.status === 409)
|
|
150
|
+
throw new NicknameTakenError(text);
|
|
116
151
|
if (!resp.ok)
|
|
117
152
|
throw new LeaderboardError(resp.status, `${method} ${path} -> ${resp.status}: ${text}`);
|
|
118
153
|
return text ? JSON.parse(text) : {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openleaderboard/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "TypeScript client for the OpenLeaderboard API (browser + Node 18+).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
"import": "./dist/index.js"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
|
-
"files": [
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
16
18
|
"scripts": {
|
|
17
19
|
"build": "tsc -p tsconfig.json",
|
|
18
20
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
@@ -20,7 +22,12 @@
|
|
|
20
22
|
"test:hmac": "node test/hmac.mjs",
|
|
21
23
|
"prepublishOnly": "npm run build"
|
|
22
24
|
},
|
|
23
|
-
"keywords": [
|
|
25
|
+
"keywords": [
|
|
26
|
+
"leaderboard",
|
|
27
|
+
"gaming",
|
|
28
|
+
"ranking",
|
|
29
|
+
"api"
|
|
30
|
+
],
|
|
24
31
|
"license": "Apache-2.0",
|
|
25
32
|
"repository": {
|
|
26
33
|
"type": "git",
|
|
@@ -28,8 +35,16 @@
|
|
|
28
35
|
"directory": "sdk/typescript"
|
|
29
36
|
},
|
|
30
37
|
"homepage": "https://github.com/kodeni-am/leaderboard/tree/main/sdk/typescript#readme",
|
|
31
|
-
"bugs": {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/kodeni-am/leaderboard/issues"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"typescript": "^5.5.0"
|
|
49
|
+
}
|
|
35
50
|
}
|