skikrumb-api 2.1.3 → 2.1.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/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ declare class SkiKrumbRealtimeClient {
10
10
  private reconnectAttempts;
11
11
  private maxReconnectAttempts;
12
12
  private reconnectDelay;
13
+ private maxReconnectDelay;
13
14
  constructor(sessionToken: string, userId: string, url: string, supabaseToken?: string);
14
15
  connect(): Promise<void>;
15
16
  private connectToWebSocket;
@@ -33,6 +34,7 @@ export declare const skiKrumb: (options?: {
33
34
  readApiKeys: () => Promise<apiKeys>;
34
35
  readDeviceDailyDistance: (deviceSerialNumber: string) => Promise<unknown>;
35
36
  readDeviceData: (deviceSerialNumber: string, limit?: number) => Promise<unknown>;
37
+ readDataForDevices: (query: QueryDevice) => Promise<Device[]>;
36
38
  readGateways: () => Promise<Gateways>;
37
39
  readShippingRates: (rateRequest: RateRequest) => Promise<Rates>;
38
40
  sendMobileLocation: (payload: {
@@ -44,6 +46,30 @@ export declare const skiKrumb: (options?: {
44
46
  createSharingLink: (shareRequest: ShareLinkRequest) => Promise<ShareLinkResponse>;
45
47
  previewSharingLink: (token: string) => Promise<SharePreviewResponse>;
46
48
  acceptSharingLink: (token: string, acceptRequest: ShareAcceptRequest) => Promise<ShareAcceptResponse>;
49
+ shareBack: (targetAccountId: string, durationDays?: number, originalFamilyId?: string) => Promise<any>;
50
+ deleteFamilyShare: (familyId: string) => Promise<{
51
+ success: boolean;
52
+ message?: string;
53
+ error?: any;
54
+ }>;
55
+ updateFamilyShare: (familyId: string, profiles: string[]) => Promise<{
56
+ success: boolean;
57
+ data?: any;
58
+ error?: any;
59
+ }>;
60
+ getReplayData: (serialNumber: string, date: string, size?: number) => Promise<any>;
61
+ getDailyStats: (serialNumber: string, date: string) => Promise<any>;
62
+ getAccessibleProfiles: () => Promise<{
63
+ success: boolean;
64
+ accessibleProfiles: any[];
65
+ profilesCount: number;
66
+ accessibleRegistrations: any[];
67
+ }>;
68
+ getLatestDeviceData: (serialNumber: string) => Promise<{
69
+ success: boolean;
70
+ data?: any;
71
+ error?: any;
72
+ }>;
47
73
  createRealtimeClient: (userId: string, sessionToken?: string, supabaseToken?: string) => SkiKrumbRealtimeClient;
48
74
  };
49
75
  export type { Device, Gateways, QueryDevice, RateRequest, Rates, apiKeys, ExternalUserRequest, ExternalUserResponse, ShareLinkRequest, ShareLinkResponse, SharePreviewResponse, ShareAcceptRequest, ShareAcceptResponse, };
package/dist/index.js CHANGED
@@ -5,8 +5,9 @@ class SkiKrumbRealtimeClient {
5
5
  this.listeners = {};
6
6
  this.isConnecting = false;
7
7
  this.reconnectAttempts = 0;
8
- this.maxReconnectAttempts = 5;
9
- this.reconnectDelay = 1000;
8
+ this.maxReconnectAttempts = Infinity; // Never stop trying
9
+ this.reconnectDelay = 1000; // 1 second base
10
+ this.maxReconnectDelay = 300000; // 5 minutes max
10
11
  this.sessionToken = sessionToken;
11
12
  this.supabaseToken = supabaseToken;
12
13
  this.userId = userId;
@@ -130,12 +131,8 @@ class SkiKrumbRealtimeClient {
130
131
  this.emit('disconnected', {
131
132
  reason: event.reason || 'Connection closed',
132
133
  });
133
- if (this.reconnectAttempts < this.maxReconnectAttempts) {
134
- this.scheduleReconnect();
135
- }
136
- else {
137
- this.emit('max_reconnect_attempts_reached');
138
- }
134
+ // Always try to reconnect with smart backoff
135
+ this.scheduleReconnect();
139
136
  }
140
137
  };
141
138
  }
@@ -147,12 +144,13 @@ class SkiKrumbRealtimeClient {
147
144
  }
148
145
  scheduleReconnect() {
149
146
  this.reconnectAttempts++;
150
- const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
147
+ // Exponential backoff with 5-minute cap
148
+ const delay = Math.min(this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1), this.maxReconnectDelay);
149
+ console.log(`🔄 Scheduling reconnect attempt #${this.reconnectAttempts} in ${delay / 1000}s`);
151
150
  setTimeout(() => {
152
151
  this.connect().catch((error) => {
153
- if (this.reconnectAttempts >= this.maxReconnectAttempts) {
154
- this.emit('max_reconnect_attempts_reached');
155
- }
152
+ // Errors are expected during reconnection, will keep trying
153
+ // No max attempts check - keeps trying indefinitely with smart backoff
156
154
  });
157
155
  }, delay);
158
156
  }
@@ -279,6 +277,15 @@ export const skiKrumb = (options = {
279
277
  throw error;
280
278
  }
281
279
  };
280
+ const readDataForDevices = async (query) => {
281
+ if (!query.serial_numbers)
282
+ throw new Error('Serial number is required');
283
+ return request
284
+ .get('devices/data', {
285
+ searchParams: new URLSearchParams({ ...query }),
286
+ })
287
+ .json();
288
+ };
282
289
  const readGateways = async () => {
283
290
  try {
284
291
  const response = await request.get('gateways').json();
@@ -386,12 +393,146 @@ export const skiKrumb = (options = {
386
393
  .json();
387
394
  return response;
388
395
  };
396
+ const patchSharingLink = async (token, body) => {
397
+ const requestWithAuth = ky.create({
398
+ prefixUrl: options.url,
399
+ headers: {
400
+ 'Content-Type': 'application/json',
401
+ Authorization: `Bearer ${options.apiKey}`,
402
+ 'supabase-auth-token': options.supabaseToken || '',
403
+ 'x-client': options.requestedWith,
404
+ },
405
+ });
406
+ const response = await requestWithAuth
407
+ .patch(`sharing/${token}`, {
408
+ json: body,
409
+ })
410
+ .json();
411
+ return response;
412
+ };
413
+ const shareBack = async (targetAccountId, durationDays, originalFamilyId) => {
414
+ const requestWithAuth = ky.create({
415
+ prefixUrl: options.url,
416
+ headers: {
417
+ 'Content-Type': 'application/json',
418
+ Authorization: `Bearer ${options.apiKey}`,
419
+ 'supabase-auth-token': options.supabaseToken || '',
420
+ 'x-client': options.requestedWith,
421
+ },
422
+ });
423
+ const response = await requestWithAuth
424
+ .post('sharing/share-back', {
425
+ json: {
426
+ target_account_id: targetAccountId,
427
+ duration_days: durationDays,
428
+ original_family_id: originalFamilyId,
429
+ },
430
+ })
431
+ .json();
432
+ return response;
433
+ };
434
+ const getReplayData = async (serialNumber, date, size = 10000) => {
435
+ const requestWithAuth = ky.create({
436
+ prefixUrl: options.url,
437
+ headers: {
438
+ 'Content-Type': 'application/json',
439
+ Authorization: `Bearer ${options.apiKey}`,
440
+ 'supabase-auth-token': options.supabaseToken || '',
441
+ 'x-client': options.requestedWith,
442
+ },
443
+ });
444
+ const response = await requestWithAuth
445
+ .get(`data/replay/${serialNumber}`, {
446
+ searchParams: { date, size },
447
+ })
448
+ .json();
449
+ return response;
450
+ };
451
+ const getDailyStats = async (serialNumber, date) => {
452
+ const requestWithAuth = ky.create({
453
+ prefixUrl: options.url,
454
+ headers: {
455
+ 'Content-Type': 'application/json',
456
+ Authorization: `Bearer ${options.apiKey}`,
457
+ 'supabase-auth-token': options.supabaseToken || '',
458
+ 'x-client': options.requestedWith,
459
+ },
460
+ });
461
+ const response = await requestWithAuth
462
+ .get(`data/stats/${serialNumber}`, {
463
+ searchParams: { date },
464
+ })
465
+ .json();
466
+ return response;
467
+ };
468
+ const getAccessibleProfiles = async () => {
469
+ const requestWithAuth = ky.create({
470
+ prefixUrl: options.url,
471
+ headers: {
472
+ 'Content-Type': 'application/json',
473
+ Authorization: `Bearer ${options.apiKey}`,
474
+ 'supabase-auth-token': options.supabaseToken || '',
475
+ 'x-client': options.requestedWith,
476
+ },
477
+ });
478
+ const response = await requestWithAuth
479
+ .get('auth/profiles')
480
+ .json();
481
+ return response;
482
+ };
483
+ const getLatestDeviceData = async (serialNumber) => {
484
+ const requestWithAuth = ky.create({
485
+ prefixUrl: options.url,
486
+ headers: {
487
+ 'Content-Type': 'application/json',
488
+ Authorization: `Bearer ${options.apiKey}`,
489
+ 'supabase-auth-token': options.supabaseToken || '',
490
+ 'x-client': options.requestedWith,
491
+ },
492
+ });
493
+ const response = await requestWithAuth
494
+ .get(`data/latest/${serialNumber}`)
495
+ .json();
496
+ return response;
497
+ };
498
+ const deleteFamilyShare = async (familyId) => {
499
+ const requestWithAuth = ky.create({
500
+ prefixUrl: options.url,
501
+ headers: {
502
+ Authorization: `Bearer ${options.apiKey}`,
503
+ 'supabase-auth-token': options.supabaseToken || '',
504
+ 'x-client': options.requestedWith,
505
+ },
506
+ });
507
+ const response = await requestWithAuth
508
+ .delete(`sharing/family/${familyId}`)
509
+ .json();
510
+ return response;
511
+ };
512
+ const updateFamilyShare = async (familyId, profiles) => {
513
+ const requestWithAuth = ky.create({
514
+ prefixUrl: options.url,
515
+ headers: {
516
+ 'Content-Type': 'application/json',
517
+ Authorization: `Bearer ${options.apiKey}`,
518
+ 'supabase-auth-token': options.supabaseToken || '',
519
+ 'x-client': options.requestedWith,
520
+ },
521
+ });
522
+ const response = await requestWithAuth
523
+ .patch(`sharing/family/${familyId}`, {
524
+ json: { profiles },
525
+ })
526
+ .json();
527
+ return response;
528
+ };
389
529
  return {
390
530
  createDevice,
391
531
  readDevices,
392
532
  readApiKeys,
393
533
  readDeviceDailyDistance,
394
534
  readDeviceData,
535
+ readDataForDevices,
395
536
  readGateways,
396
537
  readShippingRates,
397
538
  sendMobileLocation,
@@ -400,6 +541,13 @@ export const skiKrumb = (options = {
400
541
  createSharingLink,
401
542
  previewSharingLink,
402
543
  acceptSharingLink,
544
+ shareBack,
545
+ deleteFamilyShare,
546
+ updateFamilyShare,
547
+ getReplayData,
548
+ getDailyStats,
549
+ getAccessibleProfiles,
550
+ getLatestDeviceData,
403
551
  createRealtimeClient: (userId, sessionToken, supabaseToken) => {
404
552
  if (!sessionToken && !options.apiKey) {
405
553
  throw new Error('Session token or API key required for realtime client');
package/dist/models.d.ts CHANGED
@@ -168,6 +168,7 @@ export interface SharePreviewResponse {
168
168
  account_id: string;
169
169
  name: string;
170
170
  email: string;
171
+ avatar: string;
171
172
  };
172
173
  share_type: 'admin' | 'specific_profiles';
173
174
  profiles: Array<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skikrumb-api",
3
- "version": "2.1.3",
3
+ "version": "2.1.6",
4
4
  "description": "Wrapper for the skiKrumb API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",