@ingram-tech/luma 0.1.0 → 0.3.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.js CHANGED
@@ -40,6 +40,7 @@ var LumaApiError = class extends Error {
40
40
  // src/client.ts
41
41
  var serializeQueryValue = (value) => value instanceof Date ? value.toISOString() : String(value);
42
42
  var toIso = (value) => value instanceof Date ? value.toISOString() : value;
43
+ var toIsoNullable = (value) => value == null ? value : toIso(value);
43
44
  var collect = async (source) => {
44
45
  const out = [];
45
46
  for await (const item of source) {
@@ -78,7 +79,7 @@ var LumaClient = class _LumaClient {
78
79
  }
79
80
  /**
80
81
  * Low-level request against any Luma endpoint. `path` is taken relative to
81
- * the base URL, e.g. `/v1/event/get`.
82
+ * the base URL, e.g. `/v1/events/get`.
82
83
  */
83
84
  async request(path, init = {}) {
84
85
  const url = new URL(path.startsWith("/") ? path : `/${path}`, this.baseUrl);
@@ -162,33 +163,33 @@ var LumaClient = class _LumaClient {
162
163
  }
163
164
  }
164
165
  // ─── calendar ────────────────────────────────────────────────────────
166
+ //
167
+ // The calendar is inferred from the API key, so these take no calendar id.
165
168
  calendar = {
166
- /** Iterate every event on a calendar. */
167
- listEvents: (options) => this.paginate("/v1/calendar/list-events", {
168
- calendar_api_id: options.calendarApiId,
169
+ /** Iterate every event the calendar manages. */
170
+ listEvents: (options = {}) => this.paginate("/v1/calendars/events/list", {
169
171
  after: options.after,
170
172
  before: options.before,
173
+ status: options.status,
174
+ access: options.access,
175
+ platforms: options.platforms,
176
+ sort_direction: options.sortDirection,
171
177
  pagination_limit: options.paginationLimit,
172
178
  pagination_cursor: options.paginationCursor
173
179
  }),
174
- /** Collect every event on a calendar into an array. */
175
- listAllEvents: (options) => collect(this.calendar.listEvents(options)),
176
- /** Iterate every person who has interacted with a calendar. */
177
- listPeople: (options) => this.paginate("/v1/calendar/list-people", {
178
- calendar_api_id: options.calendarApiId,
180
+ /** Collect every event the calendar manages into an array. */
181
+ listAllEvents: (options = {}) => collect(this.calendar.listEvents(options)),
182
+ /** Iterate every contact on the calendar. */
183
+ listContacts: (options = {}) => this.paginate("/v1/calendars/contacts/list", {
184
+ query: options.query,
185
+ sort_direction: options.sortDirection,
179
186
  pagination_limit: options.paginationLimit,
180
187
  pagination_cursor: options.paginationCursor
181
188
  }),
182
- /** Collect every person on a calendar into an array. */
183
- listAllPeople: (options) => collect(this.calendar.listPeople(options)),
189
+ /** Collect every contact on the calendar into an array. */
190
+ listAllContacts: (options = {}) => collect(this.calendar.listContacts(options)),
184
191
  /** Iterate every coupon on the calendar tied to the API key. */
185
- listCoupons: async function* () {
186
- for await (const entry of this.paginate(
187
- "/v1/calendar/coupons"
188
- )) {
189
- yield normalizeCoupon(entry);
190
- }
191
- }.bind(this),
192
+ listCoupons: () => this.paginate("/v1/calendars/coupons/list"),
192
193
  /** Find a calendar coupon by its code, or `null` if none matches. */
193
194
  findCouponByCode: async (code) => {
194
195
  const target = code.toLowerCase();
@@ -199,93 +200,139 @@ var LumaClient = class _LumaClient {
199
200
  }
200
201
  return null;
201
202
  },
202
- /** Create a calendar coupon. */
203
- createCoupon: async (input) => {
204
- const discount = input.discount.type === "percent" ? {
205
- discount_type: "percent",
206
- percent_off: input.discount.percentOff
207
- } : {
208
- discount_type: "amount",
209
- cents_off: input.discount.centsOff,
210
- currency: input.discount.currency.toLowerCase()
211
- };
212
- const response = await this.request(
213
- "/v1/calendar/coupons/create",
214
- {
215
- method: "POST",
216
- body: {
217
- code: input.code,
218
- remaining_count: input.remainingCount,
219
- valid_start_at: input.validStartAt ? toIso(input.validStartAt) : void 0,
220
- valid_end_at: input.validEndAt ? toIso(input.validEndAt) : void 0,
221
- discount
222
- }
203
+ /** Create a calendar coupon (applies to any event the calendar manages). */
204
+ createCoupon: (input) => this.request("/v1/calendars/coupons/create", {
205
+ method: "POST",
206
+ body: {
207
+ code: input.code,
208
+ remaining_count: input.remainingCount,
209
+ valid_start_at: toIsoNullable(input.validStartAt),
210
+ valid_end_at: toIsoNullable(input.validEndAt),
211
+ discount: input.discount.type === "percent" ? {
212
+ discount_type: "percent",
213
+ percent_off: input.discount.percentOff
214
+ } : {
215
+ discount_type: "amount",
216
+ cents_off: input.discount.centsOff,
217
+ currency: input.discount.currency.toLowerCase()
223
218
  }
224
- );
225
- return normalizeCoupon(response.coupon ?? {}, input.code);
226
- }
219
+ }
220
+ })
227
221
  };
228
222
  // ─── events ──────────────────────────────────────────────────────────
229
223
  events = {
230
- /** Fetch a single event by its `api_id`. */
231
- get: async (eventApiId) => {
232
- const response = await this.request(
233
- "/v1/event/get",
234
- { query: { api_id: eventApiId } }
235
- );
236
- return response.event ?? response;
237
- },
224
+ /** Fetch a single event by its id (`evt-…`). */
225
+ get: (eventId) => this.request("/v1/events/get", {
226
+ query: { event_id: eventId }
227
+ }),
238
228
  /** Iterate every guest of an event. */
239
- listGuests: (options) => async function* () {
240
- for await (const entry of this.paginate(
241
- "/v1/event/get-guests",
242
- {
243
- event_api_id: options.eventApiId,
244
- approval_status: options.approvalStatus,
245
- pagination_limit: options.paginationLimit,
246
- pagination_cursor: options.paginationCursor
247
- }
248
- )) {
249
- const guest = entry.guest ?? entry;
250
- yield guest;
251
- }
252
- }.call(this),
229
+ listGuests: (options) => this.paginate("/v1/events/guests/list", {
230
+ event_id: options.eventId,
231
+ approval_status: options.approvalStatus,
232
+ sort_direction: options.sortDirection,
233
+ pagination_limit: options.paginationLimit,
234
+ pagination_cursor: options.paginationCursor
235
+ }),
253
236
  /** Collect every guest of an event into an array. */
254
237
  listAllGuests: (options) => collect(this.events.listGuests(options)),
255
- /** Fetch a single guest, by guest `api_id` or by registration email. */
256
- getGuest: async (options) => {
257
- if (!options.guestApiId && !options.email) {
258
- throw new Error("getGuest requires either `guestApiId` or `email`");
259
- }
238
+ /** Fetch a single guest (with order detail) by its guest id. */
239
+ getGuest: (options) => this.request("/v1/events/guests/get", {
240
+ query: { event_id: options.eventId, id: options.guestId }
241
+ }),
242
+ /** Update a guest's approval status (approve, decline, waitlist…). */
243
+ updateGuestStatus: async (options) => {
244
+ await this.request("/v1/events/guests/update-status", {
245
+ method: "POST",
246
+ body: {
247
+ event_id: options.eventId,
248
+ guest_id: options.guestId,
249
+ status: options.status,
250
+ should_refund: options.shouldRefund,
251
+ send_email: options.sendEmail,
252
+ message: options.message
253
+ }
254
+ });
255
+ },
256
+ /**
257
+ * Add guests to an event (host-side). Registers people directly — this
258
+ * does NOT take payment; Luma owns checkout/payment on its hosted flow.
259
+ * By default guests are added as approved ("Going") and emailed. Pass a
260
+ * `ticketTypeId` to assign each guest a ticket of that type.
261
+ */
262
+ addGuests: async (options) => {
263
+ await this.request("/v1/events/guests/add", {
264
+ method: "POST",
265
+ body: {
266
+ event_id: options.eventId,
267
+ guests: options.guests.map((guest) => ({
268
+ email: guest.email,
269
+ name: guest.name,
270
+ registration_answers: guest.registrationAnswers
271
+ })),
272
+ ticket: options.ticketTypeId ? { event_ticket_type_id: options.ticketTypeId } : void 0,
273
+ approval_status: options.approvalStatus,
274
+ send_email: options.sendEmail
275
+ }
276
+ });
277
+ },
278
+ /**
279
+ * List an event's ticket types (tiers), including prices. Pass
280
+ * `includeHidden` to include ticket types not shown on the public page.
281
+ */
282
+ listTicketTypes: async (options) => {
260
283
  const response = await this.request(
261
- "/v1/event/get-guest",
284
+ "/v1/events/ticket-types/list",
262
285
  {
263
286
  query: {
264
- event_api_id: options.eventApiId,
265
- api_id: options.guestApiId,
266
- email: options.email
287
+ event_id: options.eventId,
288
+ include_hidden: options.includeHidden
267
289
  }
268
290
  }
269
291
  );
270
- return response.guest ?? response;
292
+ return response.entries ?? [];
271
293
  },
272
- /** Update a guest's approval status (approve, decline, waitlist…). */
273
- updateGuestStatus: async (options) => {
274
- await this.request("/v1/event/update-guest-status", {
294
+ /** Fetch a single ticket type by its id. */
295
+ getTicketType: (ticketTypeId) => this.request("/v1/events/ticket-types/get", {
296
+ query: { event_ticket_type_id: ticketTypeId }
297
+ }),
298
+ /** Create a ticket type on an event. */
299
+ createTicketType: (input) => this.request("/v1/events/ticket-types/create", {
300
+ method: "POST",
301
+ body: ticketTypeBody(
302
+ { event_id: input.eventId, type: input.type },
303
+ input
304
+ )
305
+ }),
306
+ /** Update an existing ticket type. */
307
+ updateTicketType: (input) => this.request("/v1/events/ticket-types/update", {
308
+ method: "POST",
309
+ body: ticketTypeBody(
310
+ { event_ticket_type_id: input.ticketTypeId, type: input.type },
311
+ input
312
+ )
313
+ }),
314
+ /** Delete a ticket type by its id. */
315
+ deleteTicketType: async (ticketTypeId) => {
316
+ await this.request("/v1/events/ticket-types/delete", {
275
317
  method: "POST",
276
- body: {
277
- event_api_id: options.eventApiId,
278
- guest_api_id: options.guestApiId,
279
- status: options.status
280
- }
318
+ body: { event_ticket_type_id: ticketTypeId }
281
319
  });
282
320
  }
283
321
  };
284
322
  };
285
- var normalizeCoupon = (entry, fallbackCode) => ({
286
- ...entry,
287
- api_id: entry.api_id ?? entry.id ?? "",
288
- code: entry.code ?? fallbackCode ?? ""
323
+ var ticketTypeBody = (base, fields) => ({
324
+ ...base,
325
+ name: fields.name,
326
+ cents: fields.cents,
327
+ currency: fields.currency == null ? fields.currency : fields.currency.toLowerCase(),
328
+ require_approval: fields.requireApproval,
329
+ is_hidden: fields.isHidden,
330
+ description: fields.description,
331
+ is_flexible: fields.isFlexible,
332
+ min_cents: fields.minCents,
333
+ max_capacity: fields.maxCapacity,
334
+ valid_start_at: toIsoNullable(fields.validStartAt),
335
+ valid_end_at: toIsoNullable(fields.validEndAt)
289
336
  });
290
337
  export {
291
338
  LUMA_API_BASE_URL,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/client.ts"],"sourcesContent":["/** Base URL for the Luma public API. */\nexport const LUMA_API_BASE_URL = \"https://public-api.luma.com\";\n\n/** Header Luma expects the API key in. */\nexport const LUMA_API_KEY_HEADER = \"x-luma-api-key\";\n\n/** API version path segment prepended to every endpoint. */\nexport const LUMA_API_VERSION = \"v1\";\n","/**\n * Thrown when the Luma API responds with a non-2xx status, or when a response\n * body cannot be parsed as JSON.\n */\nexport class LumaApiError extends Error {\n\t/** HTTP status code returned by Luma (0 if the request never completed). */\n\treadonly status: number;\n\t/** Raw, untruncated response body. */\n\treadonly body: string;\n\t/** API path that produced the error, e.g. `/v1/event/get-guests`. */\n\treadonly path: string;\n\n\tconstructor(params: {\n\t\tmessage: string;\n\t\tstatus: number;\n\t\tbody: string;\n\t\tpath: string;\n\t}) {\n\t\tsuper(params.message);\n\t\tthis.name = \"LumaApiError\";\n\t\tthis.status = params.status;\n\t\tthis.body = params.body;\n\t\tthis.path = params.path;\n\t}\n\n\t/** True for 401/403 — the API key is missing, invalid, or lacks scope. */\n\tget isAuthError(): boolean {\n\t\treturn this.status === 401 || this.status === 403;\n\t}\n\n\t/** True for 429 — the caller is being rate limited. */\n\tget isRateLimited(): boolean {\n\t\treturn this.status === 429;\n\t}\n\n\t/**\n\t * True when Luma rejected a coupon create because the code already exists.\n\t * Luma surfaces this as a 400/409 whose body mentions the code already\n\t * existing; matched permissively so callers can treat it as idempotent.\n\t */\n\tget isDuplicateCouponCode(): boolean {\n\t\tconst lower = this.body.toLowerCase();\n\t\treturn (\n\t\t\t(this.status === 400 || this.status === 409) &&\n\t\t\tlower.includes(\"code\") &&\n\t\t\t(lower.includes(\"exist\") || lower.includes(\"already\"))\n\t\t);\n\t}\n}\n","import { LUMA_API_BASE_URL, LUMA_API_KEY_HEADER } from \"./constants\";\nimport { LumaApiError } from \"./errors\";\nimport type {\n\tCreateCalendarCouponInput,\n\tGetEventGuestOptions,\n\tListCalendarEventsOptions,\n\tListCalendarPeopleOptions,\n\tListEventGuestsOptions,\n\tLumaCalendarEntry,\n\tLumaCoupon,\n\tLumaCouponEntry,\n\tLumaEvent,\n\tLumaGuest,\n\tLumaGuestEntry,\n\tLumaPaginatedResponse,\n\tLumaPerson,\n\tUpdateGuestStatusOptions,\n} from \"./types\";\n\nexport interface LumaClientOptions {\n\t/** Luma API key. Found under Calendar Settings → API on a Luma Plus plan. */\n\tapiKey: string;\n\t/** Override the API base URL. Defaults to {@link LUMA_API_BASE_URL}. */\n\tbaseUrl?: string;\n\t/**\n\t * Fetch implementation to use. Defaults to the global `fetch`. Pass a\n\t * custom one to inject Next.js cache hints, retries, or a test double.\n\t */\n\tfetch?: typeof fetch;\n}\n\n/** Per-request options for {@link LumaClient.request}. */\nexport interface LumaRequestInit {\n\tmethod?: string;\n\t/** Query parameters. `undefined`/`null` values are dropped; `Date`s are\n\t * serialised as ISO strings. */\n\tquery?: Record<string, unknown>;\n\t/** JSON request body. Sets `content-type: application/json`. */\n\tbody?: unknown;\n\tsignal?: AbortSignal;\n\t/** Extra `fetch` init merged last — e.g. Next.js `{ next: { revalidate } }`. */\n\tfetchInit?: RequestInit;\n}\n\nconst serializeQueryValue = (value: unknown): string =>\n\tvalue instanceof Date ? value.toISOString() : String(value);\n\nconst toIso = (value: Date | string): string =>\n\tvalue instanceof Date ? value.toISOString() : value;\n\n/** Drain an async generator into an array. */\nexport const collect = async <T>(source: AsyncIterable<T>): Promise<T[]> => {\n\tconst out: T[] = [];\n\tfor await (const item of source) {\n\t\tout.push(item);\n\t}\n\treturn out;\n};\n\n/**\n * Typed client for the Luma (lu.ma) public API.\n *\n * The typed resource methods cover the endpoints this client is built around;\n * for anything not modelled here, {@link LumaClient.request} and\n * {@link LumaClient.paginate} are public escape hatches that work against any\n * endpoint.\n */\nexport class LumaClient {\n\treadonly baseUrl: string;\n\tprivate readonly apiKey: string;\n\tprivate readonly fetchImpl: typeof fetch;\n\n\tconstructor(options: LumaClientOptions) {\n\t\tif (!options.apiKey) {\n\t\t\tthrow new Error(\"LumaClient requires an `apiKey`\");\n\t\t}\n\t\tthis.apiKey = options.apiKey;\n\t\tthis.baseUrl = options.baseUrl ?? LUMA_API_BASE_URL;\n\t\tconst fetchImpl = options.fetch ?? globalThis.fetch;\n\t\tif (typeof fetchImpl !== \"function\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"No global `fetch` available; pass one via LumaClientOptions.fetch\",\n\t\t\t);\n\t\t}\n\t\tthis.fetchImpl = fetchImpl;\n\t}\n\n\t/**\n\t * Construct a client from environment variables. Reads `LUMA_API_KEY` and,\n\t * optionally, `LUMA_API_BASE_URL`.\n\t */\n\tstatic fromEnv(\n\t\tenv: Record<string, string | undefined> = typeof process !== \"undefined\"\n\t\t\t? process.env\n\t\t\t: {},\n\t): LumaClient {\n\t\tconst apiKey = env.LUMA_API_KEY;\n\t\tif (!apiKey) {\n\t\t\tthrow new Error(\"LUMA_API_KEY is not set\");\n\t\t}\n\t\treturn new LumaClient({ apiKey, baseUrl: env.LUMA_API_BASE_URL });\n\t}\n\n\t/**\n\t * Low-level request against any Luma endpoint. `path` is taken relative to\n\t * the base URL, e.g. `/v1/event/get`.\n\t */\n\tasync request<T>(path: string, init: LumaRequestInit = {}): Promise<T> {\n\t\tconst url = new URL(path.startsWith(\"/\") ? path : `/${path}`, this.baseUrl);\n\t\tif (init.query) {\n\t\t\tfor (const [key, value] of Object.entries(init.query)) {\n\t\t\t\tif (value !== undefined && value !== null) {\n\t\t\t\t\turl.searchParams.set(key, serializeQueryValue(value));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst headers: Record<string, string> = {\n\t\t\t[LUMA_API_KEY_HEADER]: this.apiKey,\n\t\t\taccept: \"application/json\",\n\t\t};\n\t\tlet body: string | undefined;\n\t\tif (init.body !== undefined) {\n\t\t\theaders[\"content-type\"] = \"application/json\";\n\t\t\tbody = JSON.stringify(init.body);\n\t\t}\n\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await this.fetchImpl(url, {\n\t\t\t\tmethod: init.method ?? \"GET\",\n\t\t\t\theaders,\n\t\t\t\tbody,\n\t\t\t\tsignal: init.signal,\n\t\t\t\t...init.fetchInit,\n\t\t\t});\n\t\t} catch (cause) {\n\t\t\tthrow new LumaApiError({\n\t\t\t\tmessage: `Luma API request to ${path} failed: ${\n\t\t\t\t\tcause instanceof Error ? cause.message : String(cause)\n\t\t\t\t}`,\n\t\t\t\tstatus: 0,\n\t\t\t\tbody: \"\",\n\t\t\t\tpath,\n\t\t\t});\n\t\t}\n\n\t\tconst text = await response.text();\n\t\tif (!response.ok) {\n\t\t\tthrow new LumaApiError({\n\t\t\t\tmessage: `Luma API responded ${response.status} for ${path}`,\n\t\t\t\tstatus: response.status,\n\t\t\t\tbody: text,\n\t\t\t\tpath,\n\t\t\t});\n\t\t}\n\t\tif (text.length === 0) {\n\t\t\treturn undefined as T;\n\t\t}\n\t\ttry {\n\t\t\treturn JSON.parse(text) as T;\n\t\t} catch {\n\t\t\tthrow new LumaApiError({\n\t\t\t\tmessage: `Luma API returned a non-JSON body for ${path}`,\n\t\t\t\tstatus: response.status,\n\t\t\t\tbody: text,\n\t\t\t\tpath,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Iterate a cursor-paginated endpoint, yielding each entry. Follows\n\t * `next_cursor` while `has_more` is true.\n\t */\n\tasync *paginate<T>(\n\t\tpath: string,\n\t\tquery: Record<string, unknown> = {},\n\t): AsyncGenerator<T> {\n\t\tlet cursor: string | null | undefined;\n\t\t// Hard stop so a misbehaving endpoint cannot loop forever.\n\t\tfor (let page = 0; page < 10_000; page += 1) {\n\t\t\tconst pageQuery = { ...query };\n\t\t\tif (cursor) {\n\t\t\t\tpageQuery.pagination_cursor = cursor;\n\t\t\t}\n\t\t\tconst result = await this.request<LumaPaginatedResponse<T>>(path, {\n\t\t\t\tquery: pageQuery,\n\t\t\t});\n\t\t\tfor (const entry of result.entries ?? []) {\n\t\t\t\tyield entry;\n\t\t\t}\n\t\t\tif (!result.has_more || !result.next_cursor) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcursor = result.next_cursor;\n\t\t}\n\t}\n\n\t// ─── calendar ────────────────────────────────────────────────────────\n\n\treadonly calendar = {\n\t\t/** Iterate every event on a calendar. */\n\t\tlistEvents: (options: ListCalendarEventsOptions) =>\n\t\t\tthis.paginate<LumaCalendarEntry>(\"/v1/calendar/list-events\", {\n\t\t\t\tcalendar_api_id: options.calendarApiId,\n\t\t\t\tafter: options.after,\n\t\t\t\tbefore: options.before,\n\t\t\t\tpagination_limit: options.paginationLimit,\n\t\t\t\tpagination_cursor: options.paginationCursor,\n\t\t\t}),\n\n\t\t/** Collect every event on a calendar into an array. */\n\t\tlistAllEvents: (options: ListCalendarEventsOptions) =>\n\t\t\tcollect(this.calendar.listEvents(options)),\n\n\t\t/** Iterate every person who has interacted with a calendar. */\n\t\tlistPeople: (options: ListCalendarPeopleOptions) =>\n\t\t\tthis.paginate<LumaPerson>(\"/v1/calendar/list-people\", {\n\t\t\t\tcalendar_api_id: options.calendarApiId,\n\t\t\t\tpagination_limit: options.paginationLimit,\n\t\t\t\tpagination_cursor: options.paginationCursor,\n\t\t\t}),\n\n\t\t/** Collect every person on a calendar into an array. */\n\t\tlistAllPeople: (options: ListCalendarPeopleOptions) =>\n\t\t\tcollect(this.calendar.listPeople(options)),\n\n\t\t/** Iterate every coupon on the calendar tied to the API key. */\n\t\tlistCoupons: async function* (this: LumaClient): AsyncGenerator<LumaCoupon> {\n\t\t\tfor await (const entry of this.paginate<LumaCouponEntry>(\n\t\t\t\t\"/v1/calendar/coupons\",\n\t\t\t)) {\n\t\t\t\tyield normalizeCoupon(entry);\n\t\t\t}\n\t\t}.bind(this),\n\n\t\t/** Find a calendar coupon by its code, or `null` if none matches. */\n\t\tfindCouponByCode: async (code: string): Promise<LumaCoupon | null> => {\n\t\t\tconst target = code.toLowerCase();\n\t\t\tfor await (const coupon of this.calendar.listCoupons()) {\n\t\t\t\tif (coupon.code.toLowerCase() === target) {\n\t\t\t\t\treturn coupon;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\n\t\t/** Create a calendar coupon. */\n\t\tcreateCoupon: async (input: CreateCalendarCouponInput): Promise<LumaCoupon> => {\n\t\t\tconst discount =\n\t\t\t\tinput.discount.type === \"percent\"\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tdiscount_type: \"percent\" as const,\n\t\t\t\t\t\t\tpercent_off: input.discount.percentOff,\n\t\t\t\t\t\t}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tdiscount_type: \"amount\" as const,\n\t\t\t\t\t\t\tcents_off: input.discount.centsOff,\n\t\t\t\t\t\t\tcurrency: input.discount.currency.toLowerCase(),\n\t\t\t\t\t\t};\n\t\t\tconst response = await this.request<{ coupon?: LumaCouponEntry }>(\n\t\t\t\t\"/v1/calendar/coupons/create\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: {\n\t\t\t\t\t\tcode: input.code,\n\t\t\t\t\t\tremaining_count: input.remainingCount,\n\t\t\t\t\t\tvalid_start_at: input.validStartAt\n\t\t\t\t\t\t\t? toIso(input.validStartAt)\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\tvalid_end_at: input.validEndAt\n\t\t\t\t\t\t\t? toIso(input.validEndAt)\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\tdiscount,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t\treturn normalizeCoupon(response.coupon ?? {}, input.code);\n\t\t},\n\t};\n\n\t// ─── events ──────────────────────────────────────────────────────────\n\n\treadonly events = {\n\t\t/** Fetch a single event by its `api_id`. */\n\t\tget: async (eventApiId: string): Promise<LumaEvent> => {\n\t\t\tconst response = await this.request<{ event?: LumaEvent } & LumaEvent>(\n\t\t\t\t\"/v1/event/get\",\n\t\t\t\t{ query: { api_id: eventApiId } },\n\t\t\t);\n\t\t\treturn response.event ?? response;\n\t\t},\n\n\t\t/** Iterate every guest of an event. */\n\t\tlistGuests: (options: ListEventGuestsOptions) =>\n\t\t\tasync function* (this: LumaClient): AsyncGenerator<LumaGuest> {\n\t\t\t\tfor await (const entry of this.paginate<LumaGuestEntry>(\n\t\t\t\t\t\"/v1/event/get-guests\",\n\t\t\t\t\t{\n\t\t\t\t\t\tevent_api_id: options.eventApiId,\n\t\t\t\t\t\tapproval_status: options.approvalStatus,\n\t\t\t\t\t\tpagination_limit: options.paginationLimit,\n\t\t\t\t\t\tpagination_cursor: options.paginationCursor,\n\t\t\t\t\t},\n\t\t\t\t)) {\n\t\t\t\t\tconst guest = entry.guest ?? (entry as unknown as LumaGuest);\n\t\t\t\t\tyield guest;\n\t\t\t\t}\n\t\t\t}.call(this),\n\n\t\t/** Collect every guest of an event into an array. */\n\t\tlistAllGuests: (options: ListEventGuestsOptions) =>\n\t\t\tcollect(this.events.listGuests(options)),\n\n\t\t/** Fetch a single guest, by guest `api_id` or by registration email. */\n\t\tgetGuest: async (options: GetEventGuestOptions): Promise<LumaGuest> => {\n\t\t\tif (!options.guestApiId && !options.email) {\n\t\t\t\tthrow new Error(\"getGuest requires either `guestApiId` or `email`\");\n\t\t\t}\n\t\t\tconst response = await this.request<{ guest?: LumaGuest } & LumaGuest>(\n\t\t\t\t\"/v1/event/get-guest\",\n\t\t\t\t{\n\t\t\t\t\tquery: {\n\t\t\t\t\t\tevent_api_id: options.eventApiId,\n\t\t\t\t\t\tapi_id: options.guestApiId,\n\t\t\t\t\t\temail: options.email,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t\treturn response.guest ?? response;\n\t\t},\n\n\t\t/** Update a guest's approval status (approve, decline, waitlist…). */\n\t\tupdateGuestStatus: async (options: UpdateGuestStatusOptions): Promise<void> => {\n\t\t\tawait this.request(\"/v1/event/update-guest-status\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tbody: {\n\t\t\t\t\tevent_api_id: options.eventApiId,\n\t\t\t\t\tguest_api_id: options.guestApiId,\n\t\t\t\t\tstatus: options.status,\n\t\t\t\t},\n\t\t\t});\n\t\t},\n\t};\n}\n\nconst normalizeCoupon = (\n\tentry: LumaCouponEntry,\n\tfallbackCode?: string,\n): LumaCoupon => ({\n\t...entry,\n\tapi_id: entry.api_id ?? entry.id ?? \"\",\n\tcode: entry.code ?? fallbackCode ?? \"\",\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/client.ts"],"sourcesContent":["/** Base URL for the Luma public API. */\nexport const LUMA_API_BASE_URL = \"https://public-api.luma.com\";\n\n/** Header Luma expects the API key in. */\nexport const LUMA_API_KEY_HEADER = \"x-luma-api-key\";\n\n/** API version path segment prepended to every endpoint. */\nexport const LUMA_API_VERSION = \"v1\";\n","/**\n * Thrown when the Luma API responds with a non-2xx status, or when a response\n * body cannot be parsed as JSON.\n */\nexport class LumaApiError extends Error {\n\t/** HTTP status code returned by Luma (0 if the request never completed). */\n\treadonly status: number;\n\t/** Raw, untruncated response body. */\n\treadonly body: string;\n\t/** API path that produced the error, e.g. `/v1/event/get-guests`. */\n\treadonly path: string;\n\n\tconstructor(params: {\n\t\tmessage: string;\n\t\tstatus: number;\n\t\tbody: string;\n\t\tpath: string;\n\t}) {\n\t\tsuper(params.message);\n\t\tthis.name = \"LumaApiError\";\n\t\tthis.status = params.status;\n\t\tthis.body = params.body;\n\t\tthis.path = params.path;\n\t}\n\n\t/** True for 401/403 — the API key is missing, invalid, or lacks scope. */\n\tget isAuthError(): boolean {\n\t\treturn this.status === 401 || this.status === 403;\n\t}\n\n\t/** True for 429 — the caller is being rate limited. */\n\tget isRateLimited(): boolean {\n\t\treturn this.status === 429;\n\t}\n\n\t/**\n\t * True when Luma rejected a coupon create because the code already exists.\n\t * Luma surfaces this as a 400/409 whose body mentions the code already\n\t * existing; matched permissively so callers can treat it as idempotent.\n\t */\n\tget isDuplicateCouponCode(): boolean {\n\t\tconst lower = this.body.toLowerCase();\n\t\treturn (\n\t\t\t(this.status === 400 || this.status === 409) &&\n\t\t\tlower.includes(\"code\") &&\n\t\t\t(lower.includes(\"exist\") || lower.includes(\"already\"))\n\t\t);\n\t}\n}\n","import { LUMA_API_BASE_URL, LUMA_API_KEY_HEADER } from \"./constants\";\nimport { LumaApiError } from \"./errors\";\nimport type {\n\tAddGuestsOptions,\n\tCreateCalendarCouponInput,\n\tCreateTicketTypeInput,\n\tGetEventGuestOptions,\n\tListCalendarEventsOptions,\n\tListContactsOptions,\n\tListEventGuestsOptions,\n\tListTicketTypesOptions,\n\tLumaCalendarEvent,\n\tLumaContact,\n\tLumaCoupon,\n\tLumaEvent,\n\tLumaGuest,\n\tLumaGuestDetail,\n\tLumaPaginatedResponse,\n\tLumaTicketType,\n\tUpdateGuestStatusOptions,\n\tUpdateTicketTypeInput,\n} from \"./types\";\n\nexport interface LumaClientOptions {\n\t/** Luma API key. Found under Calendar Settings → API on a Luma Plus plan. */\n\tapiKey: string;\n\t/** Override the API base URL. Defaults to {@link LUMA_API_BASE_URL}. */\n\tbaseUrl?: string;\n\t/**\n\t * Fetch implementation to use. Defaults to the global `fetch`. Pass a\n\t * custom one to inject Next.js cache hints, retries, or a test double.\n\t */\n\tfetch?: typeof fetch;\n}\n\n/** Per-request options for {@link LumaClient.request}. */\nexport interface LumaRequestInit {\n\tmethod?: string;\n\t/** Query parameters. `undefined`/`null` values are dropped; `Date`s are\n\t * serialised as ISO strings. */\n\tquery?: Record<string, unknown>;\n\t/** JSON request body. Sets `content-type: application/json`. */\n\tbody?: unknown;\n\tsignal?: AbortSignal;\n\t/** Extra `fetch` init merged last — e.g. Next.js `{ next: { revalidate } }`. */\n\tfetchInit?: RequestInit;\n}\n\nconst serializeQueryValue = (value: unknown): string =>\n\tvalue instanceof Date ? value.toISOString() : String(value);\n\nconst toIso = (value: Date | string): string =>\n\tvalue instanceof Date ? value.toISOString() : value;\n\n/** Like {@link toIso} but passes `null`/`undefined` through untouched, so a\n * caller can clear a field (`null`) or leave it unset (`undefined`). */\nconst toIsoNullable = (\n\tvalue: Date | string | null | undefined,\n): string | null | undefined => (value == null ? value : toIso(value));\n\n/** Drain an async generator into an array. */\nexport const collect = async <T>(source: AsyncIterable<T>): Promise<T[]> => {\n\tconst out: T[] = [];\n\tfor await (const item of source) {\n\t\tout.push(item);\n\t}\n\treturn out;\n};\n\n/**\n * Typed client for the Luma (lu.ma) public API.\n *\n * The typed resource methods cover the endpoints this client is built around;\n * for anything not modelled here, {@link LumaClient.request} and\n * {@link LumaClient.paginate} are public escape hatches that work against any\n * endpoint.\n */\nexport class LumaClient {\n\treadonly baseUrl: string;\n\tprivate readonly apiKey: string;\n\tprivate readonly fetchImpl: typeof fetch;\n\n\tconstructor(options: LumaClientOptions) {\n\t\tif (!options.apiKey) {\n\t\t\tthrow new Error(\"LumaClient requires an `apiKey`\");\n\t\t}\n\t\tthis.apiKey = options.apiKey;\n\t\tthis.baseUrl = options.baseUrl ?? LUMA_API_BASE_URL;\n\t\tconst fetchImpl = options.fetch ?? globalThis.fetch;\n\t\tif (typeof fetchImpl !== \"function\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"No global `fetch` available; pass one via LumaClientOptions.fetch\",\n\t\t\t);\n\t\t}\n\t\tthis.fetchImpl = fetchImpl;\n\t}\n\n\t/**\n\t * Construct a client from environment variables. Reads `LUMA_API_KEY` and,\n\t * optionally, `LUMA_API_BASE_URL`.\n\t */\n\tstatic fromEnv(\n\t\tenv: Record<string, string | undefined> = typeof process !== \"undefined\"\n\t\t\t? process.env\n\t\t\t: {},\n\t): LumaClient {\n\t\tconst apiKey = env.LUMA_API_KEY;\n\t\tif (!apiKey) {\n\t\t\tthrow new Error(\"LUMA_API_KEY is not set\");\n\t\t}\n\t\treturn new LumaClient({ apiKey, baseUrl: env.LUMA_API_BASE_URL });\n\t}\n\n\t/**\n\t * Low-level request against any Luma endpoint. `path` is taken relative to\n\t * the base URL, e.g. `/v1/events/get`.\n\t */\n\tasync request<T>(path: string, init: LumaRequestInit = {}): Promise<T> {\n\t\tconst url = new URL(path.startsWith(\"/\") ? path : `/${path}`, this.baseUrl);\n\t\tif (init.query) {\n\t\t\tfor (const [key, value] of Object.entries(init.query)) {\n\t\t\t\tif (value !== undefined && value !== null) {\n\t\t\t\t\turl.searchParams.set(key, serializeQueryValue(value));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst headers: Record<string, string> = {\n\t\t\t[LUMA_API_KEY_HEADER]: this.apiKey,\n\t\t\taccept: \"application/json\",\n\t\t};\n\t\tlet body: string | undefined;\n\t\tif (init.body !== undefined) {\n\t\t\theaders[\"content-type\"] = \"application/json\";\n\t\t\tbody = JSON.stringify(init.body);\n\t\t}\n\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await this.fetchImpl(url, {\n\t\t\t\tmethod: init.method ?? \"GET\",\n\t\t\t\theaders,\n\t\t\t\tbody,\n\t\t\t\tsignal: init.signal,\n\t\t\t\t...init.fetchInit,\n\t\t\t});\n\t\t} catch (cause) {\n\t\t\tthrow new LumaApiError({\n\t\t\t\tmessage: `Luma API request to ${path} failed: ${\n\t\t\t\t\tcause instanceof Error ? cause.message : String(cause)\n\t\t\t\t}`,\n\t\t\t\tstatus: 0,\n\t\t\t\tbody: \"\",\n\t\t\t\tpath,\n\t\t\t});\n\t\t}\n\n\t\tconst text = await response.text();\n\t\tif (!response.ok) {\n\t\t\tthrow new LumaApiError({\n\t\t\t\tmessage: `Luma API responded ${response.status} for ${path}`,\n\t\t\t\tstatus: response.status,\n\t\t\t\tbody: text,\n\t\t\t\tpath,\n\t\t\t});\n\t\t}\n\t\tif (text.length === 0) {\n\t\t\treturn undefined as T;\n\t\t}\n\t\ttry {\n\t\t\treturn JSON.parse(text) as T;\n\t\t} catch {\n\t\t\tthrow new LumaApiError({\n\t\t\t\tmessage: `Luma API returned a non-JSON body for ${path}`,\n\t\t\t\tstatus: response.status,\n\t\t\t\tbody: text,\n\t\t\t\tpath,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Iterate a cursor-paginated endpoint, yielding each entry. Follows\n\t * `next_cursor` while `has_more` is true.\n\t */\n\tasync *paginate<T>(\n\t\tpath: string,\n\t\tquery: Record<string, unknown> = {},\n\t): AsyncGenerator<T> {\n\t\tlet cursor: string | null | undefined;\n\t\t// Hard stop so a misbehaving endpoint cannot loop forever.\n\t\tfor (let page = 0; page < 10_000; page += 1) {\n\t\t\tconst pageQuery = { ...query };\n\t\t\tif (cursor) {\n\t\t\t\tpageQuery.pagination_cursor = cursor;\n\t\t\t}\n\t\t\tconst result = await this.request<LumaPaginatedResponse<T>>(path, {\n\t\t\t\tquery: pageQuery,\n\t\t\t});\n\t\t\tfor (const entry of result.entries ?? []) {\n\t\t\t\tyield entry;\n\t\t\t}\n\t\t\tif (!result.has_more || !result.next_cursor) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcursor = result.next_cursor;\n\t\t}\n\t}\n\n\t// ─── calendar ────────────────────────────────────────────────────────\n\t//\n\t// The calendar is inferred from the API key, so these take no calendar id.\n\n\treadonly calendar = {\n\t\t/** Iterate every event the calendar manages. */\n\t\tlistEvents: (options: ListCalendarEventsOptions = {}) =>\n\t\t\tthis.paginate<LumaCalendarEvent>(\"/v1/calendars/events/list\", {\n\t\t\t\tafter: options.after,\n\t\t\t\tbefore: options.before,\n\t\t\t\tstatus: options.status,\n\t\t\t\taccess: options.access,\n\t\t\t\tplatforms: options.platforms,\n\t\t\t\tsort_direction: options.sortDirection,\n\t\t\t\tpagination_limit: options.paginationLimit,\n\t\t\t\tpagination_cursor: options.paginationCursor,\n\t\t\t}),\n\n\t\t/** Collect every event the calendar manages into an array. */\n\t\tlistAllEvents: (options: ListCalendarEventsOptions = {}) =>\n\t\t\tcollect(this.calendar.listEvents(options)),\n\n\t\t/** Iterate every contact on the calendar. */\n\t\tlistContacts: (options: ListContactsOptions = {}) =>\n\t\t\tthis.paginate<LumaContact>(\"/v1/calendars/contacts/list\", {\n\t\t\t\tquery: options.query,\n\t\t\t\tsort_direction: options.sortDirection,\n\t\t\t\tpagination_limit: options.paginationLimit,\n\t\t\t\tpagination_cursor: options.paginationCursor,\n\t\t\t}),\n\n\t\t/** Collect every contact on the calendar into an array. */\n\t\tlistAllContacts: (options: ListContactsOptions = {}) =>\n\t\t\tcollect(this.calendar.listContacts(options)),\n\n\t\t/** Iterate every coupon on the calendar tied to the API key. */\n\t\tlistCoupons: (): AsyncGenerator<LumaCoupon> =>\n\t\t\tthis.paginate<LumaCoupon>(\"/v1/calendars/coupons/list\"),\n\n\t\t/** Find a calendar coupon by its code, or `null` if none matches. */\n\t\tfindCouponByCode: async (code: string): Promise<LumaCoupon | null> => {\n\t\t\tconst target = code.toLowerCase();\n\t\t\tfor await (const coupon of this.calendar.listCoupons()) {\n\t\t\t\tif (coupon.code.toLowerCase() === target) {\n\t\t\t\t\treturn coupon;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\n\t\t/** Create a calendar coupon (applies to any event the calendar manages). */\n\t\tcreateCoupon: (input: CreateCalendarCouponInput): Promise<LumaCoupon> =>\n\t\t\tthis.request<LumaCoupon>(\"/v1/calendars/coupons/create\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tbody: {\n\t\t\t\t\tcode: input.code,\n\t\t\t\t\tremaining_count: input.remainingCount,\n\t\t\t\t\tvalid_start_at: toIsoNullable(input.validStartAt),\n\t\t\t\t\tvalid_end_at: toIsoNullable(input.validEndAt),\n\t\t\t\t\tdiscount:\n\t\t\t\t\t\tinput.discount.type === \"percent\"\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tdiscount_type: \"percent\",\n\t\t\t\t\t\t\t\t\tpercent_off: input.discount.percentOff,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\tdiscount_type: \"amount\",\n\t\t\t\t\t\t\t\t\tcents_off: input.discount.centsOff,\n\t\t\t\t\t\t\t\t\tcurrency: input.discount.currency.toLowerCase(),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}),\n\t};\n\n\t// ─── events ──────────────────────────────────────────────────────────\n\n\treadonly events = {\n\t\t/** Fetch a single event by its id (`evt-…`). */\n\t\tget: (eventId: string): Promise<LumaEvent> =>\n\t\t\tthis.request<LumaEvent>(\"/v1/events/get\", {\n\t\t\t\tquery: { event_id: eventId },\n\t\t\t}),\n\n\t\t/** Iterate every guest of an event. */\n\t\tlistGuests: (options: ListEventGuestsOptions) =>\n\t\t\tthis.paginate<LumaGuest>(\"/v1/events/guests/list\", {\n\t\t\t\tevent_id: options.eventId,\n\t\t\t\tapproval_status: options.approvalStatus,\n\t\t\t\tsort_direction: options.sortDirection,\n\t\t\t\tpagination_limit: options.paginationLimit,\n\t\t\t\tpagination_cursor: options.paginationCursor,\n\t\t\t}),\n\n\t\t/** Collect every guest of an event into an array. */\n\t\tlistAllGuests: (options: ListEventGuestsOptions) =>\n\t\t\tcollect(this.events.listGuests(options)),\n\n\t\t/** Fetch a single guest (with order detail) by its guest id. */\n\t\tgetGuest: (options: GetEventGuestOptions): Promise<LumaGuestDetail> =>\n\t\t\tthis.request<LumaGuestDetail>(\"/v1/events/guests/get\", {\n\t\t\t\tquery: { event_id: options.eventId, id: options.guestId },\n\t\t\t}),\n\n\t\t/** Update a guest's approval status (approve, decline, waitlist…). */\n\t\tupdateGuestStatus: async (options: UpdateGuestStatusOptions): Promise<void> => {\n\t\t\tawait this.request(\"/v1/events/guests/update-status\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tbody: {\n\t\t\t\t\tevent_id: options.eventId,\n\t\t\t\t\tguest_id: options.guestId,\n\t\t\t\t\tstatus: options.status,\n\t\t\t\t\tshould_refund: options.shouldRefund,\n\t\t\t\t\tsend_email: options.sendEmail,\n\t\t\t\t\tmessage: options.message,\n\t\t\t\t},\n\t\t\t});\n\t\t},\n\n\t\t/**\n\t\t * Add guests to an event (host-side). Registers people directly — this\n\t\t * does NOT take payment; Luma owns checkout/payment on its hosted flow.\n\t\t * By default guests are added as approved (\"Going\") and emailed. Pass a\n\t\t * `ticketTypeId` to assign each guest a ticket of that type.\n\t\t */\n\t\taddGuests: async (options: AddGuestsOptions): Promise<void> => {\n\t\t\tawait this.request(\"/v1/events/guests/add\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tbody: {\n\t\t\t\t\tevent_id: options.eventId,\n\t\t\t\t\tguests: options.guests.map((guest) => ({\n\t\t\t\t\t\temail: guest.email,\n\t\t\t\t\t\tname: guest.name,\n\t\t\t\t\t\tregistration_answers: guest.registrationAnswers,\n\t\t\t\t\t})),\n\t\t\t\t\tticket: options.ticketTypeId\n\t\t\t\t\t\t? { event_ticket_type_id: options.ticketTypeId }\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\tapproval_status: options.approvalStatus,\n\t\t\t\t\tsend_email: options.sendEmail,\n\t\t\t\t},\n\t\t\t});\n\t\t},\n\n\t\t/**\n\t\t * List an event's ticket types (tiers), including prices. Pass\n\t\t * `includeHidden` to include ticket types not shown on the public page.\n\t\t */\n\t\tlistTicketTypes: async (\n\t\t\toptions: ListTicketTypesOptions,\n\t\t): Promise<LumaTicketType[]> => {\n\t\t\tconst response = await this.request<{ entries?: LumaTicketType[] }>(\n\t\t\t\t\"/v1/events/ticket-types/list\",\n\t\t\t\t{\n\t\t\t\t\tquery: {\n\t\t\t\t\t\tevent_id: options.eventId,\n\t\t\t\t\t\tinclude_hidden: options.includeHidden,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t\treturn response.entries ?? [];\n\t\t},\n\n\t\t/** Fetch a single ticket type by its id. */\n\t\tgetTicketType: (ticketTypeId: string): Promise<LumaTicketType> =>\n\t\t\tthis.request<LumaTicketType>(\"/v1/events/ticket-types/get\", {\n\t\t\t\tquery: { event_ticket_type_id: ticketTypeId },\n\t\t\t}),\n\n\t\t/** Create a ticket type on an event. */\n\t\tcreateTicketType: (input: CreateTicketTypeInput): Promise<LumaTicketType> =>\n\t\t\tthis.request<LumaTicketType>(\"/v1/events/ticket-types/create\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tbody: ticketTypeBody(\n\t\t\t\t\t{ event_id: input.eventId, type: input.type },\n\t\t\t\t\tinput,\n\t\t\t\t),\n\t\t\t}),\n\n\t\t/** Update an existing ticket type. */\n\t\tupdateTicketType: (input: UpdateTicketTypeInput): Promise<LumaTicketType> =>\n\t\t\tthis.request<LumaTicketType>(\"/v1/events/ticket-types/update\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tbody: ticketTypeBody(\n\t\t\t\t\t{ event_ticket_type_id: input.ticketTypeId, type: input.type },\n\t\t\t\t\tinput,\n\t\t\t\t),\n\t\t\t}),\n\n\t\t/** Delete a ticket type by its id. */\n\t\tdeleteTicketType: async (ticketTypeId: string): Promise<void> => {\n\t\t\tawait this.request(\"/v1/events/ticket-types/delete\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tbody: { event_ticket_type_id: ticketTypeId },\n\t\t\t});\n\t\t},\n\t};\n}\n\n/** Build the snake_case body shared by ticket-type create and update. Keys\n * left `undefined` are dropped by `JSON.stringify`; explicit `null` is sent. */\nconst ticketTypeBody = (\n\tbase: Record<string, unknown>,\n\tfields: {\n\t\tname?: string;\n\t\tcents?: number | null;\n\t\tcurrency?: string | null;\n\t\trequireApproval?: boolean;\n\t\tisHidden?: boolean;\n\t\tdescription?: string | null;\n\t\tisFlexible?: boolean;\n\t\tminCents?: number | null;\n\t\tmaxCapacity?: number | null;\n\t\tvalidStartAt?: Date | string | null;\n\t\tvalidEndAt?: Date | string | null;\n\t},\n): Record<string, unknown> => ({\n\t...base,\n\tname: fields.name,\n\tcents: fields.cents,\n\tcurrency: fields.currency == null ? fields.currency : fields.currency.toLowerCase(),\n\trequire_approval: fields.requireApproval,\n\tis_hidden: fields.isHidden,\n\tdescription: fields.description,\n\tis_flexible: fields.isFlexible,\n\tmin_cents: fields.minCents,\n\tmax_capacity: fields.maxCapacity,\n\tvalid_start_at: toIsoNullable(fields.validStartAt),\n\tvalid_end_at: toIsoNullable(fields.validEndAt),\n});\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingram-tech/luma",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "TypeScript client for the Luma (lu.ma) public API.",
5
5
  "license": "MIT",
6
6
  "author": "Ingram Technologies",
@@ -36,6 +36,7 @@
36
36
  "scripts": {
37
37
  "build": "tsup",
38
38
  "prepack": "bun run build",
39
+ "generate": "curl -sSf https://public-api.luma.com/openapi.json -o openapi.json && openapi-typescript openapi.json -o src/generated/openapi.ts && biome format --write src/generated/openapi.ts",
39
40
  "lint": "biome check .",
40
41
  "format": "biome format --write .",
41
42
  "type-check": "tsc --noEmit",
@@ -47,10 +48,11 @@
47
48
  "node": ">=18"
48
49
  },
49
50
  "devDependencies": {
50
- "@biomejs/biome": "^2.4.13",
51
- "@types/node": "^22.0.0",
52
- "tsup": "^8.0.0",
51
+ "@biomejs/biome": "^2.5.1",
52
+ "@types/node": "^26.0.1",
53
+ "openapi-typescript": "^7.13.0",
54
+ "tsup": "^8.5.1",
53
55
  "typescript": "^5.9.3",
54
- "vitest": "^4.0.18"
56
+ "vitest": "^4.1.9"
55
57
  }
56
58
  }