skikrumb-api 2.1.4 → 2.1.7

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,12 +10,14 @@ 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;
16
17
  private scheduleReconnect;
17
18
  disconnect(): void;
18
19
  ping(): void;
20
+ sendPong(): void;
19
21
  requestRefresh(): void;
20
22
  on(event: string, callback: Function): void;
21
23
  off(event: string, callback?: Function): void;
@@ -45,6 +47,30 @@ export declare const skiKrumb: (options?: {
45
47
  createSharingLink: (shareRequest: ShareLinkRequest) => Promise<ShareLinkResponse>;
46
48
  previewSharingLink: (token: string) => Promise<SharePreviewResponse>;
47
49
  acceptSharingLink: (token: string, acceptRequest: ShareAcceptRequest) => Promise<ShareAcceptResponse>;
50
+ shareBack: (targetAccountId: string, durationDays?: number, originalFamilyId?: string) => Promise<any>;
51
+ deleteFamilyShare: (familyId: string) => Promise<{
52
+ success: boolean;
53
+ message?: string;
54
+ error?: any;
55
+ }>;
56
+ updateFamilyShare: (familyId: string, profiles: string[]) => Promise<{
57
+ success: boolean;
58
+ data?: any;
59
+ error?: any;
60
+ }>;
61
+ getReplayData: (serialNumber: string, date: string, size?: number) => Promise<any>;
62
+ getDailyStats: (serialNumber: string, date: string) => Promise<any>;
63
+ getAccessibleProfiles: () => Promise<{
64
+ success: boolean;
65
+ accessibleProfiles: any[];
66
+ profilesCount: number;
67
+ accessibleRegistrations: any[];
68
+ }>;
69
+ getLatestDeviceData: (serialNumber: string) => Promise<{
70
+ success: boolean;
71
+ data?: any;
72
+ error?: any;
73
+ }>;
48
74
  createRealtimeClient: (userId: string, sessionToken?: string, supabaseToken?: string) => SkiKrumbRealtimeClient;
49
75
  };
50
76
  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;
@@ -67,6 +68,10 @@ class SkiKrumbRealtimeClient {
67
68
  (_a = this.websocket) === null || _a === void 0 ? void 0 : _a.close();
68
69
  reject(new Error(`Connection failed: ${message.message || message.error}`));
69
70
  }
71
+ else if (message.type === 'ping') {
72
+ // Server-initiated ping - respond with pong
73
+ this.sendPong();
74
+ }
70
75
  else if (message.type === 'session_replaced') {
71
76
  this.emit('session_replaced', message);
72
77
  }
@@ -130,12 +135,8 @@ class SkiKrumbRealtimeClient {
130
135
  this.emit('disconnected', {
131
136
  reason: event.reason || 'Connection closed',
132
137
  });
133
- if (this.reconnectAttempts < this.maxReconnectAttempts) {
134
- this.scheduleReconnect();
135
- }
136
- else {
137
- this.emit('max_reconnect_attempts_reached');
138
- }
138
+ // Always try to reconnect with smart backoff
139
+ this.scheduleReconnect();
139
140
  }
140
141
  };
141
142
  }
@@ -147,12 +148,12 @@ class SkiKrumbRealtimeClient {
147
148
  }
148
149
  scheduleReconnect() {
149
150
  this.reconnectAttempts++;
150
- const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
151
+ // Exponential backoff with 5-minute cap
152
+ const delay = Math.min(this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1), this.maxReconnectDelay);
151
153
  setTimeout(() => {
152
154
  this.connect().catch((error) => {
153
- if (this.reconnectAttempts >= this.maxReconnectAttempts) {
154
- this.emit('max_reconnect_attempts_reached');
155
- }
155
+ // Errors are expected during reconnection, will keep trying
156
+ // No max attempts check - keeps trying indefinitely with smart backoff
156
157
  });
157
158
  }, delay);
158
159
  }
@@ -178,6 +179,11 @@ class SkiKrumbRealtimeClient {
178
179
  this.websocket.send(JSON.stringify({ type: 'ping', timestamp: new Date().toISOString() }));
179
180
  }
180
181
  }
182
+ sendPong() {
183
+ if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
184
+ this.websocket.send(JSON.stringify({ type: 'pong', timestamp: new Date().toISOString() }));
185
+ }
186
+ }
181
187
  requestRefresh() {
182
188
  if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
183
189
  this.websocket.send(JSON.stringify({
@@ -395,6 +401,137 @@ export const skiKrumb = (options = {
395
401
  .json();
396
402
  return response;
397
403
  };
404
+ const patchSharingLink = async (token, body) => {
405
+ const requestWithAuth = ky.create({
406
+ prefixUrl: options.url,
407
+ headers: {
408
+ 'Content-Type': 'application/json',
409
+ Authorization: `Bearer ${options.apiKey}`,
410
+ 'supabase-auth-token': options.supabaseToken || '',
411
+ 'x-client': options.requestedWith,
412
+ },
413
+ });
414
+ const response = await requestWithAuth
415
+ .patch(`sharing/${token}`, {
416
+ json: body,
417
+ })
418
+ .json();
419
+ return response;
420
+ };
421
+ const shareBack = async (targetAccountId, durationDays, originalFamilyId) => {
422
+ const requestWithAuth = ky.create({
423
+ prefixUrl: options.url,
424
+ headers: {
425
+ 'Content-Type': 'application/json',
426
+ Authorization: `Bearer ${options.apiKey}`,
427
+ 'supabase-auth-token': options.supabaseToken || '',
428
+ 'x-client': options.requestedWith,
429
+ },
430
+ });
431
+ const response = await requestWithAuth
432
+ .post('sharing/share-back', {
433
+ json: {
434
+ target_account_id: targetAccountId,
435
+ duration_days: durationDays,
436
+ original_family_id: originalFamilyId,
437
+ },
438
+ })
439
+ .json();
440
+ return response;
441
+ };
442
+ const getReplayData = async (serialNumber, date, size = 10000) => {
443
+ const requestWithAuth = ky.create({
444
+ prefixUrl: options.url,
445
+ headers: {
446
+ 'Content-Type': 'application/json',
447
+ Authorization: `Bearer ${options.apiKey}`,
448
+ 'supabase-auth-token': options.supabaseToken || '',
449
+ 'x-client': options.requestedWith,
450
+ },
451
+ });
452
+ const response = await requestWithAuth
453
+ .get(`data/replay/${serialNumber}`, {
454
+ searchParams: { date, size },
455
+ })
456
+ .json();
457
+ return response;
458
+ };
459
+ const getDailyStats = async (serialNumber, date) => {
460
+ const requestWithAuth = ky.create({
461
+ prefixUrl: options.url,
462
+ headers: {
463
+ 'Content-Type': 'application/json',
464
+ Authorization: `Bearer ${options.apiKey}`,
465
+ 'supabase-auth-token': options.supabaseToken || '',
466
+ 'x-client': options.requestedWith,
467
+ },
468
+ });
469
+ const response = await requestWithAuth
470
+ .get(`data/stats/${serialNumber}`, {
471
+ searchParams: { date },
472
+ })
473
+ .json();
474
+ return response;
475
+ };
476
+ const getAccessibleProfiles = async () => {
477
+ const requestWithAuth = ky.create({
478
+ prefixUrl: options.url,
479
+ headers: {
480
+ 'Content-Type': 'application/json',
481
+ Authorization: `Bearer ${options.apiKey}`,
482
+ 'supabase-auth-token': options.supabaseToken || '',
483
+ 'x-client': options.requestedWith,
484
+ },
485
+ });
486
+ const response = await requestWithAuth.get('auth/profiles').json();
487
+ return response;
488
+ };
489
+ const getLatestDeviceData = async (serialNumber) => {
490
+ const requestWithAuth = ky.create({
491
+ prefixUrl: options.url,
492
+ headers: {
493
+ 'Content-Type': 'application/json',
494
+ Authorization: `Bearer ${options.apiKey}`,
495
+ 'supabase-auth-token': options.supabaseToken || '',
496
+ 'x-client': options.requestedWith,
497
+ },
498
+ });
499
+ const response = await requestWithAuth
500
+ .get(`data/latest/${serialNumber}`)
501
+ .json();
502
+ return response;
503
+ };
504
+ const deleteFamilyShare = async (familyId) => {
505
+ const requestWithAuth = ky.create({
506
+ prefixUrl: options.url,
507
+ headers: {
508
+ Authorization: `Bearer ${options.apiKey}`,
509
+ 'supabase-auth-token': options.supabaseToken || '',
510
+ 'x-client': options.requestedWith,
511
+ },
512
+ });
513
+ const response = await requestWithAuth
514
+ .delete(`sharing/family/${familyId}`)
515
+ .json();
516
+ return response;
517
+ };
518
+ const updateFamilyShare = async (familyId, profiles) => {
519
+ const requestWithAuth = ky.create({
520
+ prefixUrl: options.url,
521
+ headers: {
522
+ 'Content-Type': 'application/json',
523
+ Authorization: `Bearer ${options.apiKey}`,
524
+ 'supabase-auth-token': options.supabaseToken || '',
525
+ 'x-client': options.requestedWith,
526
+ },
527
+ });
528
+ const response = await requestWithAuth
529
+ .patch(`sharing/family/${familyId}`, {
530
+ json: { profiles },
531
+ })
532
+ .json();
533
+ return response;
534
+ };
398
535
  return {
399
536
  createDevice,
400
537
  readDevices,
@@ -410,6 +547,13 @@ export const skiKrumb = (options = {
410
547
  createSharingLink,
411
548
  previewSharingLink,
412
549
  acceptSharingLink,
550
+ shareBack,
551
+ deleteFamilyShare,
552
+ updateFamilyShare,
553
+ getReplayData,
554
+ getDailyStats,
555
+ getAccessibleProfiles,
556
+ getLatestDeviceData,
413
557
  createRealtimeClient: (userId, sessionToken, supabaseToken) => {
414
558
  if (!sessionToken && !options.apiKey) {
415
559
  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.7",
4
4
  "description": "Wrapper for the skiKrumb API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",