glitchgrab 1.53.0 → 1.54.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/server.d.mts CHANGED
@@ -359,6 +359,19 @@ interface BookingSettings {
359
359
  noticeMinutes: number;
360
360
  /** Code in the "Book on WhatsApp" link. Lower case, letters, digits and `-`; unique across projects. */
361
361
  whatsappCode: string | null;
362
+ /**
363
+ * Who else is WhatsApped when a demo is booked, besides the project owner —
364
+ * sales, the founders, whoever needs the lead as it lands. At most 5; a
365
+ * ten-digit Indian mobile is stored with its 91. Send the whole list to
366
+ * change it; leave the field out and it keeps what is stored.
367
+ */
368
+ notifyRecipients: BookingNotifyRecipient[];
369
+ }
370
+ /** One extra person alerted when a demo is booked. */
371
+ interface BookingNotifyRecipient {
372
+ name: string;
373
+ /** Digits only, country code included. */
374
+ phone: string;
362
375
  }
363
376
  interface BookingSettingsState {
364
377
  settings: BookingSettings;
@@ -393,6 +406,78 @@ declare function getBookingSettings(options?: CallsOptions): Promise<BookingSett
393
406
  * comes back as one.
394
407
  */
395
408
  declare function updateBookingSettings(changes: Partial<BookingSettings>, options?: CallsOptions): Promise<BookingSettingsResult>;
409
+ /** One booked demo. Times are ISO strings in UTC; `timezone` is what the prospect picked in. */
410
+ interface Booking {
411
+ id: string;
412
+ /** Who booked. */
413
+ name: string;
414
+ email: string;
415
+ /** Digits only, country code included. */
416
+ phone: string;
417
+ startsAt: string;
418
+ endsAt: string;
419
+ /** IANA zone the prospect booked in, e.g. `"Asia/Kolkata"`. */
420
+ timezone: string | null;
421
+ /** What they typed when booking, if anything. */
422
+ note: string | null;
423
+ /** `CONFIRMED` or `CANCELLED`. Unverified holds are never returned. */
424
+ status: string;
425
+ /** The Google Meet room. Null until the booking is confirmed. */
426
+ meetUrl: string | null;
427
+ createdAt: string;
428
+ }
429
+ interface ListBookingsOptions extends CallsOptions {
430
+ /** `"upcoming"` (default) or `"past"`. */
431
+ scope?: "upcoming" | "past";
432
+ /** Up to 100. Defaults to 50. */
433
+ limit?: number;
434
+ }
435
+ /**
436
+ * Whether the prospect was told. A move re-sends the approved confirmation
437
+ * template, so `"sent"` is real. A cancellation can only go as free text, which
438
+ * WhatsApp delivers only inside its 24-hour window and reports nothing about —
439
+ * `"attempted"` means exactly that, and your panel should say so rather than
440
+ * telling an admin the client knows.
441
+ */
442
+ type BookingClientNotice = "sent" | "attempted" | "none";
443
+ type BookingMutationResult = {
444
+ ok: true;
445
+ booking: Booking | null;
446
+ clientNotice: BookingClientNotice;
447
+ } | {
448
+ ok: false;
449
+ error: string;
450
+ code: string | null;
451
+ };
452
+ /**
453
+ * The project's booked demos, soonest first. Never throws — an empty list on
454
+ * any failure, so a panel renders "no demos" instead of a stack trace.
455
+ *
456
+ * ```ts
457
+ * const demos = await listBookings(); // GLITCHGRAB_CALLS_KEY=ggc_…
458
+ * ```
459
+ */
460
+ declare function listBookings(options?: ListBookingsOptions): Promise<Booking[]>;
461
+ /**
462
+ * Move a demo to a new start time. The Google event is patched, never
463
+ * recreated, so the Meet link in every message already sent stays alive, and
464
+ * the prospect is re-sent the confirmation with the new time.
465
+ *
466
+ * `{ ok: false, code: "SLOT_TAKEN" }` when another booking holds that slot,
467
+ * `"PAST"` for a time that has gone, `"CALENDAR"` when Google refused — in the
468
+ * last case nothing was changed and the demo still stands at its old time.
469
+ */
470
+ declare function rescheduleBooking(id: string, startsAt: Date | string, options?: CallsOptions): Promise<BookingMutationResult>;
471
+ /**
472
+ * Call a demo off: the Google event is deleted, the recorder is stood down, and
473
+ * the prospect is messaged if WhatsApp will carry it (see
474
+ * {@link BookingClientNotice}).
475
+ *
476
+ * `reason` is included in that message. Nothing is marked cancelled unless the
477
+ * calendar agreed first, so `{ ok: false, code: "CALENDAR" }` means the demo is
478
+ * still on.
479
+ */
480
+ declare function cancelBooking(id: string, reason?: string, options?: CallsOptions): Promise<BookingMutationResult>;
396
481
  /**
397
482
  * `JSON.stringify` that is safe inside `<script type="application/ld+json">`.
398
483
  *
@@ -413,4 +498,4 @@ declare function updateBookingSettings(changes: Partial<BookingSettings>, option
413
498
  */
414
499
  declare function escapeJsonForScript(value: unknown): string;
415
500
 
416
- export { type BookingSettings, type BookingSettingsResult, type BookingSettingsState, type BookingWorkingHours, type Call, type CallAudio, type CallListItem, type CallMoment, type CallParticipant, type CallsOptions, type CaptureServerErrorsOptions, type DeleteCallResult, type GetCallOptions, type Guide, type GuideReadOptions, type GuideSummary, type ListCallsOptions, ReportResult, ReportSeverity, ReportType, type ServerReportOptions, type ServerReporterConfig, type ServerReporterIdentity, type SignSessionOptions, captureServerErrors, configureServerReporter, deleteCall, escapeJsonForScript, getBookingSettings, getCall, getGuide, listCalls, listGuides, reportServerError, resetServerReporter, signSession, updateBookingSettings };
501
+ export { type Booking, type BookingClientNotice, type BookingMutationResult, type BookingNotifyRecipient, type BookingSettings, type BookingSettingsResult, type BookingSettingsState, type BookingWorkingHours, type Call, type CallAudio, type CallListItem, type CallMoment, type CallParticipant, type CallsOptions, type CaptureServerErrorsOptions, type DeleteCallResult, type GetCallOptions, type Guide, type GuideReadOptions, type GuideSummary, type ListBookingsOptions, type ListCallsOptions, ReportResult, ReportSeverity, ReportType, type ServerReportOptions, type ServerReporterConfig, type ServerReporterIdentity, type SignSessionOptions, cancelBooking, captureServerErrors, configureServerReporter, deleteCall, escapeJsonForScript, getBookingSettings, getCall, getGuide, listBookings, listCalls, listGuides, reportServerError, rescheduleBooking, resetServerReporter, signSession, updateBookingSettings };
package/dist/server.d.ts CHANGED
@@ -359,6 +359,19 @@ interface BookingSettings {
359
359
  noticeMinutes: number;
360
360
  /** Code in the "Book on WhatsApp" link. Lower case, letters, digits and `-`; unique across projects. */
361
361
  whatsappCode: string | null;
362
+ /**
363
+ * Who else is WhatsApped when a demo is booked, besides the project owner —
364
+ * sales, the founders, whoever needs the lead as it lands. At most 5; a
365
+ * ten-digit Indian mobile is stored with its 91. Send the whole list to
366
+ * change it; leave the field out and it keeps what is stored.
367
+ */
368
+ notifyRecipients: BookingNotifyRecipient[];
369
+ }
370
+ /** One extra person alerted when a demo is booked. */
371
+ interface BookingNotifyRecipient {
372
+ name: string;
373
+ /** Digits only, country code included. */
374
+ phone: string;
362
375
  }
363
376
  interface BookingSettingsState {
364
377
  settings: BookingSettings;
@@ -393,6 +406,78 @@ declare function getBookingSettings(options?: CallsOptions): Promise<BookingSett
393
406
  * comes back as one.
394
407
  */
395
408
  declare function updateBookingSettings(changes: Partial<BookingSettings>, options?: CallsOptions): Promise<BookingSettingsResult>;
409
+ /** One booked demo. Times are ISO strings in UTC; `timezone` is what the prospect picked in. */
410
+ interface Booking {
411
+ id: string;
412
+ /** Who booked. */
413
+ name: string;
414
+ email: string;
415
+ /** Digits only, country code included. */
416
+ phone: string;
417
+ startsAt: string;
418
+ endsAt: string;
419
+ /** IANA zone the prospect booked in, e.g. `"Asia/Kolkata"`. */
420
+ timezone: string | null;
421
+ /** What they typed when booking, if anything. */
422
+ note: string | null;
423
+ /** `CONFIRMED` or `CANCELLED`. Unverified holds are never returned. */
424
+ status: string;
425
+ /** The Google Meet room. Null until the booking is confirmed. */
426
+ meetUrl: string | null;
427
+ createdAt: string;
428
+ }
429
+ interface ListBookingsOptions extends CallsOptions {
430
+ /** `"upcoming"` (default) or `"past"`. */
431
+ scope?: "upcoming" | "past";
432
+ /** Up to 100. Defaults to 50. */
433
+ limit?: number;
434
+ }
435
+ /**
436
+ * Whether the prospect was told. A move re-sends the approved confirmation
437
+ * template, so `"sent"` is real. A cancellation can only go as free text, which
438
+ * WhatsApp delivers only inside its 24-hour window and reports nothing about —
439
+ * `"attempted"` means exactly that, and your panel should say so rather than
440
+ * telling an admin the client knows.
441
+ */
442
+ type BookingClientNotice = "sent" | "attempted" | "none";
443
+ type BookingMutationResult = {
444
+ ok: true;
445
+ booking: Booking | null;
446
+ clientNotice: BookingClientNotice;
447
+ } | {
448
+ ok: false;
449
+ error: string;
450
+ code: string | null;
451
+ };
452
+ /**
453
+ * The project's booked demos, soonest first. Never throws — an empty list on
454
+ * any failure, so a panel renders "no demos" instead of a stack trace.
455
+ *
456
+ * ```ts
457
+ * const demos = await listBookings(); // GLITCHGRAB_CALLS_KEY=ggc_…
458
+ * ```
459
+ */
460
+ declare function listBookings(options?: ListBookingsOptions): Promise<Booking[]>;
461
+ /**
462
+ * Move a demo to a new start time. The Google event is patched, never
463
+ * recreated, so the Meet link in every message already sent stays alive, and
464
+ * the prospect is re-sent the confirmation with the new time.
465
+ *
466
+ * `{ ok: false, code: "SLOT_TAKEN" }` when another booking holds that slot,
467
+ * `"PAST"` for a time that has gone, `"CALENDAR"` when Google refused — in the
468
+ * last case nothing was changed and the demo still stands at its old time.
469
+ */
470
+ declare function rescheduleBooking(id: string, startsAt: Date | string, options?: CallsOptions): Promise<BookingMutationResult>;
471
+ /**
472
+ * Call a demo off: the Google event is deleted, the recorder is stood down, and
473
+ * the prospect is messaged if WhatsApp will carry it (see
474
+ * {@link BookingClientNotice}).
475
+ *
476
+ * `reason` is included in that message. Nothing is marked cancelled unless the
477
+ * calendar agreed first, so `{ ok: false, code: "CALENDAR" }` means the demo is
478
+ * still on.
479
+ */
480
+ declare function cancelBooking(id: string, reason?: string, options?: CallsOptions): Promise<BookingMutationResult>;
396
481
  /**
397
482
  * `JSON.stringify` that is safe inside `<script type="application/ld+json">`.
398
483
  *
@@ -413,4 +498,4 @@ declare function updateBookingSettings(changes: Partial<BookingSettings>, option
413
498
  */
414
499
  declare function escapeJsonForScript(value: unknown): string;
415
500
 
416
- export { type BookingSettings, type BookingSettingsResult, type BookingSettingsState, type BookingWorkingHours, type Call, type CallAudio, type CallListItem, type CallMoment, type CallParticipant, type CallsOptions, type CaptureServerErrorsOptions, type DeleteCallResult, type GetCallOptions, type Guide, type GuideReadOptions, type GuideSummary, type ListCallsOptions, ReportResult, ReportSeverity, ReportType, type ServerReportOptions, type ServerReporterConfig, type ServerReporterIdentity, type SignSessionOptions, captureServerErrors, configureServerReporter, deleteCall, escapeJsonForScript, getBookingSettings, getCall, getGuide, listCalls, listGuides, reportServerError, resetServerReporter, signSession, updateBookingSettings };
501
+ export { type Booking, type BookingClientNotice, type BookingMutationResult, type BookingNotifyRecipient, type BookingSettings, type BookingSettingsResult, type BookingSettingsState, type BookingWorkingHours, type Call, type CallAudio, type CallListItem, type CallMoment, type CallParticipant, type CallsOptions, type CaptureServerErrorsOptions, type DeleteCallResult, type GetCallOptions, type Guide, type GuideReadOptions, type GuideSummary, type ListBookingsOptions, type ListCallsOptions, ReportResult, ReportSeverity, ReportType, type ServerReportOptions, type ServerReporterConfig, type ServerReporterIdentity, type SignSessionOptions, cancelBooking, captureServerErrors, configureServerReporter, deleteCall, escapeJsonForScript, getBookingSettings, getCall, getGuide, listBookings, listCalls, listGuides, reportServerError, rescheduleBooking, resetServerReporter, signSession, updateBookingSettings };
package/dist/server.js CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/server.ts
21
21
  var server_exports = {};
22
22
  __export(server_exports, {
23
+ cancelBooking: () => cancelBooking,
23
24
  captureServerErrors: () => captureServerErrors,
24
25
  configureServerReporter: () => configureServerReporter,
25
26
  deleteCall: () => deleteCall,
@@ -27,9 +28,11 @@ __export(server_exports, {
27
28
  getBookingSettings: () => getBookingSettings,
28
29
  getCall: () => getCall,
29
30
  getGuide: () => getGuide,
31
+ listBookings: () => listBookings,
30
32
  listCalls: () => listCalls,
31
33
  listGuides: () => listGuides,
32
34
  reportServerError: () => reportServerError,
35
+ rescheduleBooking: () => rescheduleBooking,
33
36
  resetServerReporter: () => resetServerReporter,
34
37
  signSession: () => signSession,
35
38
  updateBookingSettings: () => updateBookingSettings
@@ -470,7 +473,14 @@ function readBookingSettingsState(value) {
470
473
  hostAvatarUrl: stringOrNull(s.hostAvatarUrl),
471
474
  horizonDays: (_c = numberOrNull(s.horizonDays)) != null ? _c : 15,
472
475
  noticeMinutes: (_d = numberOrNull(s.noticeMinutes)) != null ? _d : 120,
473
- whatsappCode: stringOrNull(s.whatsappCode)
476
+ whatsappCode: stringOrNull(s.whatsappCode),
477
+ notifyRecipients: Array.isArray(s.notifyRecipients) ? s.notifyRecipients.filter(isRecord).map((row) => {
478
+ var _a2, _b2;
479
+ return {
480
+ name: (_a2 = stringOrNull(row.name)) != null ? _a2 : "",
481
+ phone: (_b2 = stringOrNull(row.phone)) != null ? _b2 : ""
482
+ };
483
+ }).filter((row) => row.phone !== "") : []
474
484
  },
475
485
  saved: value.saved === true,
476
486
  calendarConnected: value.calendarConnected === true
@@ -502,6 +512,85 @@ async function updateBookingSettings(changes, options = {}) {
502
512
  return { ok: false, error: "Could not save booking settings" };
503
513
  }
504
514
  }
515
+ function readBooking(value) {
516
+ var _a, _b, _c, _d, _e, _f, _g;
517
+ if (!isRecord(value) || typeof value.id !== "string") return null;
518
+ return {
519
+ id: value.id,
520
+ name: (_a = stringOrNull(value.name)) != null ? _a : "",
521
+ email: (_b = stringOrNull(value.email)) != null ? _b : "",
522
+ phone: (_c = stringOrNull(value.phone)) != null ? _c : "",
523
+ startsAt: (_d = stringOrNull(value.startsAt)) != null ? _d : "",
524
+ endsAt: (_e = stringOrNull(value.endsAt)) != null ? _e : "",
525
+ timezone: stringOrNull(value.timezone),
526
+ note: stringOrNull(value.note),
527
+ status: (_f = stringOrNull(value.status)) != null ? _f : "CONFIRMED",
528
+ meetUrl: stringOrNull(value.meetUrl),
529
+ createdAt: (_g = stringOrNull(value.createdAt)) != null ? _g : ""
530
+ };
531
+ }
532
+ async function listBookings(options = {}) {
533
+ try {
534
+ const key = callsKey(options);
535
+ if (!key) return [];
536
+ const query = new URLSearchParams();
537
+ if (options.scope === "past") query.set("scope", "past");
538
+ setNumber(query, "limit", options.limit);
539
+ const qs = query.toString();
540
+ const { data } = await callSdkApi(`/api/v1/sdk/bookings${qs ? `?${qs}` : ""}`, key, options, {
541
+ cache: "no-store"
542
+ });
543
+ if (!Array.isArray(data)) return [];
544
+ return data.map(readBooking).filter((booking) => booking !== null);
545
+ } catch (e) {
546
+ return [];
547
+ }
548
+ }
549
+ function readMutation(data, error) {
550
+ if (error !== null) return { ok: false, error, code: null };
551
+ const record = isRecord(data) ? data : {};
552
+ const notice = stringOrNull(record.clientNotice);
553
+ return {
554
+ ok: true,
555
+ booking: readBooking(record.booking),
556
+ clientNotice: notice === "sent" || notice === "attempted" ? notice : "none"
557
+ };
558
+ }
559
+ async function rescheduleBooking(id, startsAt, options = {}) {
560
+ try {
561
+ const wanted = typeof id === "string" ? id.trim() : "";
562
+ const key = callsKey(options);
563
+ if (!wanted) return { ok: false, error: "No booking id", code: null };
564
+ if (!key) return { ok: false, error: NO_KEY_ERROR, code: null };
565
+ const when = startsAt instanceof Date ? startsAt.toISOString() : String(startsAt);
566
+ const { data, error } = await callSdkApi(
567
+ `/api/v1/sdk/bookings?id=${encodeURIComponent(wanted)}`,
568
+ key,
569
+ options,
570
+ { method: "PATCH", body: { startsAt: when }, cache: "no-store" }
571
+ );
572
+ return readMutation(data, error);
573
+ } catch (e) {
574
+ return { ok: false, error: "Could not move that demo", code: null };
575
+ }
576
+ }
577
+ async function cancelBooking(id, reason, options = {}) {
578
+ try {
579
+ const wanted = typeof id === "string" ? id.trim() : "";
580
+ const key = callsKey(options);
581
+ if (!wanted) return { ok: false, error: "No booking id", code: null };
582
+ if (!key) return { ok: false, error: NO_KEY_ERROR, code: null };
583
+ const query = new URLSearchParams({ id: wanted });
584
+ if (typeof reason === "string" && reason.trim()) query.set("reason", reason.trim().slice(0, 200));
585
+ const { data, error } = await callSdkApi(`/api/v1/sdk/bookings?${query.toString()}`, key, options, {
586
+ method: "DELETE",
587
+ cache: "no-store"
588
+ });
589
+ return readMutation(data, error);
590
+ } catch (e) {
591
+ return { ok: false, error: "Could not cancel that demo", code: null };
592
+ }
593
+ }
505
594
  function escapeJsonForScript(value) {
506
595
  try {
507
596
  const json = JSON.stringify(value);
@@ -513,6 +602,7 @@ function escapeJsonForScript(value) {
513
602
  }
514
603
  // Annotate the CommonJS export names for ESM import in node:
515
604
  0 && (module.exports = {
605
+ cancelBooking,
516
606
  captureServerErrors,
517
607
  configureServerReporter,
518
608
  deleteCall,
@@ -520,9 +610,11 @@ function escapeJsonForScript(value) {
520
610
  getBookingSettings,
521
611
  getCall,
522
612
  getGuide,
613
+ listBookings,
523
614
  listCalls,
524
615
  listGuides,
525
616
  reportServerError,
617
+ rescheduleBooking,
526
618
  resetServerReporter,
527
619
  signSession,
528
620
  updateBookingSettings