skikrumb-api 2.1.4 → 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;
@@ -45,6 +46,30 @@ export declare const skiKrumb: (options?: {
45
46
  createSharingLink: (shareRequest: ShareLinkRequest) => Promise<ShareLinkResponse>;
46
47
  previewSharingLink: (token: string) => Promise<SharePreviewResponse>;
47
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
+ }>;
48
73
  createRealtimeClient: (userId: string, sessionToken?: string, supabaseToken?: string) => SkiKrumbRealtimeClient;
49
74
  };
50
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
  }
@@ -395,6 +393,139 @@ export const skiKrumb = (options = {
395
393
  .json();
396
394
  return response;
397
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
+ };
398
529
  return {
399
530
  createDevice,
400
531
  readDevices,
@@ -410,6 +541,13 @@ export const skiKrumb = (options = {
410
541
  createSharingLink,
411
542
  previewSharingLink,
412
543
  acceptSharingLink,
544
+ shareBack,
545
+ deleteFamilyShare,
546
+ updateFamilyShare,
547
+ getReplayData,
548
+ getDailyStats,
549
+ getAccessibleProfiles,
550
+ getLatestDeviceData,
413
551
  createRealtimeClient: (userId, sessionToken, supabaseToken) => {
414
552
  if (!sessionToken && !options.apiKey) {
415
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.4",
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",