hevy-shared 1.0.941 → 1.0.943

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.
@@ -1,5 +1,5 @@
1
1
  import { ClientAuthToken, DeepReadonly } from '..';
2
- import { RequestConfig, HTTPResponse, HTTPClient, HTTPResponseHandler, HTTPErrorHandler } from './types';
2
+ import { RequestConfig, HTTPResponse, HTTPClient, HTTPRequestCompletionCallback, HTTPErrorHandler } from './types';
3
3
  interface HevyAPIClientConfig<UserContext> {
4
4
  /**
5
5
  * How long before predicted token expiry to request a new token. We request
@@ -90,7 +90,7 @@ export declare class HevyAPIClient<UserContext = never> {
90
90
  private static readonly DEFAULT_TOKEN_REFRESH_THROTTLE_MS;
91
91
  private static readonly DEFAULT_REFRESH_AUTH_TOKEN_API_ENDPOINT;
92
92
  private readonly _config;
93
- private _responseHandlers;
93
+ private _requestCompletionCallbacks;
94
94
  private _errorHandlers;
95
95
  private _authTokenFactory;
96
96
  private _legacyAuthToken;
@@ -133,12 +133,12 @@ export declare class HevyAPIClient<UserContext = never> {
133
133
  * This is a lower level API than {@link attachErrorHandler} - prefer using
134
134
  * that one instead of this one if it's enough to suit your needs.
135
135
  */
136
- attachResponseHandler(onResponse: HTTPResponseHandler): void;
137
- removeResponseHandlers(): void;
136
+ attachRequestCompletionCallback(onResult: HTTPRequestCompletionCallback): void;
137
+ removeRequestCompletionCallbacks(): void;
138
138
  /**
139
139
  * Adds a callback to be executed on receiving an HTTP error from the server.
140
140
  * This callback will not be executed for any other type of error, such as a
141
- * network error. For that and more, use {@link attachResponseHandler}.
141
+ * network error. For that and more, use {@link attachRequestCompletionCallback}.
142
142
  */
143
143
  attachErrorHandler(onError: HTTPErrorHandler<{
144
144
  willRetry: boolean;
@@ -20,7 +20,7 @@ const __1 = require("..");
20
20
  const types_1 = require("./types");
21
21
  class HevyAPIClient {
22
22
  constructor(httpClient, config) {
23
- this._responseHandlers = [];
23
+ this._requestCompletionCallbacks = [];
24
24
  this._errorHandlers = [];
25
25
  this._authTokenFactory = {
26
26
  type: 'value',
@@ -146,11 +146,11 @@ class HevyAPIClient {
146
146
  response,
147
147
  });
148
148
  }
149
- this._responseHandlers.forEach((cb) => cb({ isSuccess: true, value: response }, request));
149
+ this._requestCompletionCallbacks.forEach((cb) => cb({ isSuccess: true, value: response }, request));
150
150
  return response;
151
151
  }
152
152
  catch (e) {
153
- this._responseHandlers.forEach((cb) => cb({ isSuccess: false, error: e }, request));
153
+ this._requestCompletionCallbacks.forEach((cb) => cb({ isSuccess: false, error: e }, request));
154
154
  if (!(0, types_1.isHTTPError)(e))
155
155
  throw e;
156
156
  const { response } = e;
@@ -250,16 +250,16 @@ class HevyAPIClient {
250
250
  * This is a lower level API than {@link attachErrorHandler} - prefer using
251
251
  * that one instead of this one if it's enough to suit your needs.
252
252
  */
253
- attachResponseHandler(onResponse) {
254
- this._responseHandlers.push(onResponse);
253
+ attachRequestCompletionCallback(onResult) {
254
+ this._requestCompletionCallbacks.push(onResult);
255
255
  }
256
- removeResponseHandlers() {
257
- this._responseHandlers.length = 0;
256
+ removeRequestCompletionCallbacks() {
257
+ this._requestCompletionCallbacks.length = 0;
258
258
  }
259
259
  /**
260
260
  * Adds a callback to be executed on receiving an HTTP error from the server.
261
261
  * This callback will not be executed for any other type of error, such as a
262
- * network error. For that and more, use {@link attachResponseHandler}.
262
+ * network error. For that and more, use {@link attachRequestCompletionCallback}.
263
263
  */
264
264
  attachErrorHandler(onError) {
265
265
  this._errorHandlers.push(onError);
@@ -23,7 +23,7 @@ export interface HTTPRequestFactory<T = unknown> {
23
23
  headers: Record<string, string>;
24
24
  try(): Promise<HTTPResponse<T>>;
25
25
  }
26
- export type HTTPResponseHandler<T = unknown, R = unknown> = (response: Result<HTTPResponse<T>>, request: HTTPRequestFactory<R>) => void;
26
+ export type HTTPRequestCompletionCallback<T = unknown, R = unknown> = (result: Result<HTTPResponse<T>>, request: HTTPRequestFactory<R>) => void;
27
27
  export type HTTPErrorHandler<E, T = unknown, R = unknown> = (response: HTTPResponse<T>, request: HTTPRequestFactory<R>, extraData: E) => void;
28
28
  export interface HTTPResponse<T = unknown> {
29
29
  status: number;
@@ -1,10 +1,8 @@
1
- import { WeeklyTrainingFrequency, TrainingGoal, TrainingLevel, SimplifiedMuscleGroup, MuscleGroup, LibraryExercise, ExerciseCategory, GranularEquipment, HevyTrainerProgramEquipment, RestTimerLength } from '.';
1
+ import { WeeklyTrainingFrequency, TrainingGoal, TrainingLevel, SimplifiedMuscleGroup, MuscleGroup, LibraryExercise, ExerciseCategory, GranularEquipment, HevyTrainerProgramEquipment } from '.';
2
2
  export type HevyTrainerExerciseCategory = typeof hevyTrainerExerciseCategories[number];
3
3
  export type HevyTrainerRoutineName = typeof routineNames[number];
4
4
  export declare const workoutDurationOptions: readonly [40, 60, 80];
5
5
  export type WorkoutDurationMinutes = typeof workoutDurationOptions[number];
6
- export declare const trainingGoalRestPresets: readonly ["strength_short", "strength_medium", "strength_long", "build_muscle_short", "build_muscle_medium", "build_muscle_long", "fat_loss_short", "fat_loss_medium", "fat_loss_long"];
7
- export type TrainingGoalRestPreset = typeof trainingGoalRestPresets[number];
8
6
  export declare const trainerGymTypes: readonly ["home_gym", "garage_gym", "commercial_gym", "full_gym"];
9
7
  export type TrainerGymType = typeof trainerGymTypes[number];
10
8
  export declare const granularEquipmentDefaults: {
@@ -47,7 +45,6 @@ export interface ProgramGenerationParams<T extends HevyTrainerLibraryExercise> {
47
45
  selectedLevel: TrainingLevel;
48
46
  selectedEquipments: GranularEquipment[];
49
47
  selectedWorkoutDurationMinutes?: WorkoutDurationMinutes;
50
- selectedRestTimerLength: RestTimerLength;
51
48
  exerciseStore: T[];
52
49
  focusMuscle?: SimplifiedMuscleGroup;
53
50
  excludedExerciseIds?: Set<string>;
@@ -75,7 +72,7 @@ export type RestTimersPerCategory = {
75
72
  [key in HevyTrainerExerciseCategory]: number;
76
73
  };
77
74
  export type RestTimers = {
78
- [key in TrainingGoalRestPreset]: RestTimersPerCategory;
75
+ [key in TrainingGoal]: RestTimersPerCategory;
79
76
  };
80
77
  export type ExercisePriorities = {
81
78
  [key in MuscleGroup]: exerciseId[];
@@ -138,14 +135,7 @@ export interface TrainerProgram {
138
135
  }
139
136
  export declare const getTrainerSetCount: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, frequency: WeeklyTrainingFrequency) => number;
140
137
  export declare const getTrainerRepRange: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, exerciseCategory: HevyTrainerExerciseCategory) => RepRange;
141
- export declare const getTrainerRestTimerSeconds: (trainerAlgorithmSettings: TrainerAlgorithmSettings, trainingGoalRestPreset: TrainingGoalRestPreset, exerciseCategory: HevyTrainerExerciseCategory) => number;
142
- /**
143
- * Converts a RestTimerLength to a TrainingGoalRestPreset
144
- * @param goal - The training goal
145
- * @param length - The rest timer length
146
- * @returns The training goal rest preset
147
- */
148
- export declare const getGoalRestPreset: (goal: TrainingGoal, length: RestTimerLength) => TrainingGoalRestPreset;
138
+ export declare const getTrainerRestTimerSeconds: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, exerciseCategory: HevyTrainerExerciseCategory) => number;
149
139
  /**
150
140
  * Normalizes the exercise category to a HevyTrainerExerciseCategory
151
141
  * - Treat custom exercises and exercises with no category as `compound`
@@ -1,19 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateProgram = exports.pickExerciseForPrescription = exports.getPrioritySortedExercises = exports.isEquipmentCompatible = exports.normalizeExerciseCategory = exports.getGoalRestPreset = exports.getTrainerRestTimerSeconds = exports.getTrainerRepRange = exports.getTrainerSetCount = exports.programSplits = exports.frequencyMap = exports.routineNames = exports.defaultDurationPerFrequency = exports.hevyTrainerExerciseCategories = exports.granularEquipmentsToTrainerEquipments = exports.trainerEquipmentToGranularEquipments = exports.granularEquipmentDefaults = exports.trainerGymTypes = exports.trainingGoalRestPresets = exports.workoutDurationOptions = void 0;
3
+ exports.generateProgram = exports.pickExerciseForPrescription = exports.getPrioritySortedExercises = exports.isEquipmentCompatible = exports.normalizeExerciseCategory = exports.getTrainerRestTimerSeconds = exports.getTrainerRepRange = exports.getTrainerSetCount = exports.programSplits = exports.frequencyMap = exports.routineNames = exports.defaultDurationPerFrequency = exports.hevyTrainerExerciseCategories = exports.granularEquipmentsToTrainerEquipments = exports.trainerEquipmentToGranularEquipments = exports.granularEquipmentDefaults = exports.trainerGymTypes = exports.workoutDurationOptions = void 0;
4
4
  const _1 = require(".");
5
5
  exports.workoutDurationOptions = [40, 60, 80];
6
- exports.trainingGoalRestPresets = [
7
- 'strength_short',
8
- 'strength_medium',
9
- 'strength_long',
10
- 'build_muscle_short',
11
- 'build_muscle_medium',
12
- 'build_muscle_long',
13
- 'fat_loss_short',
14
- 'fat_loss_medium',
15
- 'fat_loss_long',
16
- ];
17
6
  exports.trainerGymTypes = [
18
7
  'home_gym',
19
8
  'garage_gym',
@@ -214,42 +203,10 @@ const getTrainerRepRange = (trainerAlgorithmSettings, goal, exerciseCategory) =>
214
203
  return trainerAlgorithmSettings.rep_ranges[goal][exerciseCategory];
215
204
  };
216
205
  exports.getTrainerRepRange = getTrainerRepRange;
217
- const getTrainerRestTimerSeconds = (trainerAlgorithmSettings, trainingGoalRestPreset, exerciseCategory) => {
218
- return trainerAlgorithmSettings.rest_timers[trainingGoalRestPreset][exerciseCategory];
206
+ const getTrainerRestTimerSeconds = (trainerAlgorithmSettings, goal, exerciseCategory) => {
207
+ return trainerAlgorithmSettings.rest_timers[goal][exerciseCategory];
219
208
  };
220
209
  exports.getTrainerRestTimerSeconds = getTrainerRestTimerSeconds;
221
- /**
222
- * Converts a RestTimerLength to a TrainingGoalRestPreset
223
- * @param goal - The training goal
224
- * @param length - The rest timer length
225
- * @returns The training goal rest preset
226
- */
227
- const getGoalRestPreset = (goal, length) => {
228
- switch (goal) {
229
- case 'strength':
230
- if (length === 'short')
231
- return 'strength_short';
232
- if (length === 'long')
233
- return 'strength_long';
234
- return 'strength_medium';
235
- case 'build_muscle':
236
- if (length === 'short')
237
- return 'build_muscle_short';
238
- if (length === 'long')
239
- return 'build_muscle_long';
240
- return 'build_muscle_medium';
241
- case 'fat_loss':
242
- if (length === 'short')
243
- return 'fat_loss_short';
244
- if (length === 'long')
245
- return 'fat_loss_long';
246
- return 'fat_loss_medium';
247
- default:
248
- (0, _1.exhaustiveTypeCheck)(goal);
249
- return 'strength_medium';
250
- }
251
- };
252
- exports.getGoalRestPreset = getGoalRestPreset;
253
210
  /**
254
211
  * Normalizes the exercise category to a HevyTrainerExerciseCategory
255
212
  * - Treat custom exercises and exercises with no category as `compound`
@@ -541,7 +498,7 @@ exports.pickExerciseForPrescription = pickExerciseForPrescription;
541
498
  */
542
499
  const generateProgram = (params) => {
543
500
  var _a;
544
- const { trainerAlgorithmSettings, selectedDays, selectedGoal, selectedLevel, selectedEquipments, selectedWorkoutDurationMinutes, selectedRestTimerLength, exerciseStore, focusMuscle, excludedExerciseIds, } = params;
501
+ const { trainerAlgorithmSettings, selectedDays, selectedGoal, selectedLevel, selectedEquipments, selectedWorkoutDurationMinutes, exerciseStore, focusMuscle, excludedExerciseIds, } = params;
545
502
  const routines = exports.programSplits[selectedDays];
546
503
  const program = {
547
504
  name: selectedDays,
@@ -608,7 +565,7 @@ const generateProgram = (params) => {
608
565
  sets: (0, exports.getTrainerSetCount)(trainerAlgorithmSettings, selectedGoal, selectedDays),
609
566
  repRangeStart: repRange.rep_range_start,
610
567
  repRangeEnd: repRange.rep_range_end,
611
- restTimerSeconds: (0, exports.getTrainerRestTimerSeconds)(trainerAlgorithmSettings, (0, exports.getGoalRestPreset)(selectedGoal, selectedRestTimerLength), exercisePrescription.category),
568
+ restTimerSeconds: (0, exports.getTrainerRestTimerSeconds)(trainerAlgorithmSettings, selectedGoal, exercisePrescription.category),
612
569
  warmupSetCount: exercisePrescription.warmup_set_count,
613
570
  notes: (_a = trainerAlgorithmSettings.exercise_notes[exercise.id]) !== null && _a !== void 0 ? _a : '',
614
571
  });
package/built/index.d.ts CHANGED
@@ -640,11 +640,9 @@ export declare const isCustomExerciseType: (x: any) => x is CustomExerciseType;
640
640
  export declare const trainingGoals: readonly ["strength", "build_muscle", "fat_loss"];
641
641
  export declare const trainingLevels: readonly ["beginner", "intermediate", "advanced"];
642
642
  export declare const exerciseCategories: readonly ["isolation", "compound", "assistance-compound"];
643
- export declare const restTimerLengths: readonly ["short", "medium", "long"];
644
643
  export type TrainingGoal = Lookup<typeof trainingGoals>;
645
644
  export type TrainingLevel = Lookup<typeof trainingLevels>;
646
645
  export type ExerciseCategory = Lookup<typeof exerciseCategories>;
647
- export type RestTimerLength = typeof restTimerLengths[number];
648
646
  export type HevyTrainerProgramEquipment = Extract<Equipment, 'barbell' | 'dumbbell' | 'machine'>;
649
647
  export declare const hevyTrainerProgramEquipments: readonly ["barbell", "dumbbell", "machine"];
650
648
  export declare const granularEquipments: readonly ["barbell", "dumbbell", "kettlebell", "plate", "medicine_ball", "ez_bar", "landmine", "trap_bar", "pullup_bar", "dip_bar", "squat_rack", "flat_bench", "adjustable_bench", "dual_cable_machine", "single_cable_machine", "lat_pulldown_cable", "leg_press_machine", "smith_machine", "t_bar", "plate_machines", "stack_machines", "treadmill", "elliptical_trainer", "rowing_machine", "spinning", "stair_machine", "air_bike", "suspension_band", "resistance_band", "battle_rope", "rings", "jump_rope"];
@@ -1261,7 +1259,6 @@ export interface HevyTrainerProgram {
1261
1259
  focus_muscle?: SimplifiedMuscleGroup;
1262
1260
  next_workout_index: number;
1263
1261
  workout_duration_minutes?: WorkoutDurationMinutes;
1264
- rest_timer_length: RestTimerLength;
1265
1262
  }
1266
1263
  export interface PostHevyTrainerProgramRequestBody {
1267
1264
  program: {
@@ -1274,7 +1271,6 @@ export interface PostHevyTrainerProgramRequestBody {
1274
1271
  routines: RoutineUpdate[];
1275
1272
  next_workout_index?: number;
1276
1273
  workout_duration_minutes?: WorkoutDurationMinutes;
1277
- rest_timer_length: RestTimerLength;
1278
1274
  };
1279
1275
  }
1280
1276
  export interface UpdateHevyTrainerProgramRequestBody {
@@ -1288,7 +1284,6 @@ export interface UpdateHevyTrainerProgramRequestBody {
1288
1284
  focus_muscle?: SimplifiedMuscleGroup;
1289
1285
  next_workout_index?: number;
1290
1286
  workout_duration_minutes?: WorkoutDurationMinutes;
1291
- rest_timer_length?: RestTimerLength;
1292
1287
  routines: {
1293
1288
  id: string;
1294
1289
  title: string;
package/built/index.js CHANGED
@@ -14,8 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.isPublicWorkout = exports.isSetType = exports.isRPE = exports.validRpeValues = exports.isSetPersonalRecordType = exports.supportedInstructionsLanguages = exports.weeklyTrainingFrequencies = exports.isGranularEquipment = exports.granularEquipments = exports.hevyTrainerProgramEquipments = exports.restTimerLengths = exports.exerciseCategories = exports.trainingLevels = exports.trainingGoals = exports.isCustomExerciseType = exports.customExericseTypes = exports.isExerciseType = exports.exerciseTypes = exports.isExerciseRepType = exports.exerciseRepTypes = exports.isEquipmentFilter = exports.equipmentFilters = exports.isEquipment = exports.equipments = exports.simplifiedMuscleGroupToMuscleGroups = exports.isMuscleGroupFilter = exports.muscleGroupFilters = exports.isMuscleGroup = exports.muscleGroups = exports.isSimplifiedMuscleGroup = exports.simplifiedMuscleGroups = exports.miscellaneousMuscles = exports.chestMuscles = exports.backMuscles = exports.legMuscles = exports.armMuscles = exports.shoulderMuscles = exports.coreMuscles = exports.DefaultClientConfiguration = exports.parseClientAuthTokenResponse = exports.isCoachRole = exports.isErrorResponse = exports.isLivePRVolumeOption = exports.isTimerVolumeOption = exports.isWeekday = exports.orderedWeekdays = exports.isBodyMeasurementUnit = exports.isDistanceUnitShort = exports.isDistanceUnit = exports.isWeightUnit = void 0;
18
- exports.isOAuthScope = exports.supportedScopes = exports.isSuggestedUserSource = exports.isValidUserWorkoutMetricsType = exports.isBodyMeasurementKey = exports.measurementsList = exports.isHevyTrainerRoutine = exports.isWorkoutBiometrics = exports.isHeartRateSamples = void 0;
17
+ exports.isHeartRateSamples = exports.isPublicWorkout = exports.isSetType = exports.isRPE = exports.validRpeValues = exports.isSetPersonalRecordType = exports.supportedInstructionsLanguages = exports.weeklyTrainingFrequencies = exports.isGranularEquipment = exports.granularEquipments = exports.hevyTrainerProgramEquipments = exports.exerciseCategories = exports.trainingLevels = exports.trainingGoals = exports.isCustomExerciseType = exports.customExericseTypes = exports.isExerciseType = exports.exerciseTypes = exports.isExerciseRepType = exports.exerciseRepTypes = exports.isEquipmentFilter = exports.equipmentFilters = exports.isEquipment = exports.equipments = exports.simplifiedMuscleGroupToMuscleGroups = exports.isMuscleGroupFilter = exports.muscleGroupFilters = exports.isMuscleGroup = exports.muscleGroups = exports.isSimplifiedMuscleGroup = exports.simplifiedMuscleGroups = exports.miscellaneousMuscles = exports.chestMuscles = exports.backMuscles = exports.legMuscles = exports.armMuscles = exports.shoulderMuscles = exports.coreMuscles = exports.DefaultClientConfiguration = exports.parseClientAuthTokenResponse = exports.isCoachRole = exports.isErrorResponse = exports.isLivePRVolumeOption = exports.isTimerVolumeOption = exports.isWeekday = exports.orderedWeekdays = exports.isBodyMeasurementUnit = exports.isDistanceUnitShort = exports.isDistanceUnit = exports.isWeightUnit = void 0;
18
+ exports.isOAuthScope = exports.supportedScopes = exports.isSuggestedUserSource = exports.isValidUserWorkoutMetricsType = exports.isBodyMeasurementKey = exports.measurementsList = exports.isHevyTrainerRoutine = exports.isWorkoutBiometrics = void 0;
19
19
  const typeUtils_1 = require("./typeUtils");
20
20
  __exportStar(require("./constants"), exports);
21
21
  __exportStar(require("./utils"), exports);
@@ -227,7 +227,6 @@ exports.exerciseCategories = [
227
227
  'compound',
228
228
  'assistance-compound',
229
229
  ];
230
- exports.restTimerLengths = ['short', 'medium', 'long'];
231
230
  exports.hevyTrainerProgramEquipments = [
232
231
  'barbell',
233
232
  'dumbbell',
@@ -210,6 +210,58 @@ describe('utils', () => {
210
210
  expect((0, utils_1.findMapped)([], () => undefined)).toBeUndefined();
211
211
  });
212
212
  });
213
+ describe('toFragmentedJSON', () => {
214
+ it('converts any array into an array of JSON chunks with a maximum given length', () => {
215
+ expect((0, utils_1.toFragmentedJSON)([
216
+ { value: 'AAAAAAAAAAAAAAAA' },
217
+ { value: 'BBBBBBBBBBBBBBBBBBBBB' },
218
+ { value: 'CCCCCCCCCCCCCCCC' },
219
+ ], 64)).toEqual([
220
+ JSON.stringify([
221
+ { value: 'AAAAAAAAAAAAAAAA' },
222
+ { value: 'BBBBBBBBBBBBBBBBBBBBB' },
223
+ ]),
224
+ JSON.stringify([{ value: 'CCCCCCCCCCCCCCCC' }]),
225
+ ]);
226
+ expect((0, utils_1.toFragmentedJSON)([
227
+ { value: 'AAAAAAAAAAAAAAAA' },
228
+ { value: 'BBBBBBBBBBBBBBBBBBBBB' },
229
+ { value: 'CCCCCCCCCCCCCCCC' },
230
+ ], 35)).toEqual([
231
+ JSON.stringify([{ value: 'AAAAAAAAAAAAAAAA' }]),
232
+ JSON.stringify([{ value: 'BBBBBBBBBBBBBBBBBBBBB' }]),
233
+ JSON.stringify([{ value: 'CCCCCCCCCCCCCCCC' }]),
234
+ ]);
235
+ expect(() => (0, utils_1.toFragmentedJSON)([
236
+ { value: 'AAAAAAAAAAAAAAAA' },
237
+ { value: 'BBBBBBBBBBBBBBBBBBBBB' },
238
+ { value: 'CCCCCCCCCCCCCCCC' },
239
+ ], 34)).toThrow();
240
+ expect((0, utils_1.toFragmentedJSON)(['A', 'B', 'C'], 15)).toEqual([
241
+ JSON.stringify(['A', 'B', 'C']),
242
+ ]);
243
+ expect((0, utils_1.toFragmentedJSON)(['A', 'B', 'C'], 10)).toEqual([
244
+ JSON.stringify(['A', 'B']),
245
+ JSON.stringify(['C']),
246
+ ]);
247
+ expect((0, utils_1.toFragmentedJSON)(['A', 'B', 'C'], 5)).toEqual([
248
+ JSON.stringify(['A']),
249
+ JSON.stringify(['B']),
250
+ JSON.stringify(['C']),
251
+ ]);
252
+ expect(() => (0, utils_1.toFragmentedJSON)(['A', 'B', 'C'], 1)).toThrow();
253
+ expect(() => (0, utils_1.toFragmentedJSON)(['A', 'B', 'C'], 0)).toThrow();
254
+ expect((0, utils_1.toFragmentedJSON)([], 1)).toEqual([]);
255
+ expect((0, utils_1.toFragmentedJSON)([], 0)).toEqual([]);
256
+ });
257
+ it('returns an empty array for an empty input array', () => {
258
+ expect((0, utils_1.toFragmentedJSON)(['blah'], 100)).toEqual([
259
+ JSON.stringify(['blah']),
260
+ ]);
261
+ expect((0, utils_1.toFragmentedJSON)([], 100)).toEqual([]);
262
+ expect((0, utils_1.toFragmentedJSON)([], 100)).not.toEqual([JSON.stringify([])]);
263
+ });
264
+ });
213
265
  describe('getClosestDataPointBeforeTargetDate', () => {
214
266
  it('gets the closest data point before a target date, if such a data point exists, using a function to extract the date from the data points', () => {
215
267
  var _a, _b, _c, _d;
package/built/utils.d.ts CHANGED
@@ -90,6 +90,16 @@ export declare const stringToNumber: (value?: string) => number | undefined;
90
90
  * such value was produced. Equivalent to `firstNotNullOfOrNull` in Kotlin.
91
91
  */
92
92
  export declare const findMapped: <T, R>(array: T[], transform: (element: T) => R) => R | undefined;
93
+ /**
94
+ * converts any array into an array of JSON chunks with a maximum given length
95
+ * @param data an array of objects or primitives to be split into chunks
96
+ * @param maxLength maximum length of any single returned JSON string
97
+ * @returns an array of JSON strings, each one no longer than `maxLength`
98
+ * @throws an error if fragmentation is impossible due to input data structure
99
+ */
100
+ export declare const toFragmentedJSON: <T>(data: T[], maxLength: number, options?: {
101
+ lengthIn: "utf8bytes" | "characters";
102
+ }) => string[];
93
103
  /**
94
104
  * Finds the closest data point that occurs before or at the target date using binary search.
95
105
  * Assumes input array is sorted by date in ascending order.
package/built/utils.js CHANGED
@@ -3,8 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.startOfWeek = exports.calculateCurrentWeekStreak = exports.getYoutubeVideoId = exports.validateYoutubeUrl = exports.splitAtUsernamesAndLinks = exports.isVersionAGreaterOrEqualToVersionB = exports.generateUserGroupValue = exports.generateUserGroup = exports.isBaseExerciseTemplate = exports.getStrengthLevelFromPercentile = exports.numberToLocaleString = exports.numberWithCommas = exports.setVolume = exports.oneRepMax = exports.oneRepMaxPercentageMap = exports.getEstimatedExercisesDurationSeconds = exports.ESTIMATED_REST_TIMER_DURATION = exports.ESTIMATED_SET_DURATION = exports.UserFacingIndicatorToSetIndicator = exports.workoutSetCount = exports.userExerciseSetWeight = exports.workoutDistanceMeters = exports.workoutReps = exports.workoutDurationSeconds = exports.removeAccents = exports.getClosestDataPointAroundTargetDate = exports.getClosestDataPointBeforeTargetDate = exports.findMapped = exports.stringToNumber = exports.forceStringToNumber = exports.formatDurationInput = exports.isValidFormattedTime = exports.isWholeNumber = exports.isNumber = exports.isValidUuid = exports.isValidPhoneNumber = exports.isValidWebUrl = exports.URL_REGEX = exports.isValidEmail = exports.secondsToWordFormatMinutes = exports.secondsToWordFormat = exports.secondsToClockFormat = exports.secondsToClockParts = exports.isValidUsername = exports.roundToWholeNumber = exports.roundToOneDecimal = exports.roundToTwoDecimal = exports.divide = exports.clampNumber = exports.num = void 0;
7
- exports.rawInstructionsToIndexedSteps = exports.formatSetValue = exports.exerciseWeight = exports.distance = exports.weekdayNumberMap = void 0;
6
+ exports.calculateCurrentWeekStreak = exports.getYoutubeVideoId = exports.validateYoutubeUrl = exports.splitAtUsernamesAndLinks = exports.isVersionAGreaterOrEqualToVersionB = exports.generateUserGroupValue = exports.generateUserGroup = exports.isBaseExerciseTemplate = exports.getStrengthLevelFromPercentile = exports.numberToLocaleString = exports.numberWithCommas = exports.setVolume = exports.oneRepMax = exports.oneRepMaxPercentageMap = exports.getEstimatedExercisesDurationSeconds = exports.ESTIMATED_REST_TIMER_DURATION = exports.ESTIMATED_SET_DURATION = exports.UserFacingIndicatorToSetIndicator = exports.workoutSetCount = exports.userExerciseSetWeight = exports.workoutDistanceMeters = exports.workoutReps = exports.workoutDurationSeconds = exports.removeAccents = exports.getClosestDataPointAroundTargetDate = exports.getClosestDataPointBeforeTargetDate = exports.toFragmentedJSON = exports.findMapped = exports.stringToNumber = exports.forceStringToNumber = exports.formatDurationInput = exports.isValidFormattedTime = exports.isWholeNumber = exports.isNumber = exports.isValidUuid = exports.isValidPhoneNumber = exports.isValidWebUrl = exports.URL_REGEX = exports.isValidEmail = exports.secondsToWordFormatMinutes = exports.secondsToWordFormat = exports.secondsToClockFormat = exports.secondsToClockParts = exports.isValidUsername = exports.roundToWholeNumber = exports.roundToOneDecimal = exports.roundToTwoDecimal = exports.divide = exports.clampNumber = exports.num = void 0;
7
+ exports.rawInstructionsToIndexedSteps = exports.formatSetValue = exports.exerciseWeight = exports.distance = exports.weekdayNumberMap = exports.startOfWeek = void 0;
8
8
  const dayjs_1 = __importDefault(require("dayjs"));
9
9
  const _1 = require(".");
10
10
  /**
@@ -298,6 +298,43 @@ const findMapped = (array, transform) => {
298
298
  }
299
299
  };
300
300
  exports.findMapped = findMapped;
301
+ /**
302
+ * converts any array into an array of JSON chunks with a maximum given length
303
+ * @param data an array of objects or primitives to be split into chunks
304
+ * @param maxLength maximum length of any single returned JSON string
305
+ * @returns an array of JSON strings, each one no longer than `maxLength`
306
+ * @throws an error if fragmentation is impossible due to input data structure
307
+ */
308
+ const toFragmentedJSON = (data, maxLength, options = { lengthIn: 'utf8bytes' }) => {
309
+ const outputArray = [];
310
+ const recurse = (left, right) => {
311
+ if (left === right)
312
+ return;
313
+ // slice the array and convert it to JSON
314
+ const sliceJson = JSON.stringify(data.slice(left, right));
315
+ const length = options.lengthIn === 'characters'
316
+ ? sliceJson.length
317
+ : Buffer.byteLength(sliceJson, 'utf8');
318
+ if (length <= maxLength) {
319
+ // the size is good, push the chunk to the output array
320
+ outputArray.push(sliceJson);
321
+ }
322
+ else if (right > left + 1) {
323
+ // there are at least 2 elements in the current slice
324
+ const middle = Math.ceil((left + right) / 2);
325
+ recurse(left, middle);
326
+ recurse(middle, right);
327
+ }
328
+ else {
329
+ // we can't subdivide any further but the string is still too long
330
+ throw new Error(`couldn't subdivide element`);
331
+ }
332
+ };
333
+ outputArray.length = 0;
334
+ recurse(0, data.length);
335
+ return outputArray;
336
+ };
337
+ exports.toFragmentedJSON = toFragmentedJSON;
301
338
  /**
302
339
  * Finds the closest data point that occurs before or at the target date using binary search.
303
340
  * Assumes input array is sorted by date in ascending order.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hevy-shared",
3
- "version": "1.0.941",
3
+ "version": "1.0.943",
4
4
  "description": "",
5
5
  "main": "built/index.js",
6
6
  "types": "built/index.d.ts",