@bookitive/widget-js 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -77,6 +77,9 @@ interface WidgetController {
77
77
  setFloor: (floorId: string) => void;
78
78
  getSelection: () => SelectionItem[];
79
79
  clearSelection: () => void;
80
+ /** Selects every currently free seat of one ticket type (same rules as
81
+ * clicking each seat); returns how many were added. */
82
+ selectAvailableByType: (ticketTypeId: string) => number;
80
83
  /**
81
84
  * Mounts the Bookitive captcha bridge (a small iframe on the Bookitive
82
85
  * origin) into el — put it where your form's captcha would sit. Once
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// @bookitive/widget-js — a thin, typed loader for the embeddable Bookitive\n// seat-map widget, in the spirit of @stripe/stripe-js.\n//\n// The widget itself is a UI runtime (Svelte + the seat-map renderer + live\n// availability) served from a versioned CDN bundle — you should NOT bundle it\n// into your app. This package injects that <script> for you and hands back a\n// fully typed `Bookitive.mount()`, so hosts get type-safety and clean imports\n// while the runtime keeps auto-updating from the CDN.\n//\n// import { loadBookitive } from '@bookitive/widget-js';\n// const bk = await loadBookitive();\n// const controller = bk.mount(el, { org: 'city-theatre', event: 'spring-gala-2027', legend: true });\n// el.addEventListener('bookitive:selectionchanged', (e) => {\n// const { selection, totalCents } = (e as BookitiveCustomEvent<'bookitive:selectionchanged'>).detail;\n// });\n\n// --- mount surface -----------------------------------------------------------\n// SINGLE SOURCE OF TRUTH: these mirror the widget's public API in\n// packages/widget/types/index.d.ts. They're intentionally re-declared here (not\n// imported) so this published loader has no dependency on the private runtime\n// package — the @stripe/stripe-js pattern. Keep the two in sync when the mount\n// options / controller change.\n\nexport interface WidgetOptions {\n /** API origin; defaults to the production API. */\n baseUrl?: string;\n /** Organization slug. */\n org: string;\n /** Event slug. */\n event: string;\n /** Floor id to render first (from bookitive:ready → event.floors); default first. */\n floor?: string;\n /** Show seat code labels on the map. */\n showLabels?: boolean;\n /** Render a legend (ticket-type colours + seat statuses) above the map. */\n legend?: boolean;\n /** Show the \"Powered by Bookitive\" credit below the map (default true). */\n credit?: boolean;\n /** Origin serving the hosted checkout/captcha frames (RFC 0002); defaults\n * to the production events origin. Override for local dev. */\n checkoutOrigin?: string;\n /** UI language for Bookitive-rendered surfaces (the verification modal):\n * 'cs' | 'en'. Defaults to the buyer's browser language. */\n locale?: string;\n /** Set false to handle a VERIFICATION_REQUIRED error yourself (your own OTP\n * UI) instead of the auto-popped Bookitive modal. Default true. */\n autoVerify?: boolean;\n}\n\nexport interface SelectionItem {\n seatId: string;\n code: string;\n /** Pretty localized label, e.g. \"Stůl A7 · Místo 3\". */\n label: string;\n ticketTypeId: string;\n ticketTypeName: string;\n priceCents: number;\n}\n\nexport interface CheckoutInput {\n customer: { name: string; email: string; phone?: string };\n /** Standing/GA quantities the host sells outside the map; may carry one\n * attendee name per unit when the event requires named tickets. */\n extraItems?: {\n ticketTypeId: string;\n gaAreaId?: string;\n quantity: number;\n attendees?: string[];\n }[];\n /** Attendee name per selected seat (seatId → full name), for events that\n * require named tickets. */\n seatAttendees?: Record<string, string>;\n /** Promo code to redeem (case-insensitive); validated server-side. */\n discountCode?: string;\n /** Single-use token from verifyCheck(); required when the event demands a\n * verified phone (event.protection.requireVerifiedPhone). */\n verificationToken?: string;\n captchaToken?: string;\n}\n\n/** Proof a phone passed OTP, returned by verifyCheck(). */\nexport interface VerificationResult {\n verificationToken: string;\n expiresAt?: string;\n}\n\nexport interface CheckoutResult {\n reference?: string;\n status?: string;\n totalCents?: number;\n currency?: string;\n expiresAt?: string;\n payment?: { bankAccount?: string; variableSymbol?: string };\n}\n\n/** The handle mount() returns. */\nexport interface WidgetController {\n setFloor: (floorId: string) => void;\n getSelection: () => SelectionItem[];\n clearSelection: () => void;\n /**\n * Mounts the Bookitive captcha bridge (a small iframe on the Bookitive\n * origin) into el — put it where your form's captcha would sit. Once\n * mounted, checkout() (and verifyStart) route through that frame, so the\n * captcha is solved and verified on the Bookitive origin: your domain never\n * needs any captcha configuration.\n */\n mountCaptcha: (el: HTMLElement) => void;\n /**\n * Sends an SMS one-time code for events that require a verified phone\n * (event.protection.requireVerifiedPhone). Captcha-gated — routes through\n * the mounted bridge. Rejects on bad/blocked numbers or rate limits.\n */\n verifyStart: (phone: string) => Promise<void>;\n /**\n * Validates the SMS code and returns a single-use verificationToken to pass\n * as checkout({ verificationToken }). Rejects on wrong/expired codes.\n */\n verifyCheck: (phone: string, code: string) => Promise<VerificationResult>;\n checkout: (input: CheckoutInput) => Promise<CheckoutResult>;\n destroy: () => void;\n}\n\n/** The global the CDN runtime installs on `window`. */\nexport interface Bookitive {\n mount: (el: HTMLElement, options: WidgetOptions) => WidgetController;\n}\n\n// --- CustomEvents the widget dispatches (bubble from the mount element) -------\n\nexport interface BookitiveEventDetail {\n \"bookitive:ready\": { event: unknown };\n \"bookitive:selectionchanged\": {\n selection: SelectionItem[];\n totalCents: number;\n };\n \"bookitive:seatselected\": { seatId: string; code?: string };\n \"bookitive:seatdeselected\": {\n seatId: string;\n code?: string;\n reason?: \"taken\";\n };\n \"bookitive:error\": { message: string };\n /** A seat click was refused because the ticket type's per-order max is\n * reached — show the buyer a \"max N per order\" hint. */\n \"bookitive:limitreached\": {\n ticketTypeId: string;\n ticketTypeName?: string;\n max: number;\n };\n}\nexport type BookitiveEventName = keyof BookitiveEventDetail;\nexport type BookitiveCustomEvent<K extends BookitiveEventName> = CustomEvent<\n BookitiveEventDetail[K]\n>;\n\n// --- loader ------------------------------------------------------------------\n\nexport interface LoadBookitiveOptions {\n /** Origin serving the widget bundle. Default: https://events.bookitive.com */\n scriptOrigin?: string;\n /** Major version of the widget runtime. Default: \"v1\". */\n version?: string;\n}\n\nconst SCRIPT_ATTR = \"data-bookitive-widget\";\nlet pending: Promise<Bookitive> | null = null;\n\nfunction existingGlobal(): Bookitive | null {\n if (typeof window === \"undefined\") return null;\n return (window as unknown as { Bookitive?: Bookitive }).Bookitive ?? null;\n}\n\n/**\n * Inject the Bookitive widget runtime from the CDN (once) and resolve to the\n * typed global. Safe to call repeatedly — subsequent calls reuse the same\n * in-flight promise / already-loaded global. Rejects during SSR (no `window`).\n */\nexport function loadBookitive(\n options: LoadBookitiveOptions = {},\n): Promise<Bookitive> {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return Promise.reject(new Error(\"loadBookitive() must run in the browser\"));\n }\n const already = existingGlobal();\n if (already) return Promise.resolve(already);\n if (pending) return pending;\n\n const origin = (\n options.scriptOrigin ?? \"https://events.bookitive.com\"\n ).replace(/\\/+$/, \"\");\n const version = options.version ?? \"v1\";\n const src = `${origin}/widget/${version}.js`;\n\n pending = new Promise<Bookitive>((resolve, reject) => {\n // Clear the cached promise on any rejection so a transient failure can be\n // retried by a later loadBookitive() call (rather than caching the error).\n const settle = () => {\n const bk = existingGlobal();\n if (bk) return resolve(bk);\n pending = null;\n reject(\n new Error(\"Bookitive global missing after the widget script loaded\"),\n );\n };\n const fail = () => {\n pending = null;\n reject(new Error(`Failed to load the Bookitive widget from ${src}`));\n };\n\n // Reuse a script tag another loadBookitive() (or a manual embed) added.\n const existing = document.querySelector<HTMLScriptElement>(\n `script[${SCRIPT_ATTR}]`,\n );\n if (existing) {\n if (existingGlobal()) return settle();\n existing.addEventListener(\"load\", settle, { once: true });\n existing.addEventListener(\"error\", fail, { once: true });\n return;\n }\n\n const s = document.createElement(\"script\");\n s.src = src;\n s.async = true;\n s.setAttribute(SCRIPT_ATTR, \"\");\n s.addEventListener(\"load\", settle, { once: true });\n s.addEventListener(\"error\", fail, { once: true });\n document.head.appendChild(s);\n });\n return pending;\n}\n"],"mappings":";AAqKA,IAAM,cAAc;AACpB,IAAI,UAAqC;AAEzC,SAAS,iBAAmC;AAC1C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAQ,OAAgD,aAAa;AACvE;AAOO,SAAS,cACd,UAAgC,CAAC,GACb;AACpB,MAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aAAa;AACpE,WAAO,QAAQ,OAAO,IAAI,MAAM,yCAAyC,CAAC;AAAA,EAC5E;AACA,QAAM,UAAU,eAAe;AAC/B,MAAI,QAAS,QAAO,QAAQ,QAAQ,OAAO;AAC3C,MAAI,QAAS,QAAO;AAEpB,QAAM,UACJ,QAAQ,gBAAgB,gCACxB,QAAQ,QAAQ,EAAE;AACpB,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,MAAM,GAAG,MAAM,WAAW,OAAO;AAEvC,YAAU,IAAI,QAAmB,CAAC,SAAS,WAAW;AAGpD,UAAM,SAAS,MAAM;AACnB,YAAM,KAAK,eAAe;AAC1B,UAAI,GAAI,QAAO,QAAQ,EAAE;AACzB,gBAAU;AACV;AAAA,QACE,IAAI,MAAM,yDAAyD;AAAA,MACrE;AAAA,IACF;AACA,UAAM,OAAO,MAAM;AACjB,gBAAU;AACV,aAAO,IAAI,MAAM,4CAA4C,GAAG,EAAE,CAAC;AAAA,IACrE;AAGA,UAAM,WAAW,SAAS;AAAA,MACxB,UAAU,WAAW;AAAA,IACvB;AACA,QAAI,UAAU;AACZ,UAAI,eAAe,EAAG,QAAO,OAAO;AACpC,eAAS,iBAAiB,QAAQ,QAAQ,EAAE,MAAM,KAAK,CAAC;AACxD,eAAS,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;AACvD;AAAA,IACF;AAEA,UAAM,IAAI,SAAS,cAAc,QAAQ;AACzC,MAAE,MAAM;AACR,MAAE,QAAQ;AACV,MAAE,aAAa,aAAa,EAAE;AAC9B,MAAE,iBAAiB,QAAQ,QAAQ,EAAE,MAAM,KAAK,CAAC;AACjD,MAAE,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;AAChD,aAAS,KAAK,YAAY,CAAC;AAAA,EAC7B,CAAC;AACD,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// @bookitive/widget-js — a thin, typed loader for the embeddable Bookitive\n// seat-map widget, in the spirit of @stripe/stripe-js.\n//\n// The widget itself is a UI runtime (Svelte + the seat-map renderer + live\n// availability) served from a versioned CDN bundle — you should NOT bundle it\n// into your app. This package injects that <script> for you and hands back a\n// fully typed `Bookitive.mount()`, so hosts get type-safety and clean imports\n// while the runtime keeps auto-updating from the CDN.\n//\n// import { loadBookitive } from '@bookitive/widget-js';\n// const bk = await loadBookitive();\n// const controller = bk.mount(el, { org: 'city-theatre', event: 'spring-gala-2027', legend: true });\n// el.addEventListener('bookitive:selectionchanged', (e) => {\n// const { selection, totalCents } = (e as BookitiveCustomEvent<'bookitive:selectionchanged'>).detail;\n// });\n\n// --- mount surface -----------------------------------------------------------\n// SINGLE SOURCE OF TRUTH: these mirror the widget's public API in\n// packages/widget/types/index.d.ts. They're intentionally re-declared here (not\n// imported) so this published loader has no dependency on the private runtime\n// package — the @stripe/stripe-js pattern. Keep the two in sync when the mount\n// options / controller change.\n\nexport interface WidgetOptions {\n /** API origin; defaults to the production API. */\n baseUrl?: string;\n /** Organization slug. */\n org: string;\n /** Event slug. */\n event: string;\n /** Floor id to render first (from bookitive:ready → event.floors); default first. */\n floor?: string;\n /** Show seat code labels on the map. */\n showLabels?: boolean;\n /** Render a legend (ticket-type colours + seat statuses) above the map. */\n legend?: boolean;\n /** Show the \"Powered by Bookitive\" credit below the map (default true). */\n credit?: boolean;\n /** Origin serving the hosted checkout/captcha frames (RFC 0002); defaults\n * to the production events origin. Override for local dev. */\n checkoutOrigin?: string;\n /** UI language for Bookitive-rendered surfaces (the verification modal):\n * 'cs' | 'en'. Defaults to the buyer's browser language. */\n locale?: string;\n /** Set false to handle a VERIFICATION_REQUIRED error yourself (your own OTP\n * UI) instead of the auto-popped Bookitive modal. Default true. */\n autoVerify?: boolean;\n}\n\nexport interface SelectionItem {\n seatId: string;\n code: string;\n /** Pretty localized label, e.g. \"Stůl A7 · Místo 3\". */\n label: string;\n ticketTypeId: string;\n ticketTypeName: string;\n priceCents: number;\n}\n\nexport interface CheckoutInput {\n customer: { name: string; email: string; phone?: string };\n /** Standing/GA quantities the host sells outside the map; may carry one\n * attendee name per unit when the event requires named tickets. */\n extraItems?: {\n ticketTypeId: string;\n gaAreaId?: string;\n quantity: number;\n attendees?: string[];\n }[];\n /** Attendee name per selected seat (seatId → full name), for events that\n * require named tickets. */\n seatAttendees?: Record<string, string>;\n /** Promo code to redeem (case-insensitive); validated server-side. */\n discountCode?: string;\n /** Single-use token from verifyCheck(); required when the event demands a\n * verified phone (event.protection.requireVerifiedPhone). */\n verificationToken?: string;\n captchaToken?: string;\n}\n\n/** Proof a phone passed OTP, returned by verifyCheck(). */\nexport interface VerificationResult {\n verificationToken: string;\n expiresAt?: string;\n}\n\nexport interface CheckoutResult {\n reference?: string;\n status?: string;\n totalCents?: number;\n currency?: string;\n expiresAt?: string;\n payment?: { bankAccount?: string; variableSymbol?: string };\n}\n\n/** The handle mount() returns. */\nexport interface WidgetController {\n setFloor: (floorId: string) => void;\n getSelection: () => SelectionItem[];\n clearSelection: () => void;\n /** Selects every currently free seat of one ticket type (same rules as\n * clicking each seat); returns how many were added. */\n selectAvailableByType: (ticketTypeId: string) => number;\n /**\n * Mounts the Bookitive captcha bridge (a small iframe on the Bookitive\n * origin) into el — put it where your form's captcha would sit. Once\n * mounted, checkout() (and verifyStart) route through that frame, so the\n * captcha is solved and verified on the Bookitive origin: your domain never\n * needs any captcha configuration.\n */\n mountCaptcha: (el: HTMLElement) => void;\n /**\n * Sends an SMS one-time code for events that require a verified phone\n * (event.protection.requireVerifiedPhone). Captcha-gated — routes through\n * the mounted bridge. Rejects on bad/blocked numbers or rate limits.\n */\n verifyStart: (phone: string) => Promise<void>;\n /**\n * Validates the SMS code and returns a single-use verificationToken to pass\n * as checkout({ verificationToken }). Rejects on wrong/expired codes.\n */\n verifyCheck: (phone: string, code: string) => Promise<VerificationResult>;\n checkout: (input: CheckoutInput) => Promise<CheckoutResult>;\n destroy: () => void;\n}\n\n/** The global the CDN runtime installs on `window`. */\nexport interface Bookitive {\n mount: (el: HTMLElement, options: WidgetOptions) => WidgetController;\n}\n\n// --- CustomEvents the widget dispatches (bubble from the mount element) -------\n\nexport interface BookitiveEventDetail {\n \"bookitive:ready\": { event: unknown };\n \"bookitive:selectionchanged\": {\n selection: SelectionItem[];\n totalCents: number;\n };\n \"bookitive:seatselected\": { seatId: string; code?: string };\n \"bookitive:seatdeselected\": {\n seatId: string;\n code?: string;\n reason?: \"taken\";\n };\n \"bookitive:error\": { message: string };\n /** A seat click was refused because the ticket type's per-order max is\n * reached — show the buyer a \"max N per order\" hint. */\n \"bookitive:limitreached\": {\n ticketTypeId: string;\n ticketTypeName?: string;\n max: number;\n };\n}\nexport type BookitiveEventName = keyof BookitiveEventDetail;\nexport type BookitiveCustomEvent<K extends BookitiveEventName> = CustomEvent<\n BookitiveEventDetail[K]\n>;\n\n// --- loader ------------------------------------------------------------------\n\nexport interface LoadBookitiveOptions {\n /** Origin serving the widget bundle. Default: https://events.bookitive.com */\n scriptOrigin?: string;\n /** Major version of the widget runtime. Default: \"v1\". */\n version?: string;\n}\n\nconst SCRIPT_ATTR = \"data-bookitive-widget\";\nlet pending: Promise<Bookitive> | null = null;\n\nfunction existingGlobal(): Bookitive | null {\n if (typeof window === \"undefined\") return null;\n return (window as unknown as { Bookitive?: Bookitive }).Bookitive ?? null;\n}\n\n/**\n * Inject the Bookitive widget runtime from the CDN (once) and resolve to the\n * typed global. Safe to call repeatedly — subsequent calls reuse the same\n * in-flight promise / already-loaded global. Rejects during SSR (no `window`).\n */\nexport function loadBookitive(\n options: LoadBookitiveOptions = {},\n): Promise<Bookitive> {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return Promise.reject(new Error(\"loadBookitive() must run in the browser\"));\n }\n const already = existingGlobal();\n if (already) return Promise.resolve(already);\n if (pending) return pending;\n\n const origin = (\n options.scriptOrigin ?? \"https://events.bookitive.com\"\n ).replace(/\\/+$/, \"\");\n const version = options.version ?? \"v1\";\n const src = `${origin}/widget/${version}.js`;\n\n pending = new Promise<Bookitive>((resolve, reject) => {\n // Clear the cached promise on any rejection so a transient failure can be\n // retried by a later loadBookitive() call (rather than caching the error).\n const settle = () => {\n const bk = existingGlobal();\n if (bk) return resolve(bk);\n pending = null;\n reject(\n new Error(\"Bookitive global missing after the widget script loaded\"),\n );\n };\n const fail = () => {\n pending = null;\n reject(new Error(`Failed to load the Bookitive widget from ${src}`));\n };\n\n // Reuse a script tag another loadBookitive() (or a manual embed) added.\n const existing = document.querySelector<HTMLScriptElement>(\n `script[${SCRIPT_ATTR}]`,\n );\n if (existing) {\n if (existingGlobal()) return settle();\n existing.addEventListener(\"load\", settle, { once: true });\n existing.addEventListener(\"error\", fail, { once: true });\n return;\n }\n\n const s = document.createElement(\"script\");\n s.src = src;\n s.async = true;\n s.setAttribute(SCRIPT_ATTR, \"\");\n s.addEventListener(\"load\", settle, { once: true });\n s.addEventListener(\"error\", fail, { once: true });\n document.head.appendChild(s);\n });\n return pending;\n}\n"],"mappings":";AAwKA,IAAM,cAAc;AACpB,IAAI,UAAqC;AAEzC,SAAS,iBAAmC;AAC1C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAQ,OAAgD,aAAa;AACvE;AAOO,SAAS,cACd,UAAgC,CAAC,GACb;AACpB,MAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aAAa;AACpE,WAAO,QAAQ,OAAO,IAAI,MAAM,yCAAyC,CAAC;AAAA,EAC5E;AACA,QAAM,UAAU,eAAe;AAC/B,MAAI,QAAS,QAAO,QAAQ,QAAQ,OAAO;AAC3C,MAAI,QAAS,QAAO;AAEpB,QAAM,UACJ,QAAQ,gBAAgB,gCACxB,QAAQ,QAAQ,EAAE;AACpB,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,MAAM,GAAG,MAAM,WAAW,OAAO;AAEvC,YAAU,IAAI,QAAmB,CAAC,SAAS,WAAW;AAGpD,UAAM,SAAS,MAAM;AACnB,YAAM,KAAK,eAAe;AAC1B,UAAI,GAAI,QAAO,QAAQ,EAAE;AACzB,gBAAU;AACV;AAAA,QACE,IAAI,MAAM,yDAAyD;AAAA,MACrE;AAAA,IACF;AACA,UAAM,OAAO,MAAM;AACjB,gBAAU;AACV,aAAO,IAAI,MAAM,4CAA4C,GAAG,EAAE,CAAC;AAAA,IACrE;AAGA,UAAM,WAAW,SAAS;AAAA,MACxB,UAAU,WAAW;AAAA,IACvB;AACA,QAAI,UAAU;AACZ,UAAI,eAAe,EAAG,QAAO,OAAO;AACpC,eAAS,iBAAiB,QAAQ,QAAQ,EAAE,MAAM,KAAK,CAAC;AACxD,eAAS,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;AACvD;AAAA,IACF;AAEA,UAAM,IAAI,SAAS,cAAc,QAAQ;AACzC,MAAE,MAAM;AACR,MAAE,QAAQ;AACV,MAAE,aAAa,aAAa,EAAE;AAC9B,MAAE,iBAAiB,QAAQ,QAAQ,EAAE,MAAM,KAAK,CAAC;AACjD,MAAE,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;AAChD,aAAS,KAAK,YAAY,CAAC;AAAA,EAC7B,CAAC;AACD,SAAO;AACT;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bookitive/widget-js",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Loader + TypeScript types for the embeddable Bookitive seat-map widget. Injects the CDN runtime (like @stripe/stripe-js) and returns a typed mount().",
5
5
  "license": "MIT",
6
6
  "type": "module",