@peektravel/app-utilities 0.5.0 → 0.5.2

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.js CHANGED
@@ -2766,6 +2766,14 @@ function normalizeDate(date) {
2766
2766
  return date.split("T")[0] ?? date;
2767
2767
  }
2768
2768
 
2769
+ // src/internal/peek/money.ts
2770
+ function toPricingMoney(raw) {
2771
+ if (!raw) return null;
2772
+ const money = { amount: raw.amount, currency: raw.currency };
2773
+ if (raw.formatted != null) money.displayPrice = raw.formatted;
2774
+ return money;
2775
+ }
2776
+
2769
2777
  // src/internal/peek/products/product-converter.ts
2770
2778
  var ADD_ON_COLOR = "#FFFFFF";
2771
2779
  function fromActivities(activities) {
@@ -2778,9 +2786,12 @@ function fromActivity(activity) {
2778
2786
  name: activity.name,
2779
2787
  type: activity.type,
2780
2788
  color: activity.colorHex || "",
2789
+ currency: activity.currency || "",
2781
2790
  tickets: (activity.resourceOptions ?? []).map((option) => ({
2782
2791
  id: option.id,
2783
- name: option.name
2792
+ name: option.name,
2793
+ minPrice: toPricingMoney(option.priceRange?.min),
2794
+ maxPrice: toPricingMoney(option.priceRange?.max)
2784
2795
  }))
2785
2796
  };
2786
2797
  }
@@ -2796,11 +2807,17 @@ function fromItemOptionNodes(nodes) {
2796
2807
  name: node.item.name,
2797
2808
  type: ADD_ON_PRODUCT_TYPE,
2798
2809
  color: ADD_ON_COLOR,
2810
+ currency: "",
2799
2811
  tickets: []
2800
2812
  };
2801
2813
  grouped.set(itemId, product);
2802
2814
  }
2803
- product.tickets.push({ id: node.id, name: node.name });
2815
+ product.tickets.push({
2816
+ id: node.id,
2817
+ name: node.name,
2818
+ minPrice: null,
2819
+ maxPrice: null
2820
+ });
2804
2821
  }
2805
2822
  return Array.from(grouped.values());
2806
2823
  }
@@ -2815,9 +2832,22 @@ var PRODUCTS_QUERY = `
2815
2832
  deletedAt
2816
2833
  type
2817
2834
  colorHex
2835
+ currency
2818
2836
  resourceOptions {
2819
2837
  id
2820
2838
  name
2839
+ priceRange {
2840
+ min {
2841
+ currency
2842
+ amount
2843
+ formatted
2844
+ }
2845
+ max {
2846
+ currency
2847
+ amount
2848
+ formatted
2849
+ }
2850
+ }
2821
2851
  }
2822
2852
  }
2823
2853
  }
@@ -3041,6 +3071,330 @@ var PromoCodeService = class {
3041
3071
  }
3042
3072
  };
3043
3073
 
3074
+ // src/internal/peek/pricing/pricing-queries.ts
3075
+ var CREATE_PRICING_ENGINE_MUTATION = `
3076
+ mutation CreatePricingEngine($input: CreatePricingEngineInput!) {
3077
+ createPricingEngine(input: $input) {
3078
+ __typename
3079
+ ... on CreatePricingEngineSuccess {
3080
+ engine {
3081
+ id
3082
+ }
3083
+ }
3084
+ ... on InvalidDataError {
3085
+ message
3086
+ }
3087
+ }
3088
+ }
3089
+ `;
3090
+ var UPDATE_PRICING_ENGINE_MUTATION = `
3091
+ mutation UpdatePricingEngine($input: UpdatePricingEngineInput!) {
3092
+ updatePricingEngine(input: $input) {
3093
+ __typename
3094
+ ... on UpdatePricingEngineSuccess {
3095
+ engine {
3096
+ id
3097
+ name
3098
+ }
3099
+ }
3100
+ ... on InvalidDataError {
3101
+ message
3102
+ }
3103
+ ... on NotFoundError {
3104
+ id
3105
+ message
3106
+ }
3107
+ }
3108
+ }
3109
+ `;
3110
+ var DELETE_PRICING_ENGINE_MUTATION = `
3111
+ mutation DeletePricingEngine($id: ID!) {
3112
+ deletePricingEngine(id: $id) {
3113
+ __typename
3114
+ ... on DeletePricingEngineSuccess {
3115
+ engine {
3116
+ id
3117
+ name
3118
+ }
3119
+ }
3120
+ ... on NotFoundError {
3121
+ id
3122
+ message
3123
+ }
3124
+ }
3125
+ }
3126
+ `;
3127
+ var UPSERT_PRICING_OVERRIDES_MUTATION = `
3128
+ mutation UpsertPricingOverridesActivityContexts($input: UpsertPricingOverridesActivityContextsInput!) {
3129
+ upsertPricingOverridesActivityContexts(input: $input) {
3130
+ __typename
3131
+ ... on UpsertPricingOverridesActivityContextsSuccess {
3132
+ activityContexts {
3133
+ date
3134
+ activity {
3135
+ id
3136
+ name
3137
+ }
3138
+ engine {
3139
+ id
3140
+ name
3141
+ }
3142
+ overrides {
3143
+ order
3144
+ resourceOptions {
3145
+ resourceOption {
3146
+ id
3147
+ name
3148
+ }
3149
+ override {
3150
+ ... on PricingOverridesActivityContextResourceOptionPriceOverride {
3151
+ price {
3152
+ amount
3153
+ currency
3154
+ formatted
3155
+ }
3156
+ }
3157
+ ... on PricingOverridesActivityContextResourceOptionPercentageAdjustmentOverride {
3158
+ percentageAdjustment
3159
+ }
3160
+ }
3161
+ }
3162
+ filters {
3163
+ __typename
3164
+ ... on PricingOverridesActivityContextStartTimeRangeFilter {
3165
+ startTimeRange
3166
+ }
3167
+ ... on PricingOverridesActivityContextSpotsTakenFilter {
3168
+ minSpots
3169
+ maxSpots
3170
+ }
3171
+ }
3172
+ }
3173
+ }
3174
+ }
3175
+ ... on InvalidDataError {
3176
+ message
3177
+ }
3178
+ }
3179
+ }
3180
+ `;
3181
+ var SPOTS_TAKEN_FILTER_TYPENAME = "PricingOverridesActivityContextSpotsTakenFilter";
3182
+ function buildCreateEngineInput(input) {
3183
+ const activityIds = input.activityIds ?? [];
3184
+ return activityIds.length > 0 ? { name: input.name, filters: [{ activityIds }] } : { name: input.name };
3185
+ }
3186
+ function buildUpdateEngineInput(input) {
3187
+ const activityIds = input.activityIds ?? [];
3188
+ return {
3189
+ id: input.engineId,
3190
+ name: input.name,
3191
+ filters: activityIds.length > 0 ? [{ activityIds }] : []
3192
+ };
3193
+ }
3194
+ function buildUpsertInput(input) {
3195
+ return {
3196
+ engineId: input.engineId,
3197
+ dateRange: input.dateRange,
3198
+ activities: input.activities.map((activity) => ({
3199
+ activityId: activity.activityId,
3200
+ overrides: activity.overrides.map((override) => ({
3201
+ order: override.order,
3202
+ resourceOptions: override.resourceOptions.map(
3203
+ (option) => option.mode === "fixed" ? {
3204
+ id: option.id,
3205
+ // Send only the wire fields — `displayPrice` is read-only.
3206
+ price: { amount: option.price.amount, currency: option.price.currency }
3207
+ } : { id: option.id, percentageAdjustment: option.percentageAdjustment }
3208
+ ),
3209
+ filters: override.filters
3210
+ }))
3211
+ }))
3212
+ };
3213
+ }
3214
+ function buildClearInput(input) {
3215
+ return {
3216
+ engineId: input.engineId,
3217
+ dateRange: input.dateRange,
3218
+ activities: input.activityIds.map((activityId) => ({
3219
+ activityId,
3220
+ overrides: []
3221
+ }))
3222
+ };
3223
+ }
3224
+
3225
+ // src/internal/peek/pricing/pricing-converter.ts
3226
+ function fromActivityContexts(contexts) {
3227
+ return contexts.map(fromActivityContext);
3228
+ }
3229
+ function fromActivityContext(context) {
3230
+ return {
3231
+ date: context.date,
3232
+ activity: { id: context.activity.id, name: context.activity.name },
3233
+ engine: { id: context.engine.id, name: context.engine.name },
3234
+ overrides: context.overrides.map(fromOverride)
3235
+ };
3236
+ }
3237
+ function fromOverride(override) {
3238
+ return {
3239
+ order: override.order,
3240
+ resourceOptions: override.resourceOptions.map(fromResourceOption),
3241
+ filters: override.filters.map(fromFilter)
3242
+ };
3243
+ }
3244
+ function fromResourceOption(option) {
3245
+ return {
3246
+ resourceOption: {
3247
+ id: option.resourceOption.id,
3248
+ name: option.resourceOption.name
3249
+ },
3250
+ override: fromResolvedOverride(option.override)
3251
+ };
3252
+ }
3253
+ function fromResolvedOverride(override) {
3254
+ return "price" in override ? { mode: "fixed", price: toPricingMoney(override.price) } : { mode: "percentage", percentageAdjustment: override.percentageAdjustment };
3255
+ }
3256
+ function fromFilter(filter) {
3257
+ if (filter.__typename === SPOTS_TAKEN_FILTER_TYPENAME) {
3258
+ const spotsTaken = {};
3259
+ if (filter.minSpots !== null) spotsTaken.minSpots = filter.minSpots;
3260
+ if (filter.maxSpots !== null) spotsTaken.maxSpots = filter.maxSpots;
3261
+ return { spotsTaken };
3262
+ }
3263
+ return { startTimeRange: filter.startTimeRange };
3264
+ }
3265
+
3266
+ // src/internal/peek/pricing/pricing-service.ts
3267
+ var CURRENCY_PATTERN2 = /^[A-Z]{3}$/;
3268
+ var PERCENTAGE_FLOOR = -100;
3269
+ var ERROR_NAME_REQUIRED = "name is required";
3270
+ var ERROR_ENGINE_ID_REQUIRED = "engineId is required";
3271
+ var ERROR_DATE_RANGE_REQUIRED = "dateRange is required";
3272
+ var ERROR_ACTIVITIES_REQUIRED = "activities is required";
3273
+ var ERROR_ACTIVITY_ID_REQUIRED = "activityId is required";
3274
+ var ERROR_TICKET_ID_REQUIRED = "resource-option id is required";
3275
+ var ERROR_CURRENCY_FORMAT = "currency must be 3 uppercase letters";
3276
+ var ERROR_AMOUNT_NUMBER = "price amount must be a valid number";
3277
+ var ERROR_PERCENTAGE_NUMBER = "percentageAdjustment must be a valid number";
3278
+ var ERROR_PERCENTAGE_FLOOR = "percentageAdjustment must be greater than -100";
3279
+ var ERROR_NO_DATA = "pricing mutation returned no data";
3280
+ var PricingService = class {
3281
+ constructor(client) {
3282
+ this.client = client;
3283
+ }
3284
+ client;
3285
+ /**
3286
+ * Creates a pricing engine. Store the returned id and reuse it on every
3287
+ * subsequent override upsert. Throws on an InvalidDataError from Peek.
3288
+ */
3289
+ async createEngine(input) {
3290
+ if (!input.name) throw new Error(ERROR_NAME_REQUIRED);
3291
+ const body = await this.client.request(
3292
+ SALES_ENDPOINT,
3293
+ CREATE_PRICING_ENGINE_MUTATION,
3294
+ { input: buildCreateEngineInput(input) }
3295
+ );
3296
+ const result = body.data?.createPricingEngine;
3297
+ if (!result) throw new Error(ERROR_NO_DATA);
3298
+ if (result.__typename !== "CreatePricingEngineSuccess") {
3299
+ throw new Error(result.message);
3300
+ }
3301
+ return { id: result.engine.id };
3302
+ }
3303
+ /**
3304
+ * Updates a pricing engine's name and/or activity scope. Throws on an
3305
+ * InvalidDataError or NotFoundError from Peek.
3306
+ */
3307
+ async updateEngine(input) {
3308
+ if (!input.engineId) throw new Error(ERROR_ENGINE_ID_REQUIRED);
3309
+ if (!input.name) throw new Error(ERROR_NAME_REQUIRED);
3310
+ const body = await this.client.request(
3311
+ SALES_ENDPOINT,
3312
+ UPDATE_PRICING_ENGINE_MUTATION,
3313
+ { input: buildUpdateEngineInput(input) }
3314
+ );
3315
+ const result = body.data?.updatePricingEngine;
3316
+ if (!result) throw new Error(ERROR_NO_DATA);
3317
+ if (result.__typename !== "UpdatePricingEngineSuccess") {
3318
+ throw new Error(result.message);
3319
+ }
3320
+ return { id: result.engine.id, name: result.engine.name };
3321
+ }
3322
+ /**
3323
+ * Deletes a pricing engine. Idempotent — resolves whether the engine existed
3324
+ * or was already gone (Peek's NotFoundError is treated as success).
3325
+ */
3326
+ async deleteEngine(engineId) {
3327
+ if (!engineId) throw new Error(ERROR_ENGINE_ID_REQUIRED);
3328
+ await this.client.request(
3329
+ SALES_ENDPOINT,
3330
+ DELETE_PRICING_ENGINE_MUTATION,
3331
+ { id: engineId }
3332
+ );
3333
+ }
3334
+ /**
3335
+ * Upserts pricing overrides for the engine and date range in `input`. Returns
3336
+ * the resolved contexts Peek stored. Throws on an InvalidDataError from Peek.
3337
+ */
3338
+ async upsertOverrides(input) {
3339
+ this.validateUpsertInput(input);
3340
+ return this.sendUpsert(buildUpsertInput(input));
3341
+ }
3342
+ /**
3343
+ * Clears all overrides for the given activities across a date range. This is
3344
+ * an upsert with empty overrides — never clear by omitting an activity.
3345
+ */
3346
+ async clearOverrides(input) {
3347
+ if (!input.engineId) throw new Error(ERROR_ENGINE_ID_REQUIRED);
3348
+ if (!input.dateRange) throw new Error(ERROR_DATE_RANGE_REQUIRED);
3349
+ if (!Array.isArray(input.activityIds)) throw new Error(ERROR_ACTIVITIES_REQUIRED);
3350
+ for (const activityId of input.activityIds) {
3351
+ if (!activityId) throw new Error(ERROR_ACTIVITY_ID_REQUIRED);
3352
+ }
3353
+ return this.sendUpsert(buildClearInput(input));
3354
+ }
3355
+ async sendUpsert(rawInput) {
3356
+ const body = await this.client.request(
3357
+ SALES_ENDPOINT,
3358
+ UPSERT_PRICING_OVERRIDES_MUTATION,
3359
+ { input: rawInput }
3360
+ );
3361
+ const result = body.data?.upsertPricingOverridesActivityContexts;
3362
+ if (!result) throw new Error(ERROR_NO_DATA);
3363
+ if (result.__typename !== "UpsertPricingOverridesActivityContextsSuccess") {
3364
+ throw new Error(result.message);
3365
+ }
3366
+ return { activityContexts: fromActivityContexts(result.activityContexts) };
3367
+ }
3368
+ validateUpsertInput(input) {
3369
+ if (!input.engineId) throw new Error(ERROR_ENGINE_ID_REQUIRED);
3370
+ if (!input.dateRange) throw new Error(ERROR_DATE_RANGE_REQUIRED);
3371
+ if (!Array.isArray(input.activities)) throw new Error(ERROR_ACTIVITIES_REQUIRED);
3372
+ for (const activity of input.activities) {
3373
+ if (!activity.activityId) throw new Error(ERROR_ACTIVITY_ID_REQUIRED);
3374
+ for (const override of activity.overrides) {
3375
+ for (const option of override.resourceOptions) {
3376
+ this.validateResourceOption(option);
3377
+ }
3378
+ }
3379
+ }
3380
+ }
3381
+ validateResourceOption(option) {
3382
+ if (!option.id) throw new Error(ERROR_TICKET_ID_REQUIRED);
3383
+ if (option.mode === "fixed") {
3384
+ if (!CURRENCY_PATTERN2.test(option.price.currency)) {
3385
+ throw new Error(ERROR_CURRENCY_FORMAT);
3386
+ }
3387
+ if (Number.isNaN(Number.parseFloat(option.price.amount))) {
3388
+ throw new Error(ERROR_AMOUNT_NUMBER);
3389
+ }
3390
+ return;
3391
+ }
3392
+ const percentage = Number.parseFloat(option.percentageAdjustment);
3393
+ if (Number.isNaN(percentage)) throw new Error(ERROR_PERCENTAGE_NUMBER);
3394
+ if (percentage <= PERCENTAGE_FLOOR) throw new Error(ERROR_PERCENTAGE_FLOOR);
3395
+ }
3396
+ };
3397
+
3044
3398
  // src/logger.ts
3045
3399
  var noopLogger = {
3046
3400
  info() {
@@ -3066,6 +3420,7 @@ var PeekAccessService = class {
3066
3420
  timeslotService;
3067
3421
  resellerService;
3068
3422
  promoCodeService;
3423
+ pricingService;
3069
3424
  dailyNoteService;
3070
3425
  availabilityService;
3071
3426
  membershipService;
@@ -3200,6 +3555,16 @@ var PeekAccessService = class {
3200
3555
  }
3201
3556
  return this.promoCodeService;
3202
3557
  }
3558
+ /**
3559
+ * Returns the {@link PricingService} for this install, bound to the shared
3560
+ * authenticated transport. The instance is created lazily and reused.
3561
+ */
3562
+ getPricingService() {
3563
+ if (!this.pricingService) {
3564
+ this.pricingService = new PricingService(this.client);
3565
+ }
3566
+ return this.pricingService;
3567
+ }
3203
3568
  /**
3204
3569
  * Returns the {@link DailyNoteService} for this install, bound to the shared
3205
3570
  * authenticated transport. The instance is created lazily and reused.
@@ -3320,6 +3685,27 @@ var PeekAccessService = class {
3320
3685
  createPromoCode(input) {
3321
3686
  return this.getPromoCodeService().create(input);
3322
3687
  }
3688
+ // ─── Pricing short-forms ──────────────────────────────────────────────────
3689
+ /** Create a pricing engine. Delegates to {@link PricingService.createEngine}. */
3690
+ createPricingEngine(input) {
3691
+ return this.getPricingService().createEngine(input);
3692
+ }
3693
+ /** Update a pricing engine. Delegates to {@link PricingService.updateEngine}. */
3694
+ updatePricingEngine(input) {
3695
+ return this.getPricingService().updateEngine(input);
3696
+ }
3697
+ /** Delete a pricing engine (idempotent). Delegates to {@link PricingService.deleteEngine}. */
3698
+ deletePricingEngine(engineId) {
3699
+ return this.getPricingService().deleteEngine(engineId);
3700
+ }
3701
+ /** Upsert pricing overrides. Delegates to {@link PricingService.upsertOverrides}. */
3702
+ upsertPricingOverrides(input) {
3703
+ return this.getPricingService().upsertOverrides(input);
3704
+ }
3705
+ /** Clear pricing overrides for a set of activities. Delegates to {@link PricingService.clearOverrides}. */
3706
+ clearPricingOverrides(input) {
3707
+ return this.getPricingService().clearOverrides(input);
3708
+ }
3323
3709
  // ─── Daily-note short-forms ───────────────────────────────────────────────
3324
3710
  /** Today's daily note. Delegates to {@link DailyNoteService.getToday}. */
3325
3711
  getDailyNoteToday() {
@@ -3781,6 +4167,6 @@ function extractWaiverNode(payload) {
3781
4167
  return record;
3782
4168
  }
3783
4169
 
3784
- export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, AccountUserService, AcmeAccessService, AcmeApiError, AcmeProductService, AdminAccountRequiredError, AvailabilityService, BookingService, CngAccessService, CngApiError, CngProductService, DailyNoteService, MembershipService, PeekAccessService, PeekGraphQLError, PiiAccessDisabledError, ProductService, PromoCodeService, RENTAL_PRODUCT_TYPE, RateLimitError, ResellerService, ResourcePoolService, ReviewService, TimeslotService, noopLogger, parseBookingWebhook, parseWaiverWebhook };
4170
+ export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, AccountUserService, AcmeAccessService, AcmeApiError, AcmeProductService, AdminAccountRequiredError, AvailabilityService, BookingService, CngAccessService, CngApiError, CngProductService, DailyNoteService, MembershipService, PeekAccessService, PeekGraphQLError, PiiAccessDisabledError, PricingService, ProductService, PromoCodeService, RENTAL_PRODUCT_TYPE, RateLimitError, ResellerService, ResourcePoolService, ReviewService, TimeslotService, noopLogger, parseBookingWebhook, parseWaiverWebhook };
3785
4171
  //# sourceMappingURL=index.js.map
3786
4172
  //# sourceMappingURL=index.js.map