castle-web-sdk 0.4.4 → 0.4.6
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/README.md +321 -0
- package/dist/castle.d.ts +2 -0
- package/dist/castle.js +1 -0
- package/dist/commands.d.ts +117 -0
- package/dist/commands.js +16 -0
- package/dist/context.d.ts +2 -19
- package/dist/context.js +0 -71
- package/dist/leaderboard.js +69 -114
- package/dist/passes.d.ts +7 -0
- package/dist/passes.js +84 -0
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.js +89 -15
- package/dist/storage.d.ts +1 -2
- package/dist/storage.js +82 -198
- package/dist/time.js +5 -16
- package/dist/transport.d.ts +16 -0
- package/dist/transport.js +126 -0
- package/dist/user.js +6 -23
- package/package.json +7 -4
- package/dist/auth.d.ts +0 -8
- package/dist/auth.js +0 -52
- package/dist/graphql.d.ts +0 -15
- package/dist/graphql.js +0 -120
- package/src/auth.ts +0 -64
- package/src/castle.ts +0 -19
- package/src/context.ts +0 -124
- package/src/errors.ts +0 -32
- package/src/graphql.ts +0 -182
- package/src/leaderboard.ts +0 -456
- package/src/runtime.ts +0 -372
- package/src/storage.ts +0 -636
- package/src/time.ts +0 -226
- package/src/types.ts +0 -7
- package/src/user.ts +0 -91
package/src/time.ts
DELETED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import { CastleError } from "./errors";
|
|
2
|
-
import { graphqlRequest } from "./graphql";
|
|
3
|
-
import type { Json } from "./types";
|
|
4
|
-
|
|
5
|
-
export type CastleClockZone = "player" | "Castle";
|
|
6
|
-
|
|
7
|
-
export interface CastleDateParts {
|
|
8
|
-
sec: number;
|
|
9
|
-
min: number;
|
|
10
|
-
hour: number;
|
|
11
|
-
day: number;
|
|
12
|
-
month: number;
|
|
13
|
-
year: number;
|
|
14
|
-
wday: number;
|
|
15
|
-
yday: number;
|
|
16
|
-
daysSinceCastleEpoch: number;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface CastleTimeApi {
|
|
20
|
-
getServerTime(): Promise<number>;
|
|
21
|
-
getServerDate(timezone?: CastleClockZone): Promise<CastleDateParts>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface ServerTimeQueryData {
|
|
25
|
-
serverTime: {
|
|
26
|
-
timestamp: number;
|
|
27
|
-
timezoneOffset: number;
|
|
28
|
-
castleEpochData: Json;
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
interface ServerTimeSnapshot {
|
|
33
|
-
offsetSeconds: number;
|
|
34
|
-
castleTimezoneOffsetMinutes: number;
|
|
35
|
-
daysSinceCastleEpoch: Record<CastleClockZone, number>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const SERVER_TIME_QUERY = `
|
|
39
|
-
query CastleServerTime {
|
|
40
|
-
serverTime {
|
|
41
|
-
timestamp
|
|
42
|
-
timezoneOffset
|
|
43
|
-
castleEpochData
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
`;
|
|
47
|
-
|
|
48
|
-
let serverTimeSnapshot: ServerTimeSnapshot | null = null;
|
|
49
|
-
let serverTimePromise: Promise<ServerTimeSnapshot> | null = null;
|
|
50
|
-
|
|
51
|
-
export const Time: CastleTimeApi = {
|
|
52
|
-
getServerTime,
|
|
53
|
-
getServerDate,
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
async function getServerTime(): Promise<number> {
|
|
57
|
-
const snapshot = await syncServerTime();
|
|
58
|
-
return clientUnixSeconds() + snapshot.offsetSeconds;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async function getServerDate(
|
|
62
|
-
timezone: string = "Castle",
|
|
63
|
-
): Promise<CastleDateParts> {
|
|
64
|
-
const operation = "Time.getServerDate";
|
|
65
|
-
const zone = clockZone(timezone, operation);
|
|
66
|
-
const snapshot = await syncServerTime();
|
|
67
|
-
return dateParts(
|
|
68
|
-
clientUnixSeconds() + snapshot.offsetSeconds,
|
|
69
|
-
zone,
|
|
70
|
-
snapshot,
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async function syncServerTime(): Promise<ServerTimeSnapshot> {
|
|
75
|
-
if (serverTimeSnapshot) return serverTimeSnapshot;
|
|
76
|
-
if (!serverTimePromise) {
|
|
77
|
-
serverTimePromise = fetchServerTime().then((snapshot) => {
|
|
78
|
-
serverTimeSnapshot = snapshot;
|
|
79
|
-
return snapshot;
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
return serverTimePromise;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async function fetchServerTime(): Promise<ServerTimeSnapshot> {
|
|
86
|
-
const operation = "Time.getServerTime";
|
|
87
|
-
const data = await graphqlRequest<ServerTimeQueryData>(
|
|
88
|
-
SERVER_TIME_QUERY,
|
|
89
|
-
undefined,
|
|
90
|
-
{
|
|
91
|
-
operation,
|
|
92
|
-
},
|
|
93
|
-
);
|
|
94
|
-
const timestamp = numberField(
|
|
95
|
-
data.serverTime.timestamp,
|
|
96
|
-
"serverTime.timestamp",
|
|
97
|
-
operation,
|
|
98
|
-
);
|
|
99
|
-
const timezoneOffset = numberField(
|
|
100
|
-
data.serverTime.timezoneOffset,
|
|
101
|
-
"serverTime.timezoneOffset",
|
|
102
|
-
operation,
|
|
103
|
-
);
|
|
104
|
-
const epochData = jsonObject(
|
|
105
|
-
data.serverTime.castleEpochData,
|
|
106
|
-
"serverTime.castleEpochData",
|
|
107
|
-
operation,
|
|
108
|
-
);
|
|
109
|
-
return {
|
|
110
|
-
offsetSeconds: snappedOffset(timestamp - clientUnixSeconds()),
|
|
111
|
-
castleTimezoneOffsetMinutes: timezoneOffset,
|
|
112
|
-
daysSinceCastleEpoch: {
|
|
113
|
-
Castle: numberField(
|
|
114
|
-
epochData.daysSinceServerTz,
|
|
115
|
-
"castleEpochData.daysSinceServerTz",
|
|
116
|
-
operation,
|
|
117
|
-
),
|
|
118
|
-
player: numberField(
|
|
119
|
-
epochData.daysSinceUserTz,
|
|
120
|
-
"castleEpochData.daysSinceUserTz",
|
|
121
|
-
operation,
|
|
122
|
-
),
|
|
123
|
-
},
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function dateParts(
|
|
128
|
-
unixSeconds: number,
|
|
129
|
-
timezone: CastleClockZone,
|
|
130
|
-
snapshot: ServerTimeSnapshot,
|
|
131
|
-
): CastleDateParts {
|
|
132
|
-
const shiftedSeconds =
|
|
133
|
-
timezone === "Castle"
|
|
134
|
-
? unixSeconds + snapshot.castleTimezoneOffsetMinutes * 60
|
|
135
|
-
: unixSeconds;
|
|
136
|
-
const date = new Date(shiftedSeconds * 1000);
|
|
137
|
-
return timezone === "Castle"
|
|
138
|
-
? utcDateParts(date, snapshot.daysSinceCastleEpoch.Castle)
|
|
139
|
-
: localDateParts(date, snapshot.daysSinceCastleEpoch.player);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function utcDateParts(
|
|
143
|
-
date: Date,
|
|
144
|
-
daysSinceCastleEpoch: number,
|
|
145
|
-
): CastleDateParts {
|
|
146
|
-
return {
|
|
147
|
-
sec: date.getUTCSeconds(),
|
|
148
|
-
min: date.getUTCMinutes(),
|
|
149
|
-
hour: date.getUTCHours(),
|
|
150
|
-
day: date.getUTCDate(),
|
|
151
|
-
month: date.getUTCMonth() + 1,
|
|
152
|
-
year: date.getUTCFullYear(),
|
|
153
|
-
wday: date.getUTCDay() + 1,
|
|
154
|
-
yday: dayOfYear(
|
|
155
|
-
date.getUTCFullYear(),
|
|
156
|
-
date.getUTCMonth(),
|
|
157
|
-
date.getUTCDate(),
|
|
158
|
-
),
|
|
159
|
-
daysSinceCastleEpoch,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function localDateParts(
|
|
164
|
-
date: Date,
|
|
165
|
-
daysSinceCastleEpoch: number,
|
|
166
|
-
): CastleDateParts {
|
|
167
|
-
return {
|
|
168
|
-
sec: date.getSeconds(),
|
|
169
|
-
min: date.getMinutes(),
|
|
170
|
-
hour: date.getHours(),
|
|
171
|
-
day: date.getDate(),
|
|
172
|
-
month: date.getMonth() + 1,
|
|
173
|
-
year: date.getFullYear(),
|
|
174
|
-
wday: date.getDay() + 1,
|
|
175
|
-
yday: dayOfYear(date.getFullYear(), date.getMonth(), date.getDate()),
|
|
176
|
-
daysSinceCastleEpoch,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function dayOfYear(year: number, monthIndex: number, day: number): number {
|
|
181
|
-
const start = Date.UTC(year, 0, 1);
|
|
182
|
-
const current = Date.UTC(year, monthIndex, day);
|
|
183
|
-
return Math.floor((current - start) / 86400000) + 1;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function clockZone(timezone: string, operation: string): CastleClockZone {
|
|
187
|
-
const normalized = timezone.trim().toLowerCase();
|
|
188
|
-
if (normalized === "castle") return "Castle";
|
|
189
|
-
if (normalized === "player") return "player";
|
|
190
|
-
throw new CastleError({
|
|
191
|
-
code: "UNSUPPORTED_TIMEZONE",
|
|
192
|
-
message: 'Time.getServerDate timezone must be "Castle" or "player".',
|
|
193
|
-
operation,
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function clientUnixSeconds(): number {
|
|
198
|
-
return Math.floor(Date.now() / 1000);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function snappedOffset(offsetSeconds: number): number {
|
|
202
|
-
return Math.abs(offsetSeconds) < 10 ? 0 : offsetSeconds;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function jsonObject(
|
|
206
|
-
value: Json,
|
|
207
|
-
field: string,
|
|
208
|
-
operation: string,
|
|
209
|
-
): Record<string, Json> {
|
|
210
|
-
if (value !== null && typeof value === "object" && !Array.isArray(value))
|
|
211
|
-
return value;
|
|
212
|
-
throw new CastleError({
|
|
213
|
-
code: "GRAPHQL_BAD_DATA",
|
|
214
|
-
message: `Castle GraphQL field ${field} was not an object.`,
|
|
215
|
-
operation,
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function numberField(value: Json, field: string, operation: string): number {
|
|
220
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
221
|
-
throw new CastleError({
|
|
222
|
-
code: "GRAPHQL_BAD_DATA",
|
|
223
|
-
message: `Castle GraphQL field ${field} was not a number.`,
|
|
224
|
-
operation,
|
|
225
|
-
});
|
|
226
|
-
}
|
package/src/types.ts
DELETED
package/src/user.ts
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { requireAuthToken } from "./auth";
|
|
2
|
-
import { CastleError } from "./errors";
|
|
3
|
-
import { graphqlRequest } from "./graphql";
|
|
4
|
-
|
|
5
|
-
export interface CastleUser {
|
|
6
|
-
userId: string;
|
|
7
|
-
username: string;
|
|
8
|
-
isActive: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface CastleUserApi {
|
|
12
|
-
getCurrent(): Promise<CastleUser>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface CurrentUserQueryData {
|
|
16
|
-
me: {
|
|
17
|
-
userId?: string | null;
|
|
18
|
-
username?: string | null;
|
|
19
|
-
} | null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const CURRENT_USER_QUERY = `
|
|
23
|
-
query CastleCurrentUser {
|
|
24
|
-
me {
|
|
25
|
-
userId
|
|
26
|
-
username
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
`;
|
|
30
|
-
|
|
31
|
-
let currentUser: CastleUser | null = null;
|
|
32
|
-
let currentUserPromise: Promise<CastleUser> | null = null;
|
|
33
|
-
|
|
34
|
-
export const User: CastleUserApi = {
|
|
35
|
-
getCurrent,
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
async function getCurrent(): Promise<CastleUser> {
|
|
39
|
-
if (currentUser) return currentUser;
|
|
40
|
-
currentUserPromise ??= fetchCurrentUser().then((user) => {
|
|
41
|
-
currentUser = user;
|
|
42
|
-
return user;
|
|
43
|
-
});
|
|
44
|
-
return currentUserPromise;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function fetchCurrentUser(): Promise<CastleUser> {
|
|
48
|
-
const operation = "User.getCurrent";
|
|
49
|
-
const token = await requireAuthToken(operation);
|
|
50
|
-
const data = await graphqlRequest<CurrentUserQueryData>(
|
|
51
|
-
CURRENT_USER_QUERY,
|
|
52
|
-
undefined,
|
|
53
|
-
{
|
|
54
|
-
operation,
|
|
55
|
-
requireAuth: true,
|
|
56
|
-
token,
|
|
57
|
-
},
|
|
58
|
-
);
|
|
59
|
-
if (!data.me) {
|
|
60
|
-
throw new CastleError({
|
|
61
|
-
code: "LOGIN_REQUIRED",
|
|
62
|
-
message: "Log in to Castle before using User.getCurrent().",
|
|
63
|
-
operation,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
return normalizeCurrentUser(data.me, operation);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function normalizeCurrentUser(
|
|
70
|
-
user: NonNullable<CurrentUserQueryData["me"]>,
|
|
71
|
-
operation: string,
|
|
72
|
-
): CastleUser {
|
|
73
|
-
return {
|
|
74
|
-
userId: requiredString(user.userId, "me.userId", operation),
|
|
75
|
-
username: requiredString(user.username, "me.username", operation),
|
|
76
|
-
isActive: true,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function requiredString(
|
|
81
|
-
value: string | null | undefined,
|
|
82
|
-
field: string,
|
|
83
|
-
operation: string,
|
|
84
|
-
): string {
|
|
85
|
-
if (typeof value === "string" && value.length > 0) return value;
|
|
86
|
-
throw new CastleError({
|
|
87
|
-
code: "GRAPHQL_BAD_DATA",
|
|
88
|
-
message: `Castle GraphQL response did not include ${field}.`,
|
|
89
|
-
operation,
|
|
90
|
-
});
|
|
91
|
-
}
|