krawlet-js 1.0.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.cjs ADDED
@@ -0,0 +1,615 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AddressesResource: () => AddressesResource,
24
+ ErrorCode: () => ErrorCode,
25
+ HealthResource: () => HealthResource,
26
+ ItemsResource: () => ItemsResource,
27
+ KrawletClient: () => KrawletClient,
28
+ KrawletError: () => KrawletError,
29
+ PlayersResource: () => PlayersResource,
30
+ ReportsResource: () => ReportsResource,
31
+ ShopsResource: () => ShopsResource,
32
+ StorageResource: () => StorageResource
33
+ });
34
+ module.exports = __toCommonJS(index_exports);
35
+
36
+ // src/error.ts
37
+ var KrawletError = class _KrawletError extends Error {
38
+ constructor(message, code, statusCode, requestId, details, response) {
39
+ super(message);
40
+ this.name = "KrawletError";
41
+ this.code = code;
42
+ this.statusCode = statusCode;
43
+ this.requestId = requestId;
44
+ this.details = details;
45
+ this.response = response;
46
+ if (Error.captureStackTrace) {
47
+ Error.captureStackTrace(this, _KrawletError);
48
+ }
49
+ }
50
+ /**
51
+ * Check if this is a client error (4xx)
52
+ */
53
+ isClientError() {
54
+ return this.statusCode >= 400 && this.statusCode < 500;
55
+ }
56
+ /**
57
+ * Check if this is a server error (5xx)
58
+ */
59
+ isServerError() {
60
+ return this.statusCode >= 500;
61
+ }
62
+ /**
63
+ * Check if this is a rate limit error
64
+ */
65
+ isRateLimitError() {
66
+ return this.statusCode === 429 || this.code === "RATE_LIMIT_EXCEEDED";
67
+ }
68
+ };
69
+
70
+ // src/http-client.ts
71
+ var HttpClient = class {
72
+ constructor(config) {
73
+ this.config = {
74
+ baseUrl: config.baseUrl,
75
+ apiKey: config.apiKey || "",
76
+ timeout: config.timeout || 3e4,
77
+ headers: config.headers || {},
78
+ enableRetry: config.enableRetry !== false,
79
+ maxRetries: config.maxRetries || 3,
80
+ retryDelay: config.retryDelay || 1e3
81
+ };
82
+ }
83
+ /**
84
+ * Get the last known rate limit information
85
+ */
86
+ getRateLimit() {
87
+ return this.lastRateLimit;
88
+ }
89
+ /**
90
+ * Make an HTTP request
91
+ */
92
+ async request(path, options = {}) {
93
+ const url = this.buildUrl(path, options.params);
94
+ const headers = this.buildHeaders(options.headers, options.apiKey);
95
+ let lastError;
96
+ let attempt = 0;
97
+ const maxAttempts = this.config.enableRetry ? this.config.maxRetries + 1 : 1;
98
+ while (attempt < maxAttempts) {
99
+ try {
100
+ const controller = new AbortController();
101
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
102
+ const response = await fetch(url, {
103
+ method: options.method || "GET",
104
+ headers,
105
+ body: options.body ? JSON.stringify(options.body) : void 0,
106
+ signal: controller.signal
107
+ });
108
+ clearTimeout(timeoutId);
109
+ this.extractRateLimit(response);
110
+ if (!response.ok) {
111
+ await this.handleErrorResponse(response);
112
+ }
113
+ const data = await response.json();
114
+ return data;
115
+ } catch (error) {
116
+ lastError = error;
117
+ if (error instanceof KrawletError && error.isClientError() && !error.isRateLimitError()) {
118
+ throw error;
119
+ }
120
+ if (attempt < maxAttempts - 1 && this.shouldRetry(error)) {
121
+ const delay = this.calculateRetryDelay(attempt);
122
+ await this.sleep(delay);
123
+ attempt++;
124
+ continue;
125
+ }
126
+ throw error;
127
+ }
128
+ }
129
+ throw lastError || new Error("Request failed after retries");
130
+ }
131
+ /**
132
+ * Build the full URL with query parameters
133
+ */
134
+ buildUrl(path, params) {
135
+ const url = new URL(path, this.config.baseUrl);
136
+ if (params) {
137
+ Object.entries(params).forEach(([key, value]) => {
138
+ if (value !== void 0) {
139
+ url.searchParams.append(key, String(value));
140
+ }
141
+ });
142
+ }
143
+ return url.toString();
144
+ }
145
+ /**
146
+ * Build request headers
147
+ */
148
+ buildHeaders(customHeaders, apiKey) {
149
+ const headers = {
150
+ "Content-Type": "application/json",
151
+ ...this.config.headers,
152
+ ...customHeaders
153
+ };
154
+ const key = apiKey || this.config.apiKey;
155
+ if (key) {
156
+ headers["Authorization"] = `Bearer ${key}`;
157
+ }
158
+ return headers;
159
+ }
160
+ /**
161
+ * Extract rate limit information from response headers
162
+ */
163
+ extractRateLimit(response) {
164
+ const limit = response.headers.get("X-RateLimit-Limit");
165
+ const remaining = response.headers.get("X-RateLimit-Remaining");
166
+ const reset = response.headers.get("X-RateLimit-Reset");
167
+ if (limit && remaining && reset) {
168
+ this.lastRateLimit = {
169
+ limit: parseInt(limit, 10),
170
+ remaining: parseInt(remaining, 10),
171
+ reset: parseInt(reset, 10)
172
+ };
173
+ }
174
+ }
175
+ /**
176
+ * Handle error responses
177
+ */
178
+ async handleErrorResponse(response) {
179
+ let errorData;
180
+ try {
181
+ errorData = await response.json();
182
+ } catch {
183
+ throw new KrawletError(
184
+ `HTTP ${response.status}: ${response.statusText}`,
185
+ "UNKNOWN_ERROR",
186
+ response.status
187
+ );
188
+ }
189
+ throw new KrawletError(
190
+ errorData.error.message,
191
+ errorData.error.code,
192
+ response.status,
193
+ errorData.meta.requestId,
194
+ errorData.error.details,
195
+ errorData
196
+ );
197
+ }
198
+ /**
199
+ * Determine if a request should be retried
200
+ */
201
+ shouldRetry(error) {
202
+ if (error instanceof KrawletError) {
203
+ return error.isServerError() || error.isRateLimitError();
204
+ }
205
+ if (error instanceof Error) {
206
+ return error.name === "AbortError" || error.message.includes("fetch");
207
+ }
208
+ return false;
209
+ }
210
+ /**
211
+ * Calculate retry delay with exponential backoff
212
+ */
213
+ calculateRetryDelay(attempt) {
214
+ return this.config.retryDelay * Math.pow(2, attempt);
215
+ }
216
+ /**
217
+ * Sleep for a given duration
218
+ */
219
+ sleep(ms) {
220
+ return new Promise((resolve) => setTimeout(resolve, ms));
221
+ }
222
+ };
223
+
224
+ // src/resources/health.ts
225
+ var HealthResource = class {
226
+ constructor(client) {
227
+ this.client = client;
228
+ }
229
+ /**
230
+ * Basic health check to verify API is operational
231
+ * @returns Health status information
232
+ */
233
+ async check() {
234
+ const response = await this.client.request("/v1/health");
235
+ return response.data;
236
+ }
237
+ /**
238
+ * Detailed health check with system information
239
+ * @returns Detailed health status with system metrics
240
+ */
241
+ async detailed() {
242
+ const response = await this.client.request("/v1/health/detailed");
243
+ return response.data;
244
+ }
245
+ };
246
+
247
+ // src/resources/players.ts
248
+ var PlayersResource = class {
249
+ constructor(client) {
250
+ this.client = client;
251
+ }
252
+ /**
253
+ * Retrieve all players
254
+ * @returns Array of all players
255
+ */
256
+ async getAll() {
257
+ const response = await this.client.request("/v1/players");
258
+ return response.data;
259
+ }
260
+ /**
261
+ * Retrieve players by Kromer addresses
262
+ * @param addresses - Array of Kromer addresses
263
+ * @returns Array of matching players
264
+ */
265
+ async getByAddresses(addresses) {
266
+ const response = await this.client.request("/v1/players", {
267
+ params: {
268
+ addresses: addresses.join(",")
269
+ }
270
+ });
271
+ return response.data;
272
+ }
273
+ /**
274
+ * Retrieve players by Minecraft names
275
+ * @param names - Array of player names (case-insensitive)
276
+ * @returns Array of matching players
277
+ */
278
+ async getByNames(names) {
279
+ const response = await this.client.request("/v1/players", {
280
+ params: {
281
+ names: names.join(",")
282
+ }
283
+ });
284
+ return response.data;
285
+ }
286
+ /**
287
+ * Retrieve players by Minecraft UUIDs
288
+ * @param uuids - Array of player UUIDs
289
+ * @returns Array of matching players
290
+ */
291
+ async getByUuids(uuids) {
292
+ const response = await this.client.request("/v1/players", {
293
+ params: {
294
+ uuids: uuids.join(",")
295
+ }
296
+ });
297
+ return response.data;
298
+ }
299
+ };
300
+
301
+ // src/resources/shops.ts
302
+ var ShopsResource = class {
303
+ constructor(client) {
304
+ this.client = client;
305
+ }
306
+ /**
307
+ * Retrieve all shops with their items and addresses
308
+ * @returns Array of all shops
309
+ */
310
+ async getAll() {
311
+ const response = await this.client.request("/v1/shops");
312
+ return response.data;
313
+ }
314
+ /**
315
+ * Retrieve a specific shop by ID
316
+ * @param id - Shop ID (computer ID)
317
+ * @returns Shop data
318
+ * @throws KrawletError with SHOP_NOT_FOUND if shop doesn't exist
319
+ */
320
+ async get(id) {
321
+ const response = await this.client.request(`/v1/shops/${id}`);
322
+ return response.data;
323
+ }
324
+ /**
325
+ * Retrieve all items for a specific shop
326
+ * @param id - Shop ID
327
+ * @returns Array of items in the shop
328
+ */
329
+ async getItems(id) {
330
+ const response = await this.client.request(`/v1/shops/${id}/items`);
331
+ return response.data;
332
+ }
333
+ /**
334
+ * Create or update a shop (requires ShopSync authentication)
335
+ * @param data - Shop sync data
336
+ * @param token - ShopSync API token
337
+ * @returns Success message
338
+ * @throws KrawletError with VALIDATION_ERROR if data is invalid
339
+ * @throws KrawletError with UNAUTHORIZED if not authenticated
340
+ */
341
+ async update(data, token) {
342
+ const response = await this.client.request("/v1/shops", {
343
+ method: "POST",
344
+ body: data,
345
+ apiKey: token
346
+ });
347
+ return response.data;
348
+ }
349
+ };
350
+
351
+ // src/resources/items.ts
352
+ var ItemsResource = class {
353
+ constructor(client) {
354
+ this.client = client;
355
+ }
356
+ /**
357
+ * Retrieve all item listings with prices across all shops
358
+ * @returns Array of all items
359
+ */
360
+ async getAll() {
361
+ const response = await this.client.request("/v1/items");
362
+ return response.data;
363
+ }
364
+ };
365
+
366
+ // src/resources/addresses.ts
367
+ var AddressesResource = class {
368
+ constructor(client) {
369
+ this.client = client;
370
+ }
371
+ /**
372
+ * Retrieve all known Kromer addresses
373
+ * @returns Array of all known addresses
374
+ */
375
+ async getAll() {
376
+ const response = await this.client.request("/v1/addresses");
377
+ return response.data;
378
+ }
379
+ };
380
+
381
+ // src/resources/storage.ts
382
+ var StorageResource = class {
383
+ constructor(client) {
384
+ this.client = client;
385
+ }
386
+ /**
387
+ * Retrieve the last stored ender storage data
388
+ * @returns Storage data and retrieval timestamp
389
+ * @throws KrawletError with NOT_FOUND if no data has been stored yet
390
+ */
391
+ async get() {
392
+ const response = await this.client.request("/v1/storage");
393
+ return response.data;
394
+ }
395
+ /**
396
+ * Store ender storage data (requires authentication)
397
+ * @param data - Any JSON object to store
398
+ * @param token - Ender Storage API token
399
+ * @returns Success message and timestamp
400
+ * @throws KrawletError with BAD_REQUEST if data is invalid
401
+ * @throws KrawletError with UNAUTHORIZED if not authenticated
402
+ */
403
+ async set(data, token) {
404
+ const response = await this.client.request(
405
+ "/v1/storage",
406
+ {
407
+ method: "POST",
408
+ body: data,
409
+ apiKey: token
410
+ }
411
+ );
412
+ return response.data;
413
+ }
414
+ };
415
+
416
+ // src/resources/reports.ts
417
+ var ReportsResource = class {
418
+ constructor(client) {
419
+ this.client = client;
420
+ }
421
+ /**
422
+ * Retrieve overall statistics for ShopSync reports
423
+ * @returns Statistics object
424
+ */
425
+ async getStats() {
426
+ const response = await this.client.request("/v1/reports/stats");
427
+ return response.data;
428
+ }
429
+ /**
430
+ * Retrieve recent validation failures
431
+ * @param options - Query options
432
+ * @param options.limit - Maximum records to return (default: 50)
433
+ * @returns Validation failure records
434
+ */
435
+ async getValidationFailures(options) {
436
+ const response = await this.client.request("/v1/reports/validation-failures", {
437
+ params: {
438
+ limit: options?.limit || 50
439
+ }
440
+ });
441
+ return response.data;
442
+ }
443
+ /**
444
+ * Retrieve recent successful ShopSync posts
445
+ * @param options - Query options
446
+ * @param options.limit - Maximum records to return (default: 50)
447
+ * @returns Successful post records
448
+ */
449
+ async getSuccessfulPosts(options) {
450
+ const response = await this.client.request("/v1/reports/successful-posts", {
451
+ params: {
452
+ limit: options?.limit || 50
453
+ }
454
+ });
455
+ return response.data;
456
+ }
457
+ /**
458
+ * Retrieve recent shop change events
459
+ * @param options - Query options
460
+ * @param options.limit - Maximum records to return (default: 50)
461
+ * @param options.shopId - Filter by shop ID
462
+ * @returns Shop change event records
463
+ */
464
+ async getShopChanges(options) {
465
+ const response = await this.client.request("/v1/reports/shop-changes", {
466
+ params: {
467
+ limit: options?.limit || 50,
468
+ shopId: options?.shopId
469
+ }
470
+ });
471
+ return response.data;
472
+ }
473
+ /**
474
+ * Retrieve recent item change events
475
+ * @param options - Query options
476
+ * @param options.limit - Maximum records to return (default: 50)
477
+ * @param options.shopId - Filter by shop ID
478
+ * @returns Item change event records
479
+ */
480
+ async getItemChanges(options) {
481
+ const response = await this.client.request("/v1/reports/item-changes", {
482
+ params: {
483
+ limit: options?.limit || 50,
484
+ shopId: options?.shopId
485
+ }
486
+ });
487
+ return response.data;
488
+ }
489
+ /**
490
+ * Retrieve shop change logs from the database
491
+ * @param options - Query options including pagination and filtering
492
+ * @returns Shop change logs
493
+ */
494
+ async getShopChangeLogs(options) {
495
+ const response = await this.client.request("/v1/reports/shop-change-logs", {
496
+ params: {
497
+ limit: options?.limit,
498
+ offset: options?.offset,
499
+ shopId: options?.shopId,
500
+ since: options?.since,
501
+ until: options?.until
502
+ }
503
+ });
504
+ return response.data;
505
+ }
506
+ /**
507
+ * Retrieve item change logs from the database
508
+ * @param options - Query options including pagination and filtering
509
+ * @returns Item change logs
510
+ */
511
+ async getItemChangeLogs(options) {
512
+ const response = await this.client.request("/v1/reports/item-change-logs", {
513
+ params: {
514
+ limit: options?.limit,
515
+ offset: options?.offset,
516
+ shopId: options?.shopId,
517
+ since: options?.since,
518
+ until: options?.until
519
+ }
520
+ });
521
+ return response.data;
522
+ }
523
+ /**
524
+ * Retrieve price change logs from the database
525
+ * @param options - Query options including pagination and filtering
526
+ * @returns Price change logs
527
+ */
528
+ async getPriceChangeLogs(options) {
529
+ const response = await this.client.request("/v1/reports/price-change-logs", {
530
+ params: {
531
+ limit: options?.limit,
532
+ offset: options?.offset,
533
+ shopId: options?.shopId,
534
+ since: options?.since,
535
+ until: options?.until
536
+ }
537
+ });
538
+ return response.data;
539
+ }
540
+ /**
541
+ * Retrieve a specific report record by ID
542
+ * @param id - Report record ID
543
+ * @returns Report record
544
+ * @throws KrawletError with NOT_FOUND if record doesn't exist
545
+ */
546
+ async get(id) {
547
+ const response = await this.client.request(`/v1/reports/${id}`);
548
+ return response.data;
549
+ }
550
+ };
551
+
552
+ // src/client.ts
553
+ var KrawletClient = class {
554
+ /**
555
+ * Create a new Krawlet API client
556
+ * @param config - Client configuration options
557
+ */
558
+ constructor(config = {}) {
559
+ const httpConfig = {
560
+ baseUrl: config.baseUrl || "https://api.krawlet.cc",
561
+ apiKey: config.apiKey,
562
+ timeout: config.timeout,
563
+ headers: config.headers,
564
+ enableRetry: config.enableRetry,
565
+ maxRetries: config.maxRetries,
566
+ retryDelay: config.retryDelay
567
+ };
568
+ this.httpClient = new HttpClient(httpConfig);
569
+ this.health = new HealthResource(this.httpClient);
570
+ this.players = new PlayersResource(this.httpClient);
571
+ this.shops = new ShopsResource(this.httpClient);
572
+ this.items = new ItemsResource(this.httpClient);
573
+ this.addresses = new AddressesResource(this.httpClient);
574
+ this.storage = new StorageResource(this.httpClient);
575
+ this.reports = new ReportsResource(this.httpClient);
576
+ }
577
+ /**
578
+ * Get the last known rate limit information
579
+ * @returns Rate limit data or undefined if no requests have been made
580
+ */
581
+ getRateLimit() {
582
+ return this.httpClient.getRateLimit();
583
+ }
584
+ };
585
+
586
+ // src/types.ts
587
+ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
588
+ ErrorCode2["BAD_REQUEST"] = "BAD_REQUEST";
589
+ ErrorCode2["UNAUTHORIZED"] = "UNAUTHORIZED";
590
+ ErrorCode2["FORBIDDEN"] = "FORBIDDEN";
591
+ ErrorCode2["NOT_FOUND"] = "NOT_FOUND";
592
+ ErrorCode2["VALIDATION_ERROR"] = "VALIDATION_ERROR";
593
+ ErrorCode2["RATE_LIMIT_EXCEEDED"] = "RATE_LIMIT_EXCEEDED";
594
+ ErrorCode2["PLAYER_NOT_FOUND"] = "PLAYER_NOT_FOUND";
595
+ ErrorCode2["SHOP_NOT_FOUND"] = "SHOP_NOT_FOUND";
596
+ ErrorCode2["TURTLE_NOT_FOUND"] = "TURTLE_NOT_FOUND";
597
+ ErrorCode2["INVALID_API_KEY"] = "INVALID_API_KEY";
598
+ ErrorCode2["INTERNAL_ERROR"] = "INTERNAL_ERROR";
599
+ ErrorCode2["DATABASE_ERROR"] = "DATABASE_ERROR";
600
+ ErrorCode2["HEALTH_CHECK_FAILED"] = "HEALTH_CHECK_FAILED";
601
+ return ErrorCode2;
602
+ })(ErrorCode || {});
603
+ // Annotate the CommonJS export names for ESM import in node:
604
+ 0 && (module.exports = {
605
+ AddressesResource,
606
+ ErrorCode,
607
+ HealthResource,
608
+ ItemsResource,
609
+ KrawletClient,
610
+ KrawletError,
611
+ PlayersResource,
612
+ ReportsResource,
613
+ ShopsResource,
614
+ StorageResource
615
+ });