@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/dist/index.mjs CHANGED
@@ -125,6 +125,21 @@ const DEFAULT_MAX_RETRIES = 2;
125
125
  function resolveIdempotencyKey(idempotencyKey) {
126
126
  return idempotencyKey?.trim() || crypto.randomUUID();
127
127
  }
128
+ /**
129
+ * Serialises query parameters into a search string.
130
+ *
131
+ * `undefined` drops the parameter entirely. An empty array does not: it emits
132
+ * the bare key (`?status=`), because "filter by nothing" and "do not filter"
133
+ * are different requests and the API distinguishes them.
134
+ */
135
+ function serialiseQuery(query) {
136
+ const search = new URLSearchParams();
137
+ for (const [key, value] of Object.entries(query ?? {})) {
138
+ if (value === void 0) continue;
139
+ search.set(key, Array.isArray(value) ? value.join(",") : value);
140
+ }
141
+ return search.toString();
142
+ }
128
143
  var Transport = class {
129
144
  apiKey;
130
145
  baseUrl;
@@ -179,9 +194,7 @@ var Transport = class {
179
194
  throw lastError;
180
195
  }
181
196
  buildUrl(params) {
182
- const search = new URLSearchParams();
183
- for (const [key, value] of Object.entries(params.query ?? {})) if (value !== void 0) search.set(key, value);
184
- const queryString = search.toString();
197
+ const queryString = serialiseQuery(params.query);
185
198
  return `${this.baseUrl}${params.path}${queryString ? `?${queryString}` : ""}`;
186
199
  }
187
200
  buildHeaders(params) {
@@ -234,12 +247,30 @@ function pageQuery(params) {
234
247
  //#endregion
235
248
  //#region src/resources/admit.ts
236
249
  var AdmitResource = class extends APIResource {
237
- /** Gate admission check: `GET /admit?card=<cardId>`. */
250
+ /**
251
+ * Gate admission check: `GET /admit?card=<cardId>`.
252
+ *
253
+ * `targets` names the venue, zone, shop or gate the request is being made
254
+ * at, and is the context the entitlements are judged in — a benefit
255
+ * restricted to somewhere else drops out of `entitlements`. Three states,
256
+ * and they are answered differently: omit it and every target restriction
257
+ * is treated as satisfied; pass `[]` and only benefits valid anywhere
258
+ * survive; pass codes and a benefit needs one of them. Read
259
+ * `target_context_applied` on the result to tell the first apart from the
260
+ * other two.
261
+ *
262
+ * Codes are matched exactly, so send them back verbatim from
263
+ * `targetCodes.list()` — an unregistered or miscased code is refused with
264
+ * a 400 rather than quietly ignored.
265
+ */
238
266
  check(params, options) {
239
267
  return this.transport.request({
240
268
  method: "GET",
241
269
  path: "/api/v1/admit",
242
- query: { card: params.card },
270
+ query: {
271
+ card: params.card,
272
+ targets: params.targets
273
+ },
243
274
  retryable: true,
244
275
  options
245
276
  });
@@ -315,7 +346,7 @@ var BillingOptionsResource = class extends APIResource {
315
346
  //#endregion
316
347
  //#region src/resources/calendar-rules.ts
317
348
  /**
318
- * A calendar rule is one row of a calendar's date vocabulary. Conditions AND
349
+ * A calendar rule is one row of a calendar's set of dates. Conditions AND
319
350
  * within a rule and rules OR across a calendar, so "weekends in August" is a
320
351
  * single rule rather than two.
321
352
  *
@@ -353,17 +384,24 @@ var CalendarRulesResource = class extends APIResource {
353
384
  //#endregion
354
385
  //#region src/resources/calendars.ts
355
386
  /**
356
- * Calendars are named, reusable date vocabularies ("Peak 2026", "Weekends").
387
+ * Calendars are named, reusable sets of dates ("Peak 2026", "Weekends").
357
388
  * The dates themselves live in the rules attached to them.
358
389
  *
359
390
  * A calendar containing no rules is valid for **all** dates, not none.
360
391
  */
361
392
  var CalendarsResource = class extends APIResource {
393
+ /**
394
+ * Pass `view: 'full'` to include `plan_entitlement_count` on every
395
+ * calendar.
396
+ */
362
397
  async list(params, options) {
363
398
  const response = await this.transport.request({
364
399
  method: "GET",
365
400
  path: "/api/v1/calendars",
366
- query: pageQuery(params),
401
+ query: {
402
+ ...pageQuery(params),
403
+ ...params?.view ? { view: params.view } : {}
404
+ },
367
405
  retryable: true,
368
406
  options
369
407
  });
@@ -373,10 +411,14 @@ var CalendarsResource = class extends APIResource {
373
411
  total_size: response.total_size
374
412
  };
375
413
  }
376
- get(id, options) {
414
+ /**
415
+ * Pass `view: 'full'` to include `plan_entitlement_count`.
416
+ */
417
+ get(id, params = {}, options) {
377
418
  return this.transport.request({
378
419
  method: "GET",
379
420
  path: `/api/v1/calendars/${encodeURIComponent(id)}`,
421
+ query: params.view ? { view: params.view } : void 0,
380
422
  retryable: true,
381
423
  options
382
424
  });
@@ -529,6 +571,14 @@ var EntitlementsResource = class extends APIResource {
529
571
  /**
530
572
  * Fire-and-forget batch usage reporting (one row per member). Idempotent
531
573
  * per (key, member). Returns the recorded usage rows.
574
+ *
575
+ * `target` is the single registered code for the venue, zone, shop or gate
576
+ * the usage happened at — one event, one place, which is why it is
577
+ * singular here where the reads take `targets`. It is both the context the
578
+ * entitlement is judged in and a column on the row that comes back, so it
579
+ * is queryable in a way the free-form `context` is not. It is part of the
580
+ * idempotency fingerprint: a replay of the same key at a different target
581
+ * is refused rather than silently returning the original row.
532
582
  */
533
583
  async recordUsage(params, options = {}) {
534
584
  const { idempotencyKey, ...requestOptions } = options;
@@ -654,11 +704,29 @@ var MembersResource = class extends APIResource {
654
704
  options
655
705
  });
656
706
  }
707
+ /**
708
+ * The entitlements a member can use right now.
709
+ *
710
+ * `targets` filters the result down to the benefits valid at the venue,
711
+ * zone, shop or gate it names — the same codes `admit.check` takes, but
712
+ * read as a filter here rather than as the context a gate decision is made
713
+ * in. Three states: omit it and nothing is filtered by target; pass `[]`
714
+ * and only benefits valid anywhere come back; pass codes and a benefit
715
+ * needs one of them. Codes are matched exactly, so send them back verbatim
716
+ * from `targetCodes.list()` — an unregistered one is refused with a 400.
717
+ *
718
+ * Each entitlement carries `valid_on` and `valid_at` when something
719
+ * restricts it by date or by target; both list only what it matched, never
720
+ * what it failed.
721
+ */
657
722
  async listEntitlements(id, params = {}, options) {
658
723
  return (await this.transport.request({
659
724
  method: "GET",
660
725
  path: `/api/v1/members/${encodeURIComponent(id)}/entitlements`,
661
- query: { type: params.type },
726
+ query: {
727
+ type: params.type,
728
+ targets: params.targets
729
+ },
662
730
  retryable: true,
663
731
  options
664
732
  })).entitlements;
@@ -917,6 +985,45 @@ var MembershipsResource = class extends APIResource {
917
985
  }
918
986
  };
919
987
  //#endregion
988
+ //#region src/resources/plan-entitlement-availabilities.ts
989
+ /**
990
+ * An availability row says when and where one plan entitlement applies: a
991
+ * calendar (null = any date) and a set of registered target codes (empty =
992
+ * anywhere). Rows OR, and a plan entitlement with no rows at all is
993
+ * unrestricted.
994
+ *
995
+ * Rows are created and listed through their plan entitlement
996
+ * (`ophelio.planEntitlements.createAvailability` / `listAvailabilities`); this
997
+ * resource addresses an individual row.
998
+ */
999
+ var PlanEntitlementAvailabilitiesResource = class extends APIResource {
1000
+ get(id, options) {
1001
+ return this.transport.request({
1002
+ method: "GET",
1003
+ path: `/api/v1/plan-entitlement-availabilities/${encodeURIComponent(id)}`,
1004
+ retryable: true,
1005
+ options
1006
+ });
1007
+ }
1008
+ update(id, params, options) {
1009
+ return this.transport.request({
1010
+ method: "PATCH",
1011
+ path: `/api/v1/plan-entitlement-availabilities/${encodeURIComponent(id)}`,
1012
+ body: params,
1013
+ retryable: false,
1014
+ options
1015
+ });
1016
+ }
1017
+ async delete(id, options) {
1018
+ await this.transport.request({
1019
+ method: "DELETE",
1020
+ path: `/api/v1/plan-entitlement-availabilities/${encodeURIComponent(id)}`,
1021
+ retryable: false,
1022
+ options
1023
+ });
1024
+ }
1025
+ };
1026
+ //#endregion
920
1027
  //#region src/resources/plan-entitlements.ts
921
1028
  var PlanEntitlementsResource = class extends APIResource {
922
1029
  get(id, options) {
@@ -945,6 +1052,29 @@ var PlanEntitlementsResource = class extends APIResource {
945
1052
  options
946
1053
  });
947
1054
  }
1055
+ async listAvailabilities(id, params, options) {
1056
+ const response = await this.transport.request({
1057
+ method: "GET",
1058
+ path: `/api/v1/plan-entitlements/${encodeURIComponent(id)}/availabilities`,
1059
+ query: pageQuery(params),
1060
+ retryable: true,
1061
+ options
1062
+ });
1063
+ return {
1064
+ items: response.availabilities,
1065
+ next_page_token: response.next_page_token,
1066
+ total_size: response.total_size
1067
+ };
1068
+ }
1069
+ createAvailability(id, params, options) {
1070
+ return this.transport.request({
1071
+ method: "POST",
1072
+ path: `/api/v1/plan-entitlements/${encodeURIComponent(id)}/availabilities`,
1073
+ body: params,
1074
+ retryable: false,
1075
+ options
1076
+ });
1077
+ }
948
1078
  };
949
1079
  //#endregion
950
1080
  //#region src/resources/plan-links.ts
@@ -1181,16 +1311,23 @@ var SalesChannelsResource = class extends APIResource {
1181
1311
  //#endregion
1182
1312
  //#region src/resources/target-codes.ts
1183
1313
  /**
1184
- * The registered vocabulary of locations a gate can report (`venue_a`,
1185
- * `coffee_shop`). Codes are matched exactly — list them and send values back
1186
- * verbatim rather than constructing or case-folding them.
1314
+ * The registered code for each venue, zone, shop or gate a request can name
1315
+ * (`venue_a`, `coffee_shop`). Codes are matched exactly — list them and send
1316
+ * values back verbatim rather than constructing or case-folding them.
1187
1317
  */
1188
1318
  var TargetCodesResource = class extends APIResource {
1319
+ /**
1320
+ * Pass `view: 'full'` to include `plan_entitlement_count` on every target
1321
+ * code.
1322
+ */
1189
1323
  async list(params, options) {
1190
1324
  const response = await this.transport.request({
1191
1325
  method: "GET",
1192
1326
  path: "/api/v1/target-codes",
1193
- query: pageQuery(params),
1327
+ query: {
1328
+ ...pageQuery(params),
1329
+ ...params?.view ? { view: params.view } : {}
1330
+ },
1194
1331
  retryable: true,
1195
1332
  options
1196
1333
  });
@@ -1200,10 +1337,14 @@ var TargetCodesResource = class extends APIResource {
1200
1337
  total_size: response.total_size
1201
1338
  };
1202
1339
  }
1203
- get(id, options) {
1340
+ /**
1341
+ * Pass `view: 'full'` to include `plan_entitlement_count`.
1342
+ */
1343
+ get(id, params = {}, options) {
1204
1344
  return this.transport.request({
1205
1345
  method: "GET",
1206
1346
  path: `/api/v1/target-codes/${encodeURIComponent(id)}`,
1347
+ query: params.view ? { view: params.view } : void 0,
1207
1348
  retryable: true,
1208
1349
  options
1209
1350
  });
@@ -1375,6 +1516,7 @@ var Ophelio = class Ophelio {
1375
1516
  calendars;
1376
1517
  calendarRules;
1377
1518
  planEntitlements;
1519
+ planEntitlementAvailabilities;
1378
1520
  planLinks;
1379
1521
  planPricingGroupMappings;
1380
1522
  billingOptionSalesChannels;
@@ -1401,6 +1543,7 @@ var Ophelio = class Ophelio {
1401
1543
  this.calendars = new CalendarsResource(transport);
1402
1544
  this.calendarRules = new CalendarRulesResource(transport);
1403
1545
  this.planEntitlements = new PlanEntitlementsResource(transport);
1546
+ this.planEntitlementAvailabilities = new PlanEntitlementAvailabilitiesResource(transport);
1404
1547
  this.planLinks = new PlanLinksResource(transport);
1405
1548
  this.planPricingGroupMappings = new PlanPricingGroupMappingsResource(transport);
1406
1549
  this.billingOptionSalesChannels = new BillingOptionSalesChannelsResource(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ophelio/sdk",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Official JavaScript / TypeScript SDK for the Ophel.io membership & entitlement API.",
5
5
  "license": "MIT",
6
6
  "author": "Ophel.io",