@colyseus/sdk 0.17.1 → 0.17.2

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.
Files changed (67) hide show
  1. package/build/cjs/3rd_party/discord.js +1 -1
  2. package/build/cjs/Auth.js +1 -1
  3. package/build/cjs/Client.js +1 -1
  4. package/build/cjs/Connection.js +1 -1
  5. package/build/cjs/HTTP.js +1 -1
  6. package/build/cjs/Protocol.js +1 -1
  7. package/build/cjs/Room.js +1 -1
  8. package/build/cjs/Storage.js +1 -1
  9. package/build/cjs/core/nanoevents.js +1 -1
  10. package/build/cjs/core/signal.js +1 -1
  11. package/build/cjs/core/utils.js +1 -1
  12. package/build/cjs/errors/Errors.js +1 -1
  13. package/build/cjs/index.js +1 -1
  14. package/build/cjs/legacy.js +1 -1
  15. package/build/cjs/serializer/NoneSerializer.js +1 -1
  16. package/build/cjs/serializer/SchemaSerializer.js +1 -1
  17. package/build/cjs/serializer/Serializer.js +1 -1
  18. package/build/cjs/transport/H3Transport.js +1 -1
  19. package/build/cjs/transport/WebSocketTransport.js +1 -1
  20. package/build/esm/3rd_party/discord.mjs +1 -1
  21. package/build/esm/Auth.mjs +1 -1
  22. package/build/esm/Client.mjs +1 -1
  23. package/build/esm/Connection.mjs +1 -1
  24. package/build/esm/HTTP.mjs +1 -1
  25. package/build/esm/Protocol.mjs +1 -1
  26. package/build/esm/Room.mjs +1 -1
  27. package/build/esm/Storage.mjs +1 -1
  28. package/build/esm/core/nanoevents.mjs +1 -1
  29. package/build/esm/core/signal.mjs +1 -1
  30. package/build/esm/core/utils.mjs +1 -1
  31. package/build/esm/errors/Errors.mjs +1 -1
  32. package/build/esm/index.mjs +1 -1
  33. package/build/esm/legacy.mjs +1 -1
  34. package/build/esm/serializer/NoneSerializer.mjs +1 -1
  35. package/build/esm/serializer/SchemaSerializer.mjs +1 -1
  36. package/build/esm/serializer/Serializer.mjs +1 -1
  37. package/build/esm/transport/H3Transport.mjs +1 -1
  38. package/build/esm/transport/WebSocketTransport.mjs +1 -1
  39. package/dist/colyseus-cocos-creator.js +1 -1
  40. package/dist/colyseus.js +1 -1
  41. package/dist/debug.js +1 -1
  42. package/lib/core/http_bkp.d.ts +10 -10
  43. package/package.json +7 -6
  44. package/src/3rd_party/discord.ts +48 -0
  45. package/src/Auth.ts +177 -0
  46. package/src/Client.ts +459 -0
  47. package/src/Connection.ts +51 -0
  48. package/src/HTTP.ts +545 -0
  49. package/src/HTTP_bkp.ts +67 -0
  50. package/src/Protocol.ts +25 -0
  51. package/src/Room.ts +505 -0
  52. package/src/Storage.ts +94 -0
  53. package/src/core/http_bkp.ts +358 -0
  54. package/src/core/nanoevents.ts +38 -0
  55. package/src/core/signal.ts +62 -0
  56. package/src/core/utils.ts +3 -0
  57. package/src/debug.ts +2743 -0
  58. package/src/errors/Errors.ts +29 -0
  59. package/src/index.ts +18 -0
  60. package/src/legacy.ts +29 -0
  61. package/src/serializer/FossilDeltaSerializer.ts +39 -0
  62. package/src/serializer/NoneSerializer.ts +9 -0
  63. package/src/serializer/SchemaSerializer.ts +61 -0
  64. package/src/serializer/Serializer.ts +23 -0
  65. package/src/transport/H3Transport.ts +199 -0
  66. package/src/transport/ITransport.ts +18 -0
  67. package/src/transport/WebSocketTransport.ts +53 -0
package/src/HTTP.ts ADDED
@@ -0,0 +1,545 @@
1
+ import type { Router, HasRequiredKeys, Prettify, UnionToIntersection, Endpoint, HTTPMethod } from "@colyseus/better-call";
2
+ import { ColyseusSDK } from "./Client.ts";
3
+
4
+ // Helper to check if a type is 'any'
5
+ type IsAny<T> = 0 extends 1 & T ? true : false;
6
+
7
+ // Helper to check if a type resolves to any after indexed access
8
+ // When T is any, T[K] is also any, but IsAny<T[K]> may not detect it due to deferred evaluation
9
+ // We check multiple characteristics of 'any':
10
+ // 1. Direct any check: IsAny<T>
11
+ // 2. Accepts all string keys: string extends keyof T
12
+ // 3. Accepts all number and symbol keys: for complete 'any' detection
13
+ type IsAnyOrAnyIndexed<T> = IsAny<T> extends true
14
+ ? true
15
+ : (string extends keyof T
16
+ ? true
17
+ : (number extends keyof T
18
+ ? (symbol extends keyof T ? true : false)
19
+ : false));
20
+
21
+ type HasRequired<
22
+ T extends {
23
+ body?: any;
24
+ query?: any;
25
+ params?: any;
26
+ },
27
+ > = T["body"] extends object
28
+ ? HasRequiredKeys<T["body"]> extends true
29
+ ? true
30
+ : T["query"] extends object
31
+ ? HasRequiredKeys<T["query"]> extends true
32
+ ? true
33
+ : T["params"] extends object
34
+ ? HasRequiredKeys<T["params"]>
35
+ : false
36
+ : T["params"] extends object
37
+ ? HasRequiredKeys<T["params"]>
38
+ : false
39
+ : T["query"] extends object
40
+ ? HasRequiredKeys<T["query"]> extends true
41
+ ? true
42
+ : T["params"] extends object
43
+ ? HasRequiredKeys<T["params"]>
44
+ : false
45
+ : T["params"] extends object
46
+ ? HasRequiredKeys<T["params"]>
47
+ : false;
48
+
49
+ type InferContext<T> = T extends (ctx: infer Ctx) => any
50
+ ? Ctx extends object
51
+ ? Ctx
52
+ : never
53
+ : never;
54
+
55
+ // WithRequired - makes specific keys required
56
+ // This works by spreading T and then overriding the specified keys to be non-nullable
57
+ type WithRequired<T, K extends keyof any> = Prettify<T & {
58
+ [P in K & keyof T]-?: NonNullable<T[P]>
59
+ }>;
60
+
61
+ type WithoutServerOnly<T extends Record<string, Endpoint>> = {
62
+ [K in keyof T]: T[K] extends Endpoint<any, infer O>
63
+ ? O extends { metadata: { SERVER_ONLY: true } }
64
+ ? never
65
+ : T[K]
66
+ : T[K];
67
+ };
68
+
69
+ // Method-specific options type
70
+ type MethodOptions<API, M extends HTTPMethod> = API extends { [key: string]: infer T; }
71
+ ? T extends Endpoint<any, infer O>
72
+ ? O["method"] extends M
73
+ ? { [key in T["path"]]: T; }
74
+ : O["method"] extends M[]
75
+ ? M extends O["method"][number]
76
+ ? { [key in T["path"]]: T; }
77
+ : {}
78
+ : O["method"] extends "*"
79
+ ? { [key in T["path"]]: T; }
80
+ : {}
81
+ : {}
82
+ : {};
83
+
84
+ export type RequiredOptionKeys<
85
+ C extends {
86
+ body?: any;
87
+ query?: any;
88
+ params?: any;
89
+ },
90
+ > = (C["body"] extends object
91
+ ? HasRequiredKeys<C["body"]> extends true
92
+ ? { body: true }
93
+ : {}
94
+ : {}) &
95
+ (C["query"] extends object
96
+ ? HasRequiredKeys<C["query"]> extends true
97
+ ? { query: true }
98
+ : {}
99
+ : {}) &
100
+ (C["params"] extends object
101
+ ? HasRequiredKeys<C["params"]> extends true
102
+ ? { params: true }
103
+ : {}
104
+ : {});
105
+
106
+
107
+ type CommonHeaders = {
108
+ accept: "application/json" | "text/plain" | "application/octet-stream";
109
+ "content-type": "application/json" | "text/plain" | "application/x-www-form-urlencoded" | "multipart/form-data" | "application/octet-stream";
110
+ authorization: "Bearer" | "Basic";
111
+ };
112
+
113
+ type FetchRequestOptions<
114
+ Body = any,
115
+ Query extends Record<string, any> = any,
116
+ Params extends Record<string, any> | Array<string> | undefined = any, Res = any,
117
+ ExtraOptions extends Record<string, any> = {}
118
+ > = Prettify<ExtraOptions & Omit<RequestInit, "body"> & {
119
+ // baseURL?: string;
120
+
121
+ /**
122
+ * Headers
123
+ */
124
+ headers?: CommonHeaders | Headers | HeadersInit;
125
+
126
+ /**
127
+ * Body
128
+ */
129
+ body?: Body;
130
+
131
+ /**
132
+ * Query parameters (key-value pairs)
133
+ */
134
+ query?: Query;
135
+
136
+ /**
137
+ * Dynamic parameters.
138
+ *
139
+ * If url is defined as /path/:id, params will be { id: string }
140
+ */
141
+ params?: Params;
142
+ }>
143
+
144
+ type ResponseData<T> = {
145
+ ok: true;
146
+ data: T;
147
+ error: null,
148
+ response: Response;
149
+ headers: Headers;
150
+ status: number;
151
+ statusText: string;
152
+ };
153
+
154
+ type ResponseError<E> = {
155
+ ok: false,
156
+ data: null,
157
+ error: Prettify<(E extends Record<string, any> ? E : {
158
+ message?: string;
159
+ }) & {
160
+ code?: string;
161
+ }>;
162
+ response: Response;
163
+ headers: Headers;
164
+ status: number;
165
+ statusText: string;
166
+ };
167
+
168
+ type FetchResponse<T, E extends Record<string, unknown> | unknown = unknown, Throw extends boolean = false> =
169
+ Throw extends true
170
+ ? T
171
+ : ResponseData<T> | ResponseError<E>;
172
+
173
+ export function isJSONSerializable(value: any) {
174
+ if (value === undefined) {
175
+ return false;
176
+ }
177
+ const t = typeof value;
178
+ if (t === "string" || t === "number" || t === "boolean" || t === null) {
179
+ return true;
180
+ }
181
+ if (t !== "object") {
182
+ return false;
183
+ }
184
+ if (Array.isArray(value)) {
185
+ return true;
186
+ }
187
+ if (value.buffer) {
188
+ return false;
189
+ }
190
+ return (
191
+ (value.constructor && value.constructor.name === "Object") ||
192
+ typeof value.toJSON === "function"
193
+ );
194
+ }
195
+
196
+ const JSON_RE = /^application\/(?:[\w!#$%&*.^`~-]*\+)?json(;.+)?$/i;
197
+
198
+ export type ResponseType = "json" | "text" | "blob";
199
+ export function detectResponseType(request: Response): ResponseType {
200
+ const _contentType = request.headers.get("content-type");
201
+ const textTypes = new Set([
202
+ "image/svg",
203
+ "application/xml",
204
+ "application/xhtml",
205
+ "application/html",
206
+ ]);
207
+ if (!_contentType) {
208
+ return "json";
209
+ }
210
+ const contentType = _contentType.split(";").shift() || "";
211
+ if (JSON_RE.test(contentType)) {
212
+ return "json";
213
+ }
214
+ if (textTypes.has(contentType) || contentType.startsWith("text/")) {
215
+ return "text";
216
+ }
217
+ return "blob";
218
+ }
219
+
220
+ function getURLWithQueryParams(url: string, option?: FetchRequestOptions) {
221
+ const { params, query } = option || {};
222
+
223
+ // Parse the URL and extract existing query parameters
224
+ const [urlPath, urlQuery] = url.split("?");
225
+ let path = urlPath;
226
+
227
+ // Handle params substitution
228
+ if (params) {
229
+ if (Array.isArray(params)) {
230
+ const paramPaths = path.split("/").filter((p) => p.startsWith(":"));
231
+ for (const [index, key] of paramPaths.entries()) {
232
+ const value = params[index];
233
+ path = path.replace(key, value);
234
+ }
235
+ } else {
236
+ for (const [key, value] of Object.entries(params)) {
237
+ path = path.replace(`:${key}`, String(value));
238
+ }
239
+ }
240
+ }
241
+
242
+ // Merge query parameters from URL and options
243
+ const queryParams = new URLSearchParams(urlQuery);
244
+ if (query) {
245
+ for (const [key, value] of Object.entries(query)) {
246
+ if (value == null) continue;
247
+ queryParams.set(key, String(value));
248
+ }
249
+ }
250
+
251
+ // Build final URL
252
+ let queryParamString = queryParams.toString();
253
+ queryParamString = queryParamString.length > 0 ? `?${queryParamString}`.replace(/\+/g, "%20") : "";
254
+
255
+ return `${path}${queryParamString}`;
256
+ }
257
+
258
+ type InferredAPI<R> = R extends { endpoints: Record<string, Endpoint> }
259
+ ? WithoutServerOnly<R["endpoints"]>
260
+ : WithoutServerOnly<R & Record<string, Endpoint>>;
261
+
262
+ export class HTTP<R extends Router | Router["endpoints"]> {
263
+ public authToken: string | undefined;
264
+ public options: FetchRequestOptions;
265
+
266
+ private sdk: ColyseusSDK;
267
+
268
+ constructor(sdk: ColyseusSDK, baseOptions: FetchRequestOptions) {
269
+ this.sdk = sdk;
270
+ this.options = baseOptions;
271
+ }
272
+
273
+ private async request<
274
+ M extends HTTPMethod,
275
+ API extends InferredAPI<R> = InferredAPI<R>,
276
+ OPT extends Prettify<UnionToIntersection<MethodOptions<API, M>>> = Prettify<UnionToIntersection<MethodOptions<API, M>>>,
277
+ K extends keyof OPT = keyof OPT,
278
+ C extends InferContext<OPT[K]> = InferContext<OPT[K]>
279
+ >(
280
+ method: M,
281
+ path: K,
282
+ options?: FetchRequestOptions<C["body"], C["query"], C["params"]>
283
+ ): Promise<
284
+ FetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>
285
+ > {
286
+ return this.executeRequest(method, path, options);
287
+ }
288
+
289
+ // Overload for endpoints WITH required fields (body/query/params)
290
+ get<
291
+ API extends InferredAPI<R> = InferredAPI<R>,
292
+ OPT extends Prettify<UnionToIntersection<MethodOptions<API, "GET">>> = Prettify<UnionToIntersection<MethodOptions<API, "GET">>>,
293
+ K extends keyof OPT = keyof OPT,
294
+ C extends InferContext<OPT[K]> = InferContext<OPT[K]>
295
+ >(
296
+ path: IsAnyOrAnyIndexed<R> extends true ? string : (HasRequired<C> extends true ? K : never),
297
+ options: IsAnyOrAnyIndexed<R> extends true
298
+ ? FetchRequestOptions<any, any, any>
299
+ : WithRequired<FetchRequestOptions<C["body"], C["query"], C["params"]>, keyof RequiredOptionKeys<C>>
300
+ ): Promise<
301
+ FetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>
302
+ >;
303
+
304
+ // Overload for endpoints WITHOUT required fields (permissive when R is 'any')
305
+ get<
306
+ API extends InferredAPI<R> = InferredAPI<R>,
307
+ OPT extends Prettify<UnionToIntersection<MethodOptions<API, "GET">>> = Prettify<UnionToIntersection<MethodOptions<API, "GET">>>,
308
+ K extends keyof OPT = keyof OPT,
309
+ C extends InferContext<OPT[K]> = InferContext<OPT[K]>
310
+ >(
311
+ path: IsAnyOrAnyIndexed<R> extends true ? string : (HasRequired<C> extends false ? K : never),
312
+ options?: IsAnyOrAnyIndexed<R> extends true
313
+ ? FetchRequestOptions<any, any, any>
314
+ : FetchRequestOptions<C["body"], C["query"], C["params"]>
315
+ ): Promise<
316
+ FetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>
317
+ >;
318
+
319
+ get(path: any, options?: any): Promise<any> {
320
+ return this.request("GET", path, options);
321
+ }
322
+
323
+ // Overload for endpoints WITH required fields (body/query/params)
324
+ post<
325
+ API extends InferredAPI<R> = InferredAPI<R>,
326
+ OPT extends Prettify<UnionToIntersection<MethodOptions<API, "POST">>> = Prettify<UnionToIntersection<MethodOptions<API, "POST">>>,
327
+ K extends keyof OPT = keyof OPT,
328
+ C extends InferContext<OPT[K]> = InferContext<OPT[K]>
329
+ >(
330
+ path: (IsAnyOrAnyIndexed<R> extends true ? string : never) | (IsAny<API> extends true ? string : never) | (HasRequired<C> extends true ? K : never),
331
+ options: IsAnyOrAnyIndexed<R> extends true ? FetchRequestOptions<any, any, any> : (IsAny<API> extends true
332
+ ? FetchRequestOptions<any, any, any>
333
+ : WithRequired<FetchRequestOptions<C["body"], C["query"], C["params"]>, keyof RequiredOptionKeys<C>>)
334
+ ): Promise<
335
+ FetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>
336
+ >;
337
+
338
+ // Overload for endpoints WITHOUT required fields (permissive when R is 'any')
339
+ post<
340
+ API extends InferredAPI<R> = InferredAPI<R>,
341
+ OPT extends Prettify<UnionToIntersection<MethodOptions<API, "POST">>> = Prettify<UnionToIntersection<MethodOptions<API, "POST">>>,
342
+ K extends keyof OPT = keyof OPT,
343
+ C extends InferContext<OPT[K]> = InferContext<OPT[K]>
344
+ >(
345
+ path: (IsAnyOrAnyIndexed<R> extends true ? string : never) | (IsAny<API> extends true ? string : never) | (HasRequired<C> extends false ? K : never),
346
+ options?: IsAnyOrAnyIndexed<R> extends true ? FetchRequestOptions<any, any, any> : (IsAny<API> extends true
347
+ ? FetchRequestOptions<any, any, any>
348
+ : FetchRequestOptions<C["body"], C["query"], C["params"]>)
349
+ ): Promise<
350
+ FetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>
351
+ >;
352
+
353
+ post(path: any, options?: any): Promise<any> {
354
+ return this.request("POST", path, options);
355
+ }
356
+
357
+ // Overload for endpoints WITH required fields (body/query/params)
358
+ delete<
359
+ API extends InferredAPI<R> = InferredAPI<R>,
360
+ OPT extends Prettify<UnionToIntersection<MethodOptions<API, "DELETE">>> = Prettify<UnionToIntersection<MethodOptions<API, "DELETE">>>,
361
+ K extends keyof OPT = keyof OPT,
362
+ C extends InferContext<OPT[K]> = InferContext<OPT[K]>
363
+ >(
364
+ path: IsAnyOrAnyIndexed<R> extends true ? string : (HasRequired<C> extends true ? K : never),
365
+ options: IsAnyOrAnyIndexed<R> extends true
366
+ ? FetchRequestOptions<any, any, any>
367
+ : WithRequired<FetchRequestOptions<C["body"], C["query"], C["params"]>, keyof RequiredOptionKeys<C>>
368
+ ): Promise<
369
+ FetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>
370
+ >;
371
+
372
+ // Overload for endpoints WITHOUT required fields (permissive when R is 'any')
373
+ delete<
374
+ API extends InferredAPI<R> = InferredAPI<R>,
375
+ OPT extends Prettify<UnionToIntersection<MethodOptions<API, "DELETE">>> = Prettify<UnionToIntersection<MethodOptions<API, "DELETE">>>,
376
+ K extends keyof OPT = keyof OPT,
377
+ C extends InferContext<OPT[K]> = InferContext<OPT[K]>
378
+ >(
379
+ path: IsAnyOrAnyIndexed<R> extends true ? string : (HasRequired<C> extends false ? K : never),
380
+ options?: IsAnyOrAnyIndexed<R> extends true
381
+ ? FetchRequestOptions<any, any, any>
382
+ : FetchRequestOptions<C["body"], C["query"], C["params"]>
383
+ ): Promise<
384
+ FetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>
385
+ >;
386
+
387
+ delete(path: any, options?: any): Promise<any> {
388
+ return this.request("DELETE", path, options);
389
+ }
390
+
391
+ // Overload for endpoints WITH required fields (body/query/params)
392
+ patch<
393
+ API extends InferredAPI<R> = InferredAPI<R>,
394
+ OPT extends Prettify<UnionToIntersection<MethodOptions<API, "PATCH">>> = Prettify<UnionToIntersection<MethodOptions<API, "PATCH">>>,
395
+ K extends keyof OPT = keyof OPT,
396
+ C extends InferContext<OPT[K]> = InferContext<OPT[K]>
397
+ >(
398
+ path: IsAnyOrAnyIndexed<R> extends true ? string : (HasRequired<C> extends true ? K : never),
399
+ options: IsAnyOrAnyIndexed<R> extends true
400
+ ? FetchRequestOptions<any, any, any>
401
+ : WithRequired<FetchRequestOptions<C["body"], C["query"], C["params"]>, keyof RequiredOptionKeys<C>>
402
+ ): Promise<
403
+ FetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>
404
+ >;
405
+
406
+ // Overload for endpoints WITHOUT required fields (permissive when R is 'any')
407
+ patch<
408
+ API extends InferredAPI<R> = InferredAPI<R>,
409
+ OPT extends Prettify<UnionToIntersection<MethodOptions<API, "PATCH">>> = Prettify<UnionToIntersection<MethodOptions<API, "PATCH">>>,
410
+ K extends keyof OPT = keyof OPT,
411
+ C extends InferContext<OPT[K]> = InferContext<OPT[K]>
412
+ >(
413
+ path: IsAnyOrAnyIndexed<R> extends true ? string : (HasRequired<C> extends false ? K : never),
414
+ options?: IsAnyOrAnyIndexed<R> extends true
415
+ ? FetchRequestOptions<any, any, any>
416
+ : FetchRequestOptions<C["body"], C["query"], C["params"]>
417
+ ): Promise<
418
+ FetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>
419
+ >;
420
+
421
+ patch(path: any, options?: any): Promise<any> {
422
+ return this.request("PATCH", path, options);
423
+ }
424
+
425
+ // Overload for endpoints WITH required fields (body/query/params)
426
+ put<
427
+ API extends InferredAPI<R> = InferredAPI<R>,
428
+ OPT extends Prettify<UnionToIntersection<MethodOptions<API, "PUT">>> = Prettify<UnionToIntersection<MethodOptions<API, "PUT">>>,
429
+ K extends keyof OPT = keyof OPT,
430
+ C extends InferContext<OPT[K]> = InferContext<OPT[K]>
431
+ >(
432
+ path: IsAnyOrAnyIndexed<R> extends true ? string : (HasRequired<C> extends true ? K : never),
433
+ options: IsAnyOrAnyIndexed<R> extends true
434
+ ? FetchRequestOptions<any, any, any>
435
+ : WithRequired<FetchRequestOptions<C["body"], C["query"], C["params"]>, keyof RequiredOptionKeys<C>>
436
+ ): Promise<
437
+ FetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>
438
+ >;
439
+
440
+ // Overload for endpoints WITHOUT required fields (permissive when R is 'any')
441
+ put<
442
+ API extends InferredAPI<R> = InferredAPI<R>,
443
+ OPT extends Prettify<UnionToIntersection<MethodOptions<API, "PUT">>> = Prettify<UnionToIntersection<MethodOptions<API, "PUT">>>,
444
+ K extends keyof OPT = keyof OPT,
445
+ C extends InferContext<OPT[K]> = InferContext<OPT[K]>
446
+ >(
447
+ path: IsAnyOrAnyIndexed<R> extends true ? string : (HasRequired<C> extends false ? K : never),
448
+ options?: IsAnyOrAnyIndexed<R> extends true
449
+ ? FetchRequestOptions<any, any, any>
450
+ : FetchRequestOptions<C["body"], C["query"], C["params"]>
451
+ ): Promise<
452
+ FetchResponse<Awaited<ReturnType<OPT[K] extends Endpoint ? OPT[K] : never>>>
453
+ >;
454
+
455
+ put(path: any, options?: any): Promise<any> {
456
+ return this.request("PUT", path, options);
457
+ }
458
+
459
+ protected async executeRequest<M extends HTTPMethod>(
460
+ method: M,
461
+ path: any,
462
+ requestOptions?: any
463
+ ): Promise<any> {
464
+ //
465
+ // FIXME: if FormData is provided, merging "baseOptions.body" with
466
+ // "options.body" will not work as intended
467
+ //
468
+ let body = (this.options.body)
469
+ ? { ...this.options.body, ...(requestOptions?.body as object || {}) }
470
+ : requestOptions?.body;
471
+
472
+ const query = (this.options.query)
473
+ ? { ...this.options.query, ...(requestOptions?.query as object || {}) }
474
+ : requestOptions?.query;
475
+
476
+ const params = (this.options.params)
477
+ ? { ...this.options.params, ...(requestOptions?.params as object || {}) }
478
+ : requestOptions?.params;
479
+
480
+ const headers = new Headers(
481
+ (this.options.headers)
482
+ ? { ...this.options.headers, ...(requestOptions?.headers || {}) }
483
+ : requestOptions?.headers
484
+ );
485
+
486
+ // Stringify JSON-serializable objects for fetch() body
487
+ if (isJSONSerializable(body) && typeof body === 'object' && body !== null) {
488
+ if (!headers.has("content-type")) {
489
+ headers.set("content-type", "application/json");
490
+ }
491
+ for (const [key, value] of Object.entries(body)) {
492
+ if (value instanceof Date) {
493
+ body[key] = value.toISOString();
494
+ }
495
+ }
496
+ body = JSON.stringify(body);
497
+ }
498
+
499
+ const mergedOptions = {
500
+ credentials: requestOptions?.credentials || "include",
501
+ ...this.options,
502
+ ...requestOptions,
503
+ query,
504
+ params,
505
+ headers,
506
+ body,
507
+ method,
508
+ };
509
+
510
+ const url = getURLWithQueryParams(this.sdk['getHttpEndpoint'](path.toString()), mergedOptions);
511
+
512
+ const response = await fetch(url, mergedOptions);
513
+ const contentType = response.headers.get("content-type");
514
+
515
+ let data: any;
516
+ let error = null;
517
+
518
+ // TODO: improve content-type detection here!
519
+ if (contentType?.indexOf("json")) {
520
+ data = await response.json();
521
+
522
+ } else if (contentType?.indexOf("text")) {
523
+ data = await response.text();
524
+
525
+ } else {
526
+ data = await response.blob();
527
+ }
528
+
529
+ if (!response.ok) {
530
+ // TODO: throw error here?!
531
+ error = data;
532
+ data = null;
533
+ }
534
+
535
+ return {
536
+ ok: response.ok,
537
+ headers: response.headers,
538
+ data,
539
+ error,
540
+ status: response.status,
541
+ statusText: response.statusText,
542
+ response,
543
+ };
544
+ }
545
+ }
@@ -0,0 +1,67 @@
1
+ import * as httpie from "@colyseus/httpie";
2
+
3
+ import { ColyseusSDK } from "./Client.ts";
4
+ import { AbortError, ServerError } from "./errors/Errors.ts";
5
+
6
+ export class HTTP {
7
+ public authToken: string | undefined;
8
+
9
+ constructor(
10
+ protected sdk: ColyseusSDK,
11
+ public headers: { [id: string]: string } = {},
12
+ ) {}
13
+
14
+ public get<T = any>(path: string, options: Partial<httpie.Options> = {}): Promise<httpie.Response<T>> {
15
+ return this.request("get", path, options);
16
+ }
17
+
18
+ public post<T = any>(path: string, options: Partial<httpie.Options> = {}): Promise<httpie.Response<T>> {
19
+ return this.request("post", path, options);
20
+ }
21
+
22
+ public del<T = any>(path: string, options: Partial<httpie.Options> = {}): Promise<httpie.Response<T>> {
23
+ return this.request("del", path, options);
24
+ }
25
+
26
+ public put<T = any>(path: string, options: Partial<httpie.Options> = {}): Promise<httpie.Response<T>> {
27
+ return this.request("put", path, options);
28
+ }
29
+
30
+ protected request(method: "get" | "post" | "put" | "del", path: string, options: Partial<httpie.Options> = {}): Promise<httpie.Response> {
31
+ return httpie[method](this.sdk['getHttpEndpoint'](path), this.getOptions(options)).catch((e: any) => {
32
+ if (e.aborted) {
33
+ throw new AbortError("Request aborted");
34
+ }
35
+
36
+ const status = e.statusCode; // || -1
37
+ const message = e.data?.error || e.statusMessage || e.message; // || "offline"
38
+
39
+ if (!status && !message) {
40
+ throw e;
41
+ }
42
+
43
+ throw new ServerError(status, message);
44
+ });
45
+ }
46
+
47
+ protected getOptions(options: Partial<httpie.Options>) {
48
+ // merge default custom headers with user headers
49
+ options.headers = Object.assign({}, this.headers, options.headers);
50
+
51
+ if (this.authToken) {
52
+ options.headers['Authorization'] = `Bearer ${this.authToken}`;
53
+ }
54
+
55
+ if (typeof (cc) !== 'undefined' && cc.sys && cc.sys.isNative) {
56
+ //
57
+ // Workaround for Cocos Creator on Native platform
58
+ // "Cannot set property withCredentials of #<XMLHttpRequest> which has only a getter"
59
+ //
60
+ } else {
61
+ // always include credentials
62
+ options.withCredentials = true;
63
+ }
64
+
65
+ return options;
66
+ }
67
+ }
@@ -0,0 +1,25 @@
1
+ // Use codes between 0~127 for lesser throughput (1 byte)
2
+ export enum Protocol {
3
+ // Room-related (10~19)
4
+ HANDSHAKE = 9,
5
+ JOIN_ROOM = 10,
6
+ ERROR = 11,
7
+ LEAVE_ROOM = 12,
8
+ ROOM_DATA = 13,
9
+ ROOM_STATE = 14,
10
+ ROOM_STATE_PATCH = 15,
11
+ ROOM_DATA_SCHEMA = 16,
12
+ ROOM_DATA_BYTES = 17,
13
+ PING = 18,
14
+ }
15
+
16
+ export enum ErrorCode {
17
+ MATCHMAKE_NO_HANDLER = 520,
18
+ MATCHMAKE_INVALID_CRITERIA = 521,
19
+ MATCHMAKE_INVALID_ROOM_ID = 522,
20
+ MATCHMAKE_UNHANDLED = 523,
21
+ MATCHMAKE_EXPIRED = 524,
22
+
23
+ AUTH_FAILED = 525,
24
+ APPLICATION_ERROR = 526,
25
+ }