@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.cjs CHANGED
@@ -2788,6 +2788,14 @@ function normalizeDate(date) {
2788
2788
  return date.split("T")[0] ?? date;
2789
2789
  }
2790
2790
 
2791
+ // src/internal/peek/money.ts
2792
+ function toPricingMoney(raw) {
2793
+ if (!raw) return null;
2794
+ const money = { amount: raw.amount, currency: raw.currency };
2795
+ if (raw.formatted != null) money.displayPrice = raw.formatted;
2796
+ return money;
2797
+ }
2798
+
2791
2799
  // src/internal/peek/products/product-converter.ts
2792
2800
  var ADD_ON_COLOR = "#FFFFFF";
2793
2801
  function fromActivities(activities) {
@@ -2800,9 +2808,12 @@ function fromActivity(activity) {
2800
2808
  name: activity.name,
2801
2809
  type: activity.type,
2802
2810
  color: activity.colorHex || "",
2811
+ currency: activity.currency || "",
2803
2812
  tickets: (activity.resourceOptions ?? []).map((option) => ({
2804
2813
  id: option.id,
2805
- name: option.name
2814
+ name: option.name,
2815
+ minPrice: toPricingMoney(option.priceRange?.min),
2816
+ maxPrice: toPricingMoney(option.priceRange?.max)
2806
2817
  }))
2807
2818
  };
2808
2819
  }
@@ -2818,11 +2829,17 @@ function fromItemOptionNodes(nodes) {
2818
2829
  name: node.item.name,
2819
2830
  type: ADD_ON_PRODUCT_TYPE,
2820
2831
  color: ADD_ON_COLOR,
2832
+ currency: "",
2821
2833
  tickets: []
2822
2834
  };
2823
2835
  grouped.set(itemId, product);
2824
2836
  }
2825
- product.tickets.push({ id: node.id, name: node.name });
2837
+ product.tickets.push({
2838
+ id: node.id,
2839
+ name: node.name,
2840
+ minPrice: null,
2841
+ maxPrice: null
2842
+ });
2826
2843
  }
2827
2844
  return Array.from(grouped.values());
2828
2845
  }
@@ -2837,9 +2854,22 @@ var PRODUCTS_QUERY = `
2837
2854
  deletedAt
2838
2855
  type
2839
2856
  colorHex
2857
+ currency
2840
2858
  resourceOptions {
2841
2859
  id
2842
2860
  name
2861
+ priceRange {
2862
+ min {
2863
+ currency
2864
+ amount
2865
+ formatted
2866
+ }
2867
+ max {
2868
+ currency
2869
+ amount
2870
+ formatted
2871
+ }
2872
+ }
2843
2873
  }
2844
2874
  }
2845
2875
  }
@@ -3063,6 +3093,330 @@ var PromoCodeService = class {
3063
3093
  }
3064
3094
  };
3065
3095
 
3096
+ // src/internal/peek/pricing/pricing-queries.ts
3097
+ var CREATE_PRICING_ENGINE_MUTATION = `
3098
+ mutation CreatePricingEngine($input: CreatePricingEngineInput!) {
3099
+ createPricingEngine(input: $input) {
3100
+ __typename
3101
+ ... on CreatePricingEngineSuccess {
3102
+ engine {
3103
+ id
3104
+ }
3105
+ }
3106
+ ... on InvalidDataError {
3107
+ message
3108
+ }
3109
+ }
3110
+ }
3111
+ `;
3112
+ var UPDATE_PRICING_ENGINE_MUTATION = `
3113
+ mutation UpdatePricingEngine($input: UpdatePricingEngineInput!) {
3114
+ updatePricingEngine(input: $input) {
3115
+ __typename
3116
+ ... on UpdatePricingEngineSuccess {
3117
+ engine {
3118
+ id
3119
+ name
3120
+ }
3121
+ }
3122
+ ... on InvalidDataError {
3123
+ message
3124
+ }
3125
+ ... on NotFoundError {
3126
+ id
3127
+ message
3128
+ }
3129
+ }
3130
+ }
3131
+ `;
3132
+ var DELETE_PRICING_ENGINE_MUTATION = `
3133
+ mutation DeletePricingEngine($id: ID!) {
3134
+ deletePricingEngine(id: $id) {
3135
+ __typename
3136
+ ... on DeletePricingEngineSuccess {
3137
+ engine {
3138
+ id
3139
+ name
3140
+ }
3141
+ }
3142
+ ... on NotFoundError {
3143
+ id
3144
+ message
3145
+ }
3146
+ }
3147
+ }
3148
+ `;
3149
+ var UPSERT_PRICING_OVERRIDES_MUTATION = `
3150
+ mutation UpsertPricingOverridesActivityContexts($input: UpsertPricingOverridesActivityContextsInput!) {
3151
+ upsertPricingOverridesActivityContexts(input: $input) {
3152
+ __typename
3153
+ ... on UpsertPricingOverridesActivityContextsSuccess {
3154
+ activityContexts {
3155
+ date
3156
+ activity {
3157
+ id
3158
+ name
3159
+ }
3160
+ engine {
3161
+ id
3162
+ name
3163
+ }
3164
+ overrides {
3165
+ order
3166
+ resourceOptions {
3167
+ resourceOption {
3168
+ id
3169
+ name
3170
+ }
3171
+ override {
3172
+ ... on PricingOverridesActivityContextResourceOptionPriceOverride {
3173
+ price {
3174
+ amount
3175
+ currency
3176
+ formatted
3177
+ }
3178
+ }
3179
+ ... on PricingOverridesActivityContextResourceOptionPercentageAdjustmentOverride {
3180
+ percentageAdjustment
3181
+ }
3182
+ }
3183
+ }
3184
+ filters {
3185
+ __typename
3186
+ ... on PricingOverridesActivityContextStartTimeRangeFilter {
3187
+ startTimeRange
3188
+ }
3189
+ ... on PricingOverridesActivityContextSpotsTakenFilter {
3190
+ minSpots
3191
+ maxSpots
3192
+ }
3193
+ }
3194
+ }
3195
+ }
3196
+ }
3197
+ ... on InvalidDataError {
3198
+ message
3199
+ }
3200
+ }
3201
+ }
3202
+ `;
3203
+ var SPOTS_TAKEN_FILTER_TYPENAME = "PricingOverridesActivityContextSpotsTakenFilter";
3204
+ function buildCreateEngineInput(input) {
3205
+ const activityIds = input.activityIds ?? [];
3206
+ return activityIds.length > 0 ? { name: input.name, filters: [{ activityIds }] } : { name: input.name };
3207
+ }
3208
+ function buildUpdateEngineInput(input) {
3209
+ const activityIds = input.activityIds ?? [];
3210
+ return {
3211
+ id: input.engineId,
3212
+ name: input.name,
3213
+ filters: activityIds.length > 0 ? [{ activityIds }] : []
3214
+ };
3215
+ }
3216
+ function buildUpsertInput(input) {
3217
+ return {
3218
+ engineId: input.engineId,
3219
+ dateRange: input.dateRange,
3220
+ activities: input.activities.map((activity) => ({
3221
+ activityId: activity.activityId,
3222
+ overrides: activity.overrides.map((override) => ({
3223
+ order: override.order,
3224
+ resourceOptions: override.resourceOptions.map(
3225
+ (option) => option.mode === "fixed" ? {
3226
+ id: option.id,
3227
+ // Send only the wire fields — `displayPrice` is read-only.
3228
+ price: { amount: option.price.amount, currency: option.price.currency }
3229
+ } : { id: option.id, percentageAdjustment: option.percentageAdjustment }
3230
+ ),
3231
+ filters: override.filters
3232
+ }))
3233
+ }))
3234
+ };
3235
+ }
3236
+ function buildClearInput(input) {
3237
+ return {
3238
+ engineId: input.engineId,
3239
+ dateRange: input.dateRange,
3240
+ activities: input.activityIds.map((activityId) => ({
3241
+ activityId,
3242
+ overrides: []
3243
+ }))
3244
+ };
3245
+ }
3246
+
3247
+ // src/internal/peek/pricing/pricing-converter.ts
3248
+ function fromActivityContexts(contexts) {
3249
+ return contexts.map(fromActivityContext);
3250
+ }
3251
+ function fromActivityContext(context) {
3252
+ return {
3253
+ date: context.date,
3254
+ activity: { id: context.activity.id, name: context.activity.name },
3255
+ engine: { id: context.engine.id, name: context.engine.name },
3256
+ overrides: context.overrides.map(fromOverride)
3257
+ };
3258
+ }
3259
+ function fromOverride(override) {
3260
+ return {
3261
+ order: override.order,
3262
+ resourceOptions: override.resourceOptions.map(fromResourceOption),
3263
+ filters: override.filters.map(fromFilter)
3264
+ };
3265
+ }
3266
+ function fromResourceOption(option) {
3267
+ return {
3268
+ resourceOption: {
3269
+ id: option.resourceOption.id,
3270
+ name: option.resourceOption.name
3271
+ },
3272
+ override: fromResolvedOverride(option.override)
3273
+ };
3274
+ }
3275
+ function fromResolvedOverride(override) {
3276
+ return "price" in override ? { mode: "fixed", price: toPricingMoney(override.price) } : { mode: "percentage", percentageAdjustment: override.percentageAdjustment };
3277
+ }
3278
+ function fromFilter(filter) {
3279
+ if (filter.__typename === SPOTS_TAKEN_FILTER_TYPENAME) {
3280
+ const spotsTaken = {};
3281
+ if (filter.minSpots !== null) spotsTaken.minSpots = filter.minSpots;
3282
+ if (filter.maxSpots !== null) spotsTaken.maxSpots = filter.maxSpots;
3283
+ return { spotsTaken };
3284
+ }
3285
+ return { startTimeRange: filter.startTimeRange };
3286
+ }
3287
+
3288
+ // src/internal/peek/pricing/pricing-service.ts
3289
+ var CURRENCY_PATTERN2 = /^[A-Z]{3}$/;
3290
+ var PERCENTAGE_FLOOR = -100;
3291
+ var ERROR_NAME_REQUIRED = "name is required";
3292
+ var ERROR_ENGINE_ID_REQUIRED = "engineId is required";
3293
+ var ERROR_DATE_RANGE_REQUIRED = "dateRange is required";
3294
+ var ERROR_ACTIVITIES_REQUIRED = "activities is required";
3295
+ var ERROR_ACTIVITY_ID_REQUIRED = "activityId is required";
3296
+ var ERROR_TICKET_ID_REQUIRED = "resource-option id is required";
3297
+ var ERROR_CURRENCY_FORMAT = "currency must be 3 uppercase letters";
3298
+ var ERROR_AMOUNT_NUMBER = "price amount must be a valid number";
3299
+ var ERROR_PERCENTAGE_NUMBER = "percentageAdjustment must be a valid number";
3300
+ var ERROR_PERCENTAGE_FLOOR = "percentageAdjustment must be greater than -100";
3301
+ var ERROR_NO_DATA = "pricing mutation returned no data";
3302
+ var PricingService = class {
3303
+ constructor(client) {
3304
+ this.client = client;
3305
+ }
3306
+ client;
3307
+ /**
3308
+ * Creates a pricing engine. Store the returned id and reuse it on every
3309
+ * subsequent override upsert. Throws on an InvalidDataError from Peek.
3310
+ */
3311
+ async createEngine(input) {
3312
+ if (!input.name) throw new Error(ERROR_NAME_REQUIRED);
3313
+ const body = await this.client.request(
3314
+ SALES_ENDPOINT,
3315
+ CREATE_PRICING_ENGINE_MUTATION,
3316
+ { input: buildCreateEngineInput(input) }
3317
+ );
3318
+ const result = body.data?.createPricingEngine;
3319
+ if (!result) throw new Error(ERROR_NO_DATA);
3320
+ if (result.__typename !== "CreatePricingEngineSuccess") {
3321
+ throw new Error(result.message);
3322
+ }
3323
+ return { id: result.engine.id };
3324
+ }
3325
+ /**
3326
+ * Updates a pricing engine's name and/or activity scope. Throws on an
3327
+ * InvalidDataError or NotFoundError from Peek.
3328
+ */
3329
+ async updateEngine(input) {
3330
+ if (!input.engineId) throw new Error(ERROR_ENGINE_ID_REQUIRED);
3331
+ if (!input.name) throw new Error(ERROR_NAME_REQUIRED);
3332
+ const body = await this.client.request(
3333
+ SALES_ENDPOINT,
3334
+ UPDATE_PRICING_ENGINE_MUTATION,
3335
+ { input: buildUpdateEngineInput(input) }
3336
+ );
3337
+ const result = body.data?.updatePricingEngine;
3338
+ if (!result) throw new Error(ERROR_NO_DATA);
3339
+ if (result.__typename !== "UpdatePricingEngineSuccess") {
3340
+ throw new Error(result.message);
3341
+ }
3342
+ return { id: result.engine.id, name: result.engine.name };
3343
+ }
3344
+ /**
3345
+ * Deletes a pricing engine. Idempotent — resolves whether the engine existed
3346
+ * or was already gone (Peek's NotFoundError is treated as success).
3347
+ */
3348
+ async deleteEngine(engineId) {
3349
+ if (!engineId) throw new Error(ERROR_ENGINE_ID_REQUIRED);
3350
+ await this.client.request(
3351
+ SALES_ENDPOINT,
3352
+ DELETE_PRICING_ENGINE_MUTATION,
3353
+ { id: engineId }
3354
+ );
3355
+ }
3356
+ /**
3357
+ * Upserts pricing overrides for the engine and date range in `input`. Returns
3358
+ * the resolved contexts Peek stored. Throws on an InvalidDataError from Peek.
3359
+ */
3360
+ async upsertOverrides(input) {
3361
+ this.validateUpsertInput(input);
3362
+ return this.sendUpsert(buildUpsertInput(input));
3363
+ }
3364
+ /**
3365
+ * Clears all overrides for the given activities across a date range. This is
3366
+ * an upsert with empty overrides — never clear by omitting an activity.
3367
+ */
3368
+ async clearOverrides(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.activityIds)) throw new Error(ERROR_ACTIVITIES_REQUIRED);
3372
+ for (const activityId of input.activityIds) {
3373
+ if (!activityId) throw new Error(ERROR_ACTIVITY_ID_REQUIRED);
3374
+ }
3375
+ return this.sendUpsert(buildClearInput(input));
3376
+ }
3377
+ async sendUpsert(rawInput) {
3378
+ const body = await this.client.request(
3379
+ SALES_ENDPOINT,
3380
+ UPSERT_PRICING_OVERRIDES_MUTATION,
3381
+ { input: rawInput }
3382
+ );
3383
+ const result = body.data?.upsertPricingOverridesActivityContexts;
3384
+ if (!result) throw new Error(ERROR_NO_DATA);
3385
+ if (result.__typename !== "UpsertPricingOverridesActivityContextsSuccess") {
3386
+ throw new Error(result.message);
3387
+ }
3388
+ return { activityContexts: fromActivityContexts(result.activityContexts) };
3389
+ }
3390
+ validateUpsertInput(input) {
3391
+ if (!input.engineId) throw new Error(ERROR_ENGINE_ID_REQUIRED);
3392
+ if (!input.dateRange) throw new Error(ERROR_DATE_RANGE_REQUIRED);
3393
+ if (!Array.isArray(input.activities)) throw new Error(ERROR_ACTIVITIES_REQUIRED);
3394
+ for (const activity of input.activities) {
3395
+ if (!activity.activityId) throw new Error(ERROR_ACTIVITY_ID_REQUIRED);
3396
+ for (const override of activity.overrides) {
3397
+ for (const option of override.resourceOptions) {
3398
+ this.validateResourceOption(option);
3399
+ }
3400
+ }
3401
+ }
3402
+ }
3403
+ validateResourceOption(option) {
3404
+ if (!option.id) throw new Error(ERROR_TICKET_ID_REQUIRED);
3405
+ if (option.mode === "fixed") {
3406
+ if (!CURRENCY_PATTERN2.test(option.price.currency)) {
3407
+ throw new Error(ERROR_CURRENCY_FORMAT);
3408
+ }
3409
+ if (Number.isNaN(Number.parseFloat(option.price.amount))) {
3410
+ throw new Error(ERROR_AMOUNT_NUMBER);
3411
+ }
3412
+ return;
3413
+ }
3414
+ const percentage = Number.parseFloat(option.percentageAdjustment);
3415
+ if (Number.isNaN(percentage)) throw new Error(ERROR_PERCENTAGE_NUMBER);
3416
+ if (percentage <= PERCENTAGE_FLOOR) throw new Error(ERROR_PERCENTAGE_FLOOR);
3417
+ }
3418
+ };
3419
+
3066
3420
  // src/logger.ts
3067
3421
  var noopLogger = {
3068
3422
  info() {
@@ -3088,6 +3442,7 @@ var PeekAccessService = class {
3088
3442
  timeslotService;
3089
3443
  resellerService;
3090
3444
  promoCodeService;
3445
+ pricingService;
3091
3446
  dailyNoteService;
3092
3447
  availabilityService;
3093
3448
  membershipService;
@@ -3222,6 +3577,16 @@ var PeekAccessService = class {
3222
3577
  }
3223
3578
  return this.promoCodeService;
3224
3579
  }
3580
+ /**
3581
+ * Returns the {@link PricingService} for this install, bound to the shared
3582
+ * authenticated transport. The instance is created lazily and reused.
3583
+ */
3584
+ getPricingService() {
3585
+ if (!this.pricingService) {
3586
+ this.pricingService = new PricingService(this.client);
3587
+ }
3588
+ return this.pricingService;
3589
+ }
3225
3590
  /**
3226
3591
  * Returns the {@link DailyNoteService} for this install, bound to the shared
3227
3592
  * authenticated transport. The instance is created lazily and reused.
@@ -3342,6 +3707,27 @@ var PeekAccessService = class {
3342
3707
  createPromoCode(input) {
3343
3708
  return this.getPromoCodeService().create(input);
3344
3709
  }
3710
+ // ─── Pricing short-forms ──────────────────────────────────────────────────
3711
+ /** Create a pricing engine. Delegates to {@link PricingService.createEngine}. */
3712
+ createPricingEngine(input) {
3713
+ return this.getPricingService().createEngine(input);
3714
+ }
3715
+ /** Update a pricing engine. Delegates to {@link PricingService.updateEngine}. */
3716
+ updatePricingEngine(input) {
3717
+ return this.getPricingService().updateEngine(input);
3718
+ }
3719
+ /** Delete a pricing engine (idempotent). Delegates to {@link PricingService.deleteEngine}. */
3720
+ deletePricingEngine(engineId) {
3721
+ return this.getPricingService().deleteEngine(engineId);
3722
+ }
3723
+ /** Upsert pricing overrides. Delegates to {@link PricingService.upsertOverrides}. */
3724
+ upsertPricingOverrides(input) {
3725
+ return this.getPricingService().upsertOverrides(input);
3726
+ }
3727
+ /** Clear pricing overrides for a set of activities. Delegates to {@link PricingService.clearOverrides}. */
3728
+ clearPricingOverrides(input) {
3729
+ return this.getPricingService().clearOverrides(input);
3730
+ }
3345
3731
  // ─── Daily-note short-forms ───────────────────────────────────────────────
3346
3732
  /** Today's daily note. Delegates to {@link DailyNoteService.getToday}. */
3347
3733
  getDailyNoteToday() {
@@ -3820,6 +4206,7 @@ exports.MembershipService = MembershipService;
3820
4206
  exports.PeekAccessService = PeekAccessService;
3821
4207
  exports.PeekGraphQLError = PeekGraphQLError;
3822
4208
  exports.PiiAccessDisabledError = PiiAccessDisabledError;
4209
+ exports.PricingService = PricingService;
3823
4210
  exports.ProductService = ProductService;
3824
4211
  exports.PromoCodeService = PromoCodeService;
3825
4212
  exports.RENTAL_PRODUCT_TYPE = RENTAL_PRODUCT_TYPE;