@ophelio/sdk 0.3.1 → 0.4.0

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/README.md CHANGED
@@ -21,7 +21,10 @@ import { Ophelio } from '@ophelio/sdk'
21
21
  const ophelio = new Ophelio({ apiKey: process.env.OPHELIO_API_KEY })
22
22
 
23
23
  // Gate admission check
24
- const result = await ophelio.admit.check({ card: 'M-123456' })
24
+ const result = await ophelio.admit.check({
25
+ card: 'M-123456',
26
+ targets: ['venue_a'], // where this gate is; optional
27
+ })
25
28
  if (result.admitted) {
26
29
  console.log(result.pricing_group) // e.g. 'member_adult'
27
30
  }
@@ -154,26 +157,27 @@ member roles, pricing groups, sales channels, entitlements, plans and
154
157
 
155
158
  Everything reachable with an API key is covered:
156
159
 
157
- | Namespace | Methods |
158
- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
159
- | `admit` | `check({ card })` |
160
- | `members` | `sync({ since? })`, `get(id)`, `listEntitlements(id, { type? })`, `update(id, ...)`, `remove(id)`, `setPrimary(id)`, `reissueCard(id, { note? })` |
161
- | `memberships` | `list(params?)`, `get(id)`, `create(...)`, `update(id, ...)`, `cancel(id, ...)`, `uncancel(id)`, `renew(id, ...)`, `upgrade(id, ...)`, `downgrade(id, ...)`, `listMembers(id)`, `addMember(id, ...)`, `listScheduledChanges(id, params?)`, `cancelScheduledChange(id, changeId)`, `listTransactions(id, params?)`, `listTermTransactions(id, termId, params?)`, `createTransaction(id, ...)` |
162
- | `customers` | `list(params?)`, `get(id, params?)`, `create(...)`, `update(id, ...)`, `delete(id)`, `listMemberships(id, params?)` — `list`/`get` accept `view: 'full'` to include `membership_summary` |
163
- | `entitlements` | `redeem(...)`, `recordUsage(...)`, `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)` |
164
- | `plans` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)`, `listBillingOptions(id)`, `createBillingOption(id, ...)`, `listEntitlements(id)`, `createEntitlement(id, ...)`, `listPlanLinks(id)`, `createPlanLink(id, ...)`, `listPricingGroupMappings(id)`, `createPricingGroupMapping(id, ...)` |
165
- | `billingOptions` | `list()`, `get(id)`, `update(id, ...)`, `delete(id)`, `listSalesChannels(id)`, `createSalesChannelLink(id, ...)` |
166
- | `memberRoles` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)` |
167
- | `pricingGroups` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)` |
168
- | `salesChannels` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)` |
169
- | `targetCodes` | `list(params?)`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)` — `update` takes `display_name` / `description` only; `code` is immutable |
170
- | `calendars` | `list(params?)`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)`, `listRules(id, params?)`, `createRule(id, ...)` |
171
- | `calendarRules` | `get(id)`, `update(id, ...)`, `delete(id)` |
172
- | `planEntitlements` | `get(id)`, `update(id, ...)`, `delete(id)` |
173
- | `planLinks` | `delete(id)` |
174
- | `planPricingGroupMappings` | `delete(id)` |
175
- | `billingOptionSalesChannels` | `delete(id)` |
176
- | `webhookSubscriptions` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)`, `listDeliveries(id, params?)` |
160
+ | Namespace | Methods |
161
+ | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
162
+ | `admit` | `check({ card, targets? })` |
163
+ | `members` | `sync({ since? })`, `get(id)`, `listEntitlements(id, { type?, targets? })`, `update(id, ...)`, `remove(id)`, `setPrimary(id)`, `reissueCard(id, { note? })` |
164
+ | `memberships` | `list(params?)`, `get(id)`, `create(...)`, `update(id, ...)`, `cancel(id, ...)`, `uncancel(id)`, `renew(id, ...)`, `upgrade(id, ...)`, `downgrade(id, ...)`, `listMembers(id)`, `addMember(id, ...)`, `listScheduledChanges(id, params?)`, `cancelScheduledChange(id, changeId)`, `listTransactions(id, params?)`, `listTermTransactions(id, termId, params?)`, `createTransaction(id, ...)` |
165
+ | `customers` | `list(params?)`, `get(id, params?)`, `create(...)`, `update(id, ...)`, `delete(id)`, `listMemberships(id, params?)` — `list`/`get` accept `view: 'full'` to include `membership_summary` |
166
+ | `entitlements` | `redeem(...)`, `recordUsage(...)`, `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)` |
167
+ | `plans` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)`, `listBillingOptions(id)`, `createBillingOption(id, ...)`, `listEntitlements(id)`, `createEntitlement(id, ...)`, `listPlanLinks(id)`, `createPlanLink(id, ...)`, `listPricingGroupMappings(id)`, `createPricingGroupMapping(id, ...)` |
168
+ | `billingOptions` | `list()`, `get(id)`, `update(id, ...)`, `delete(id)`, `listSalesChannels(id)`, `createSalesChannelLink(id, ...)` |
169
+ | `memberRoles` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)` |
170
+ | `pricingGroups` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)` |
171
+ | `salesChannels` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)` |
172
+ | `targetCodes` | `list(params?)`, `get(id, params?)`, `create(...)`, `update(id, ...)`, `delete(id)` — `list`/`get` accept `view: 'full'` to include `plan_entitlement_count`; `update` takes `display_name` / `description` only, as `code` is immutable |
173
+ | `calendars` | `list(params?)`, `get(id, params?)`, `create(...)`, `update(id, ...)`, `delete(id)`, `listRules(id, params?)`, `createRule(id, ...)` — `list`/`get` accept `view: 'full'` to include `plan_entitlement_count` |
174
+ | `calendarRules` | `get(id)`, `update(id, ...)`, `delete(id)` |
175
+ | `planEntitlements` | `get(id)`, `update(id, ...)`, `delete(id)`, `listAvailabilities(id, params?)`, `createAvailability(id, ...)` |
176
+ | `planEntitlementAvailabilities` | `get(id)`, `update(id, ...)`, `delete(id)` |
177
+ | `planLinks` | `delete(id)` |
178
+ | `planPricingGroupMappings` | `delete(id)` |
179
+ | `billingOptionSalesChannels` | `delete(id)` |
180
+ | `webhookSubscriptions` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)`, `listDeliveries(id, params?)` |
177
181
 
178
182
  Every method accepts a trailing `options` argument: `{ signal?, headers? }`
179
183
  (plus `idempotencyKey?` on the idempotency-keyed mutations: `memberships.create`
@@ -208,8 +212,10 @@ A calendar containing no rules is valid for **all** dates, not none.
208
212
 
209
213
  ### Target codes
210
214
 
211
- The registered vocabulary of locations a gate reports (`venue_a`,
212
- `coffee_shop`). A `code` is lowercase letters and digits with single
215
+ A registered code for each venue, zone, shop or gate that accepts
216
+ memberships (`venue_a`, `coffee_shop`). Register each one once, then name it
217
+ wherever a benefit is restricted, and wherever a request says where it is
218
+ coming from. A `code` is lowercase letters and digits with single
213
219
  underscores between segments, at least one letter, 2–64 characters — and it is
214
220
  **matched exactly**. A miscased code is an unregistered code, so send values
215
221
  back verbatim from `list()` rather than constructing or case-folding them.
@@ -232,3 +238,70 @@ await ophelio.targetCodes.update(venue.id, { display_name: 'Venue A (west)' })
232
238
 
233
239
  To correct a genuine typo, create the right code, repoint whatever uses it, and
234
240
  delete the old one.
241
+
242
+ ### Plan entitlement availabilities
243
+
244
+ An availability row says when and where one plan entitlement applies: a
245
+ `calendar_id` (null means any date) and `targets`, a set of registered target
246
+ codes (an empty array means anywhere).
247
+
248
+ ```ts
249
+ await ophelio.planEntitlements.createAvailability(planEntitlement.id, {
250
+ calendar_id: peak.id,
251
+ targets: ['venue_a'],
252
+ })
253
+ ```
254
+
255
+ Rows **OR**, and the absence of rows is judged **per plan entitlement**: a plan
256
+ entitlement with no rows at all is unrestricted, never restricted to nothing.
257
+ Every code in `targets` must already be registered as a target code — an
258
+ unregistered one is rejected, never registered on the fly.
259
+
260
+ ### Target context
261
+
262
+ A read can say where it is being made, and the entitlements it gets back are
263
+ judged against the availability rows in that light. `admit.check` and
264
+ `members.listEntitlements` take `targets` — a set of registered target codes,
265
+ sent comma-separated on the wire — and `entitlements.recordUsage` takes a
266
+ singular `target`, because one usage event happens at one place.
267
+
268
+ ```ts
269
+ const result = await ophelio.admit.check({
270
+ card: 'M-123456',
271
+ targets: ['venue_a', 'coffee_shop'],
272
+ })
273
+
274
+ result.target_context_applied // true — the entitlements were target-filtered
275
+
276
+ for (const entitlement of result.entitlements) {
277
+ entitlement.valid_at // ['coffee_shop'] — where it is valid, when restricted
278
+ entitlement.valid_on // the date windows it matched, when restricted
279
+ }
280
+ ```
281
+
282
+ **Three states, answered differently.** Omitting `targets` is the permissive
283
+ default: every target restriction is treated as satisfied, and
284
+ `target_context_applied` comes back `false`. Passing `[]` says the caller is
285
+ naming no targets, so only benefits valid anywhere survive. Passing codes
286
+ means a benefit needs one of them. Dates are enforced in all three.
287
+
288
+ **Codes are matched exactly.** Send them back verbatim from
289
+ `targetCodes.list()`; an unregistered or miscased code is refused with a 400
290
+ rather than dropped, so a typo cannot pass for a benefit that does not apply.
291
+
292
+ **The parameter means something different on each surface.** At the gate it is
293
+ the context the decision is made in; on `members.listEntitlements` it is a
294
+ filter over the result. `members.sync` takes no target context at all — the
295
+ offline roster carries each member's entitlements as at the moment of sync,
296
+ and the gate that caches it evaluates no rules of its own.
297
+
298
+ `valid_on` and `valid_at` carry only what an entitlement **matched**, and are
299
+ omitted entirely when nothing restricts it on that axis. What it failed is
300
+ never returned: the response says where and when a benefit is valid, never
301
+ which restrictions the member did not satisfy. Because rows OR, the two read
302
+ as independent unions rather than as pairs.
303
+
304
+ A project can require target context — once every gate reports its own, it is
305
+ turned on and a read that says nothing about where it is is refused from then
306
+ on. It is a per-project setting rather than anything the client sends, so roll
307
+ out sending `targets` before expecting it to be enforced.
package/dist/index.cjs CHANGED
@@ -126,6 +126,21 @@ const DEFAULT_MAX_RETRIES = 2;
126
126
  function resolveIdempotencyKey(idempotencyKey) {
127
127
  return idempotencyKey?.trim() || crypto.randomUUID();
128
128
  }
129
+ /**
130
+ * Serialises query parameters into a search string.
131
+ *
132
+ * `undefined` drops the parameter entirely. An empty array does not: it emits
133
+ * the bare key (`?status=`), because "filter by nothing" and "do not filter"
134
+ * are different requests and the API distinguishes them.
135
+ */
136
+ function serialiseQuery(query) {
137
+ const search = new URLSearchParams();
138
+ for (const [key, value] of Object.entries(query ?? {})) {
139
+ if (value === void 0) continue;
140
+ search.set(key, Array.isArray(value) ? value.join(",") : value);
141
+ }
142
+ return search.toString();
143
+ }
129
144
  var Transport = class {
130
145
  apiKey;
131
146
  baseUrl;
@@ -180,9 +195,7 @@ var Transport = class {
180
195
  throw lastError;
181
196
  }
182
197
  buildUrl(params) {
183
- const search = new URLSearchParams();
184
- for (const [key, value] of Object.entries(params.query ?? {})) if (value !== void 0) search.set(key, value);
185
- const queryString = search.toString();
198
+ const queryString = serialiseQuery(params.query);
186
199
  return `${this.baseUrl}${params.path}${queryString ? `?${queryString}` : ""}`;
187
200
  }
188
201
  buildHeaders(params) {
@@ -235,12 +248,30 @@ function pageQuery(params) {
235
248
  //#endregion
236
249
  //#region src/resources/admit.ts
237
250
  var AdmitResource = class extends APIResource {
238
- /** Gate admission check: `GET /admit?card=<cardId>`. */
251
+ /**
252
+ * Gate admission check: `GET /admit?card=<cardId>`.
253
+ *
254
+ * `targets` names the venue, zone, shop or gate the request is being made
255
+ * at, and is the context the entitlements are judged in — a benefit
256
+ * restricted to somewhere else drops out of `entitlements`. Three states,
257
+ * and they are answered differently: omit it and every target restriction
258
+ * is treated as satisfied; pass `[]` and only benefits valid anywhere
259
+ * survive; pass codes and a benefit needs one of them. Read
260
+ * `target_context_applied` on the result to tell the first apart from the
261
+ * other two.
262
+ *
263
+ * Codes are matched exactly, so send them back verbatim from
264
+ * `targetCodes.list()` — an unregistered or miscased code is refused with
265
+ * a 400 rather than quietly ignored.
266
+ */
239
267
  check(params, options) {
240
268
  return this.transport.request({
241
269
  method: "GET",
242
270
  path: "/api/v1/admit",
243
- query: { card: params.card },
271
+ query: {
272
+ card: params.card,
273
+ targets: params.targets
274
+ },
244
275
  retryable: true,
245
276
  options
246
277
  });
@@ -316,7 +347,7 @@ var BillingOptionsResource = class extends APIResource {
316
347
  //#endregion
317
348
  //#region src/resources/calendar-rules.ts
318
349
  /**
319
- * A calendar rule is one row of a calendar's date vocabulary. Conditions AND
350
+ * A calendar rule is one row of a calendar's set of dates. Conditions AND
320
351
  * within a rule and rules OR across a calendar, so "weekends in August" is a
321
352
  * single rule rather than two.
322
353
  *
@@ -354,17 +385,24 @@ var CalendarRulesResource = class extends APIResource {
354
385
  //#endregion
355
386
  //#region src/resources/calendars.ts
356
387
  /**
357
- * Calendars are named, reusable date vocabularies ("Peak 2026", "Weekends").
388
+ * Calendars are named, reusable sets of dates ("Peak 2026", "Weekends").
358
389
  * The dates themselves live in the rules attached to them.
359
390
  *
360
391
  * A calendar containing no rules is valid for **all** dates, not none.
361
392
  */
362
393
  var CalendarsResource = class extends APIResource {
394
+ /**
395
+ * Pass `view: 'full'` to include `plan_entitlement_count` on every
396
+ * calendar.
397
+ */
363
398
  async list(params, options) {
364
399
  const response = await this.transport.request({
365
400
  method: "GET",
366
401
  path: "/api/v1/calendars",
367
- query: pageQuery(params),
402
+ query: {
403
+ ...pageQuery(params),
404
+ ...params?.view ? { view: params.view } : {}
405
+ },
368
406
  retryable: true,
369
407
  options
370
408
  });
@@ -374,10 +412,14 @@ var CalendarsResource = class extends APIResource {
374
412
  total_size: response.total_size
375
413
  };
376
414
  }
377
- get(id, options) {
415
+ /**
416
+ * Pass `view: 'full'` to include `plan_entitlement_count`.
417
+ */
418
+ get(id, params = {}, options) {
378
419
  return this.transport.request({
379
420
  method: "GET",
380
421
  path: `/api/v1/calendars/${encodeURIComponent(id)}`,
422
+ query: params.view ? { view: params.view } : void 0,
381
423
  retryable: true,
382
424
  options
383
425
  });
@@ -530,6 +572,14 @@ var EntitlementsResource = class extends APIResource {
530
572
  /**
531
573
  * Fire-and-forget batch usage reporting (one row per member). Idempotent
532
574
  * per (key, member). Returns the recorded usage rows.
575
+ *
576
+ * `target` is the single registered code for the venue, zone, shop or gate
577
+ * the usage happened at — one event, one place, which is why it is
578
+ * singular here where the reads take `targets`. It is both the context the
579
+ * entitlement is judged in and a column on the row that comes back, so it
580
+ * is queryable in a way the free-form `context` is not. It is part of the
581
+ * idempotency fingerprint: a replay of the same key at a different target
582
+ * is refused rather than silently returning the original row.
533
583
  */
534
584
  async recordUsage(params, options = {}) {
535
585
  const { idempotencyKey, ...requestOptions } = options;
@@ -655,11 +705,29 @@ var MembersResource = class extends APIResource {
655
705
  options
656
706
  });
657
707
  }
708
+ /**
709
+ * The entitlements a member can use right now.
710
+ *
711
+ * `targets` filters the result down to the benefits valid at the venue,
712
+ * zone, shop or gate it names — the same codes `admit.check` takes, but
713
+ * read as a filter here rather than as the context a gate decision is made
714
+ * in. Three states: omit it and nothing is filtered by target; pass `[]`
715
+ * and only benefits valid anywhere come back; pass codes and a benefit
716
+ * needs one of them. Codes are matched exactly, so send them back verbatim
717
+ * from `targetCodes.list()` — an unregistered one is refused with a 400.
718
+ *
719
+ * Each entitlement carries `valid_on` and `valid_at` when something
720
+ * restricts it by date or by target; both list only what it matched, never
721
+ * what it failed.
722
+ */
658
723
  async listEntitlements(id, params = {}, options) {
659
724
  return (await this.transport.request({
660
725
  method: "GET",
661
726
  path: `/api/v1/members/${encodeURIComponent(id)}/entitlements`,
662
- query: { type: params.type },
727
+ query: {
728
+ type: params.type,
729
+ targets: params.targets
730
+ },
663
731
  retryable: true,
664
732
  options
665
733
  })).entitlements;
@@ -918,6 +986,45 @@ var MembershipsResource = class extends APIResource {
918
986
  }
919
987
  };
920
988
  //#endregion
989
+ //#region src/resources/plan-entitlement-availabilities.ts
990
+ /**
991
+ * An availability row says when and where one plan entitlement applies: a
992
+ * calendar (null = any date) and a set of registered target codes (empty =
993
+ * anywhere). Rows OR, and a plan entitlement with no rows at all is
994
+ * unrestricted.
995
+ *
996
+ * Rows are created and listed through their plan entitlement
997
+ * (`ophelio.planEntitlements.createAvailability` / `listAvailabilities`); this
998
+ * resource addresses an individual row.
999
+ */
1000
+ var PlanEntitlementAvailabilitiesResource = class extends APIResource {
1001
+ get(id, options) {
1002
+ return this.transport.request({
1003
+ method: "GET",
1004
+ path: `/api/v1/plan-entitlement-availabilities/${encodeURIComponent(id)}`,
1005
+ retryable: true,
1006
+ options
1007
+ });
1008
+ }
1009
+ update(id, params, options) {
1010
+ return this.transport.request({
1011
+ method: "PATCH",
1012
+ path: `/api/v1/plan-entitlement-availabilities/${encodeURIComponent(id)}`,
1013
+ body: params,
1014
+ retryable: false,
1015
+ options
1016
+ });
1017
+ }
1018
+ async delete(id, options) {
1019
+ await this.transport.request({
1020
+ method: "DELETE",
1021
+ path: `/api/v1/plan-entitlement-availabilities/${encodeURIComponent(id)}`,
1022
+ retryable: false,
1023
+ options
1024
+ });
1025
+ }
1026
+ };
1027
+ //#endregion
921
1028
  //#region src/resources/plan-entitlements.ts
922
1029
  var PlanEntitlementsResource = class extends APIResource {
923
1030
  get(id, options) {
@@ -946,6 +1053,29 @@ var PlanEntitlementsResource = class extends APIResource {
946
1053
  options
947
1054
  });
948
1055
  }
1056
+ async listAvailabilities(id, params, options) {
1057
+ const response = await this.transport.request({
1058
+ method: "GET",
1059
+ path: `/api/v1/plan-entitlements/${encodeURIComponent(id)}/availabilities`,
1060
+ query: pageQuery(params),
1061
+ retryable: true,
1062
+ options
1063
+ });
1064
+ return {
1065
+ items: response.availabilities,
1066
+ next_page_token: response.next_page_token,
1067
+ total_size: response.total_size
1068
+ };
1069
+ }
1070
+ createAvailability(id, params, options) {
1071
+ return this.transport.request({
1072
+ method: "POST",
1073
+ path: `/api/v1/plan-entitlements/${encodeURIComponent(id)}/availabilities`,
1074
+ body: params,
1075
+ retryable: false,
1076
+ options
1077
+ });
1078
+ }
949
1079
  };
950
1080
  //#endregion
951
1081
  //#region src/resources/plan-links.ts
@@ -1182,16 +1312,23 @@ var SalesChannelsResource = class extends APIResource {
1182
1312
  //#endregion
1183
1313
  //#region src/resources/target-codes.ts
1184
1314
  /**
1185
- * The registered vocabulary of locations a gate can report (`venue_a`,
1186
- * `coffee_shop`). Codes are matched exactly — list them and send values back
1187
- * verbatim rather than constructing or case-folding them.
1315
+ * The registered code for each venue, zone, shop or gate a request can name
1316
+ * (`venue_a`, `coffee_shop`). Codes are matched exactly — list them and send
1317
+ * values back verbatim rather than constructing or case-folding them.
1188
1318
  */
1189
1319
  var TargetCodesResource = class extends APIResource {
1320
+ /**
1321
+ * Pass `view: 'full'` to include `plan_entitlement_count` on every target
1322
+ * code.
1323
+ */
1190
1324
  async list(params, options) {
1191
1325
  const response = await this.transport.request({
1192
1326
  method: "GET",
1193
1327
  path: "/api/v1/target-codes",
1194
- query: pageQuery(params),
1328
+ query: {
1329
+ ...pageQuery(params),
1330
+ ...params?.view ? { view: params.view } : {}
1331
+ },
1195
1332
  retryable: true,
1196
1333
  options
1197
1334
  });
@@ -1201,10 +1338,14 @@ var TargetCodesResource = class extends APIResource {
1201
1338
  total_size: response.total_size
1202
1339
  };
1203
1340
  }
1204
- get(id, options) {
1341
+ /**
1342
+ * Pass `view: 'full'` to include `plan_entitlement_count`.
1343
+ */
1344
+ get(id, params = {}, options) {
1205
1345
  return this.transport.request({
1206
1346
  method: "GET",
1207
1347
  path: `/api/v1/target-codes/${encodeURIComponent(id)}`,
1348
+ query: params.view ? { view: params.view } : void 0,
1208
1349
  retryable: true,
1209
1350
  options
1210
1351
  });
@@ -1376,6 +1517,7 @@ var Ophelio = class Ophelio {
1376
1517
  calendars;
1377
1518
  calendarRules;
1378
1519
  planEntitlements;
1520
+ planEntitlementAvailabilities;
1379
1521
  planLinks;
1380
1522
  planPricingGroupMappings;
1381
1523
  billingOptionSalesChannels;
@@ -1402,6 +1544,7 @@ var Ophelio = class Ophelio {
1402
1544
  this.calendars = new CalendarsResource(transport);
1403
1545
  this.calendarRules = new CalendarRulesResource(transport);
1404
1546
  this.planEntitlements = new PlanEntitlementsResource(transport);
1547
+ this.planEntitlementAvailabilities = new PlanEntitlementAvailabilitiesResource(transport);
1405
1548
  this.planLinks = new PlanLinksResource(transport);
1406
1549
  this.planPricingGroupMappings = new PlanPricingGroupMappingsResource(transport);
1407
1550
  this.billingOptionSalesChannels = new BillingOptionSalesChannelsResource(transport);