@seatlayer/js 0.39.0 → 0.41.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.cjs +847 -732
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +226 -166
- package/dist/index.d.ts +226 -166
- package/dist/index.js +847 -732
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -185,6 +185,176 @@ declare function createBuyerAccessContext(options: {
|
|
|
185
185
|
buyerAccessToken?: string | BuyerAccessToken;
|
|
186
186
|
}, hooks?: Pick<BuyerAccessContextOptions, 'onExpired' | 'onUnavailable'>): BuyerAccessContext | null;
|
|
187
187
|
|
|
188
|
+
/**
|
|
189
|
+
* BuyerRealtimeClient — the client half of `docs/realtime-protocol-2026-08-01.md`.
|
|
190
|
+
*
|
|
191
|
+
* Why this exists as a separate socket rather than inside PickerController:
|
|
192
|
+
* a private scope authenticates with a one-use **subscribe ticket** carried in
|
|
193
|
+
* `Sec-WebSocket-Protocol`, and a browser can only set that at construction —
|
|
194
|
+
* `new WebSocket(url, protocols)`. The controller's socket is built from a URL
|
|
195
|
+
* alone (`PickerTransport.socketUrl`), and a bearer must never travel in a URL
|
|
196
|
+
* because URLs are routinely logged. So an access-scoped picker asks the
|
|
197
|
+
* transport for an empty `socketUrl()` (the controller then skips its own
|
|
198
|
+
* connection entirely) and this client owns the wire instead.
|
|
199
|
+
*
|
|
200
|
+
* A tokenless public picker never reaches this file. It keeps the controller's
|
|
201
|
+
* original socket, offers no subprotocol, and therefore receives byte-for-byte
|
|
202
|
+
* the frames it received before this module existed (protocol doc §1).
|
|
203
|
+
*
|
|
204
|
+
* What it implements:
|
|
205
|
+
* - protocol negotiation: offer `seatlayer.v1`, believe the 101 echo, and fall
|
|
206
|
+
* back to legacy frame handling when the server (or a proxy) does not echo;
|
|
207
|
+
* - the ticket exchange, one mint per connection attempt, ticket in the
|
|
208
|
+
* subprotocol list and never in the URL;
|
|
209
|
+
* - compact `{default, exceptions}` snapshot reconstruction;
|
|
210
|
+
* - `sv.<n>` resume, handling BOTH outcomes (a `resumed` delta or a full
|
|
211
|
+
* snapshot) on every reconnect;
|
|
212
|
+
* - close code 4401 as a typed access-revoked state, never a reconnect loop;
|
|
213
|
+
* - liveness by ping/pong only. Silence is normal and carries no information
|
|
214
|
+
* (protocol doc §5) — a quiet socket is never treated as a dead one.
|
|
215
|
+
*/
|
|
216
|
+
|
|
217
|
+
/** The projected status of one unit, as the server words it on the wire. */
|
|
218
|
+
type WireStatus = string;
|
|
219
|
+
/** A scope's projection: one default plus the units that differ from it. */
|
|
220
|
+
interface Projection {
|
|
221
|
+
default: WireStatus;
|
|
222
|
+
exceptions: Record<string, WireStatus>;
|
|
223
|
+
}
|
|
224
|
+
interface StatusChange {
|
|
225
|
+
label: string;
|
|
226
|
+
status: WireStatus;
|
|
227
|
+
}
|
|
228
|
+
/** Where reconstructed inventory goes. Implemented over PickerController. */
|
|
229
|
+
interface RealtimeSink {
|
|
230
|
+
/** Apply a batch of label→status changes. Implementations must paint this as
|
|
231
|
+
* ONE pass, not one pass per label (motion system §4 rule 2). */
|
|
232
|
+
applyStatuses(changes: StatusChange[]): void;
|
|
233
|
+
/** Re-pull authoritative state over the scoped HTTP route. Used when a frame
|
|
234
|
+
* cannot be diffed against what we hold (first snapshot, or the scope's
|
|
235
|
+
* default itself changed, which redefines every unit we were never told
|
|
236
|
+
* about). */
|
|
237
|
+
resync(): void | Promise<void>;
|
|
238
|
+
/** Section availability changed (channel-agnostic; identical for every scope). */
|
|
239
|
+
onSections?(hidden: string[], closed: string[]): void;
|
|
240
|
+
/** Scope-projected presence counters. */
|
|
241
|
+
onPresence?(counts: {
|
|
242
|
+
shoppingSessions: number;
|
|
243
|
+
activeHolds: number;
|
|
244
|
+
}): void;
|
|
245
|
+
}
|
|
246
|
+
interface SubscribeTicket$1 {
|
|
247
|
+
ticket?: string;
|
|
248
|
+
/** Exactly what to hand `new WebSocket(url, protocols)`, per protocol doc §3. */
|
|
249
|
+
protocols?: string[];
|
|
250
|
+
}
|
|
251
|
+
interface BuyerRealtimeOptions {
|
|
252
|
+
/** The subscribe URL. Must never carry a credential — asserted below. */
|
|
253
|
+
url: string;
|
|
254
|
+
sink: RealtimeSink;
|
|
255
|
+
/** Mint a one-use ticket for THIS connection attempt. Returns null for the
|
|
256
|
+
* anonymous public case (no ticket needed). Throwing stops the client. */
|
|
257
|
+
mintTicket?: () => Promise<SubscribeTicket$1 | null>;
|
|
258
|
+
/** Typed access states. 4401 arrives here as `revoked`. */
|
|
259
|
+
onAccessUnavailable?: (event: BuyerAccessUnavailableEvent) => void;
|
|
260
|
+
/** Test seam. Defaults to the global WebSocket. */
|
|
261
|
+
socketFactory?: (url: string, protocols: string[]) => WebSocket;
|
|
262
|
+
/** Test seam for the keepalive/backoff timers. */
|
|
263
|
+
now?: () => number;
|
|
264
|
+
}
|
|
265
|
+
declare class BuyerRealtimeClient {
|
|
266
|
+
private readonly opts;
|
|
267
|
+
private ws;
|
|
268
|
+
private stopped;
|
|
269
|
+
private attempt;
|
|
270
|
+
private reconnectTimer;
|
|
271
|
+
private pingTimer;
|
|
272
|
+
private pongTimer;
|
|
273
|
+
private resumeTimer;
|
|
274
|
+
/** Our model of this scope's projection. Null until the first snapshot. */
|
|
275
|
+
private projection;
|
|
276
|
+
/** Last `snapshotVersion` seen on any frame that carried one — the resume point. */
|
|
277
|
+
private version;
|
|
278
|
+
/** True once the 101 echoed `seatlayer.v1`. */
|
|
279
|
+
private v1;
|
|
280
|
+
/** Set when we offered v1 and the handshake came back without it — a proxy
|
|
281
|
+
* most likely stripped the header, so the next attempt selects the v1 frame
|
|
282
|
+
* format with the `?pv=1` marker instead (protocol doc §1). The marker
|
|
283
|
+
* selects a format and can never carry a credential or widen a scope. */
|
|
284
|
+
private useQueryMarker;
|
|
285
|
+
private hidden;
|
|
286
|
+
private closedSections;
|
|
287
|
+
constructor(options: BuyerRealtimeOptions);
|
|
288
|
+
/** Negotiated protocol, for tests and diagnostics. */
|
|
289
|
+
get protocol(): 'v1' | 'legacy' | null;
|
|
290
|
+
get snapshotVersion(): number | null;
|
|
291
|
+
start(): void;
|
|
292
|
+
/** Stop for good (destroy, or a revocation). Safe to call twice. */
|
|
293
|
+
stop(): void;
|
|
294
|
+
/** Restart after the host re-authorized a revoked buyer (`refreshAccess()`). */
|
|
295
|
+
restart(): void;
|
|
296
|
+
private connect;
|
|
297
|
+
private handleFrame;
|
|
298
|
+
/** The server answered our resume; cancel the fallback resync. */
|
|
299
|
+
private answered;
|
|
300
|
+
private reportIfAccessError;
|
|
301
|
+
/**
|
|
302
|
+
* Liveness is ping/pong, and only ping/pong. A socket that receives nothing
|
|
303
|
+
* for minutes is the normal, correct state for a narrowly-scoped buyer on a
|
|
304
|
+
* busy event (protocol doc §5), so quiet time never triggers a reconnect.
|
|
305
|
+
*/
|
|
306
|
+
private startKeepalive;
|
|
307
|
+
/**
|
|
308
|
+
* FULL jitter, not plain exponential backoff.
|
|
309
|
+
*
|
|
310
|
+
* A deterministic `2**attempt` schedule makes every browser that lost the same
|
|
311
|
+
* socket — a worker redeploy, a DO eviction, a flaky edge PoP — come back in
|
|
312
|
+
* the same millisecond, and an on-sale crowd reconnecting in lockstep is the
|
|
313
|
+
* thing that turns one blip into a self-sustaining thundering herd. Full
|
|
314
|
+
* jitter (`random() * ceiling`) spreads the same crowd across the whole
|
|
315
|
+
* window; the ceiling still doubles, so a persistent outage still backs off.
|
|
316
|
+
*
|
|
317
|
+
* `Math.random` is correct here: this is client code choosing a delay, not a
|
|
318
|
+
* Workflow step that has to replay deterministically.
|
|
319
|
+
*/
|
|
320
|
+
private scheduleReconnect;
|
|
321
|
+
private clearPongTimer;
|
|
322
|
+
private clearTimers;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* The slice of PickerController this module drives. Structural on purpose — it
|
|
326
|
+
* keeps this file free of an `@seatlayer/core` import, and every member here is
|
|
327
|
+
* public controller API, so nothing in the engine mirror has to change.
|
|
328
|
+
*/
|
|
329
|
+
interface PickerControllerLike {
|
|
330
|
+
idForLabel(label: string): string | undefined;
|
|
331
|
+
tableSelection(seatIdOrLabel: string): {
|
|
332
|
+
physicalSeatIds: string[];
|
|
333
|
+
} | null;
|
|
334
|
+
setStatus(ids: string[], status: 'free' | 'held' | 'booked' | 'not_for_sale'): void;
|
|
335
|
+
getStatus(id: string): string | undefined;
|
|
336
|
+
flashSeat(id: string, color?: string): void;
|
|
337
|
+
currentHold(): {
|
|
338
|
+
labels: string[];
|
|
339
|
+
} | null;
|
|
340
|
+
getSelection(): Array<{
|
|
341
|
+
id: string;
|
|
342
|
+
label: string;
|
|
343
|
+
}>;
|
|
344
|
+
deselect(ids: string[]): void;
|
|
345
|
+
refresh(): Promise<void>;
|
|
346
|
+
}
|
|
347
|
+
interface ControllerSinkOptions {
|
|
348
|
+
/** Pulse seats other buyers take, as the controller's own socket does. */
|
|
349
|
+
flashOnLiveChange?: boolean;
|
|
350
|
+
/** Selected-but-unheld units that stopped being selectable. */
|
|
351
|
+
onSelectedObjectUnavailable?: (labels: string[], reason: 'ineligible' | 'taken') => void;
|
|
352
|
+
/** Section availability changed and the chart itself needs rebuilding. */
|
|
353
|
+
onSections?: (hidden: string[], closed: string[]) => void;
|
|
354
|
+
onStatusChange?: () => void;
|
|
355
|
+
}
|
|
356
|
+
declare function createControllerSink(controller: PickerControllerLike, options?: ControllerSinkOptions): RealtimeSink;
|
|
357
|
+
|
|
188
358
|
/**
|
|
189
359
|
* Minimal client for the public embed surface of workers/api (the `/pub/*`
|
|
190
360
|
* routes). Deliberately self-contained — it does NOT reuse src/lib/api.ts,
|
|
@@ -217,7 +387,15 @@ declare class ApiError extends Error {
|
|
|
217
387
|
conflicts?: HoldConflict[];
|
|
218
388
|
/** Present when best-available 409s ('not_enough_together' | 'sold_out'). */
|
|
219
389
|
reason?: string;
|
|
220
|
-
|
|
390
|
+
/**
|
|
391
|
+
* Seconds the server asked the caller to wait, off a 429's `Retry-After`.
|
|
392
|
+
*
|
|
393
|
+
* Present ONLY on a rate-limit error, and it is the server's number — never a
|
|
394
|
+
* guess. A widget that catches this can say "try again in N seconds" instead
|
|
395
|
+
* of rendering the blank map a swallowed 429 used to produce.
|
|
396
|
+
*/
|
|
397
|
+
retryAfterS?: number;
|
|
398
|
+
constructor(status: number, message: string, code?: string, conflicts?: HoldConflict[], reason?: string, retryAfterS?: number);
|
|
221
399
|
}
|
|
222
400
|
interface HoldResult {
|
|
223
401
|
holdId: string;
|
|
@@ -425,6 +603,21 @@ interface SeatingChartOptions {
|
|
|
425
603
|
*/
|
|
426
604
|
onSelectedObjectUnavailable?: (event: SelectedObjectUnavailableEvent) => void;
|
|
427
605
|
onError?: (err: unknown) => void;
|
|
606
|
+
/**
|
|
607
|
+
* What the BUYER sees when the chart cannot load.
|
|
608
|
+
*
|
|
609
|
+
* `'message'` (the default) renders a plain, styleable notice with a Try
|
|
610
|
+
* again button. This used to be silent unconditionally: `render()` returned
|
|
611
|
+
* with an EMPTY mounted div and only `onError` fired, so a host that had not
|
|
612
|
+
* wired `onError` — or had wired it to a logger — showed buyers a blank
|
|
613
|
+
* rectangle where the seat map belongs, on the host's own domain, which
|
|
614
|
+
* reads as a broken website rather than a temporary fault. `SeatPicker` has
|
|
615
|
+
* always failed loud with a retry; this is the embed class catching up.
|
|
616
|
+
*
|
|
617
|
+
* `'none'` restores the silent behaviour for hosts that render their own
|
|
618
|
+
* failure UI from `onError`.
|
|
619
|
+
*/
|
|
620
|
+
errorDisplay?: 'message' | 'none';
|
|
428
621
|
/**
|
|
429
622
|
* Multi-floor charts only: fires when the buyer taps a deck in the stacked
|
|
430
623
|
* 3D view, after the picker switches to that floor — lets the host page sync
|
|
@@ -446,6 +639,7 @@ declare class SeatingChart {
|
|
|
446
639
|
private mount;
|
|
447
640
|
private hostEl;
|
|
448
641
|
private rendered;
|
|
642
|
+
private retryBtn;
|
|
449
643
|
private mode_;
|
|
450
644
|
private tipEl;
|
|
451
645
|
private tipPos;
|
|
@@ -590,165 +784,15 @@ declare class SeatingChart {
|
|
|
590
784
|
* true when a fresh bearer is held; the realtime feed restarts with it.
|
|
591
785
|
*/
|
|
592
786
|
refreshAccess(): Promise<boolean>;
|
|
593
|
-
destroy(): void;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
/**
|
|
597
|
-
* BuyerRealtimeClient — the client half of `docs/realtime-protocol-2026-08-01.md`.
|
|
598
|
-
*
|
|
599
|
-
* Why this exists as a separate socket rather than inside PickerController:
|
|
600
|
-
* a private scope authenticates with a one-use **subscribe ticket** carried in
|
|
601
|
-
* `Sec-WebSocket-Protocol`, and a browser can only set that at construction —
|
|
602
|
-
* `new WebSocket(url, protocols)`. The controller's socket is built from a URL
|
|
603
|
-
* alone (`PickerTransport.socketUrl`), and a bearer must never travel in a URL
|
|
604
|
-
* because URLs are routinely logged. So an access-scoped picker asks the
|
|
605
|
-
* transport for an empty `socketUrl()` (the controller then skips its own
|
|
606
|
-
* connection entirely) and this client owns the wire instead.
|
|
607
|
-
*
|
|
608
|
-
* A tokenless public picker never reaches this file. It keeps the controller's
|
|
609
|
-
* original socket, offers no subprotocol, and therefore receives byte-for-byte
|
|
610
|
-
* the frames it received before this module existed (protocol doc §1).
|
|
611
|
-
*
|
|
612
|
-
* What it implements:
|
|
613
|
-
* - protocol negotiation: offer `seatlayer.v1`, believe the 101 echo, and fall
|
|
614
|
-
* back to legacy frame handling when the server (or a proxy) does not echo;
|
|
615
|
-
* - the ticket exchange, one mint per connection attempt, ticket in the
|
|
616
|
-
* subprotocol list and never in the URL;
|
|
617
|
-
* - compact `{default, exceptions}` snapshot reconstruction;
|
|
618
|
-
* - `sv.<n>` resume, handling BOTH outcomes (a `resumed` delta or a full
|
|
619
|
-
* snapshot) on every reconnect;
|
|
620
|
-
* - close code 4401 as a typed access-revoked state, never a reconnect loop;
|
|
621
|
-
* - liveness by ping/pong only. Silence is normal and carries no information
|
|
622
|
-
* (protocol doc §5) — a quiet socket is never treated as a dead one.
|
|
623
|
-
*/
|
|
624
|
-
|
|
625
|
-
/** The projected status of one unit, as the server words it on the wire. */
|
|
626
|
-
type WireStatus = string;
|
|
627
|
-
/** A scope's projection: one default plus the units that differ from it. */
|
|
628
|
-
interface Projection {
|
|
629
|
-
default: WireStatus;
|
|
630
|
-
exceptions: Record<string, WireStatus>;
|
|
631
|
-
}
|
|
632
|
-
interface StatusChange {
|
|
633
|
-
label: string;
|
|
634
|
-
status: WireStatus;
|
|
635
|
-
}
|
|
636
|
-
/** Where reconstructed inventory goes. Implemented over PickerController. */
|
|
637
|
-
interface RealtimeSink {
|
|
638
|
-
/** Apply a batch of label→status changes. Implementations must paint this as
|
|
639
|
-
* ONE pass, not one pass per label (motion system §4 rule 2). */
|
|
640
|
-
applyStatuses(changes: StatusChange[]): void;
|
|
641
|
-
/** Re-pull authoritative state over the scoped HTTP route. Used when a frame
|
|
642
|
-
* cannot be diffed against what we hold (first snapshot, or the scope's
|
|
643
|
-
* default itself changed, which redefines every unit we were never told
|
|
644
|
-
* about). */
|
|
645
|
-
resync(): void | Promise<void>;
|
|
646
|
-
/** Section availability changed (channel-agnostic; identical for every scope). */
|
|
647
|
-
onSections?(hidden: string[], closed: string[]): void;
|
|
648
|
-
/** Scope-projected presence counters. */
|
|
649
|
-
onPresence?(counts: {
|
|
650
|
-
shoppingSessions: number;
|
|
651
|
-
activeHolds: number;
|
|
652
|
-
}): void;
|
|
653
|
-
}
|
|
654
|
-
interface SubscribeTicket$1 {
|
|
655
|
-
ticket?: string;
|
|
656
|
-
/** Exactly what to hand `new WebSocket(url, protocols)`, per protocol doc §3. */
|
|
657
|
-
protocols?: string[];
|
|
658
|
-
}
|
|
659
|
-
interface BuyerRealtimeOptions {
|
|
660
|
-
/** The subscribe URL. Must never carry a credential — asserted below. */
|
|
661
|
-
url: string;
|
|
662
|
-
sink: RealtimeSink;
|
|
663
|
-
/** Mint a one-use ticket for THIS connection attempt. Returns null for the
|
|
664
|
-
* anonymous public case (no ticket needed). Throwing stops the client. */
|
|
665
|
-
mintTicket?: () => Promise<SubscribeTicket$1 | null>;
|
|
666
|
-
/** Typed access states. 4401 arrives here as `revoked`. */
|
|
667
|
-
onAccessUnavailable?: (event: BuyerAccessUnavailableEvent) => void;
|
|
668
|
-
/** Test seam. Defaults to the global WebSocket. */
|
|
669
|
-
socketFactory?: (url: string, protocols: string[]) => WebSocket;
|
|
670
|
-
/** Test seam for the keepalive/backoff timers. */
|
|
671
|
-
now?: () => number;
|
|
672
|
-
}
|
|
673
|
-
declare class BuyerRealtimeClient {
|
|
674
|
-
private readonly opts;
|
|
675
|
-
private ws;
|
|
676
|
-
private stopped;
|
|
677
|
-
private attempt;
|
|
678
|
-
private reconnectTimer;
|
|
679
|
-
private pingTimer;
|
|
680
|
-
private pongTimer;
|
|
681
|
-
private resumeTimer;
|
|
682
|
-
/** Our model of this scope's projection. Null until the first snapshot. */
|
|
683
|
-
private projection;
|
|
684
|
-
/** Last `snapshotVersion` seen on any frame that carried one — the resume point. */
|
|
685
|
-
private version;
|
|
686
|
-
/** True once the 101 echoed `seatlayer.v1`. */
|
|
687
|
-
private v1;
|
|
688
|
-
/** Set when we offered v1 and the handshake came back without it — a proxy
|
|
689
|
-
* most likely stripped the header, so the next attempt selects the v1 frame
|
|
690
|
-
* format with the `?pv=1` marker instead (protocol doc §1). The marker
|
|
691
|
-
* selects a format and can never carry a credential or widen a scope. */
|
|
692
|
-
private useQueryMarker;
|
|
693
|
-
private hidden;
|
|
694
|
-
private closedSections;
|
|
695
|
-
constructor(options: BuyerRealtimeOptions);
|
|
696
|
-
/** Negotiated protocol, for tests and diagnostics. */
|
|
697
|
-
get protocol(): 'v1' | 'legacy' | null;
|
|
698
|
-
get snapshotVersion(): number | null;
|
|
699
|
-
start(): void;
|
|
700
|
-
/** Stop for good (destroy, or a revocation). Safe to call twice. */
|
|
701
|
-
stop(): void;
|
|
702
|
-
/** Restart after the host re-authorized a revoked buyer (`refreshAccess()`). */
|
|
703
|
-
restart(): void;
|
|
704
|
-
private connect;
|
|
705
|
-
private handleFrame;
|
|
706
|
-
/** The server answered our resume; cancel the fallback resync. */
|
|
707
|
-
private answered;
|
|
708
|
-
private reportIfAccessError;
|
|
709
787
|
/**
|
|
710
|
-
*
|
|
711
|
-
*
|
|
712
|
-
*
|
|
788
|
+
* The visible failure state. Deliberately inline-styled and dependency-free:
|
|
789
|
+
* this renders on a stranger's website, where our stylesheet may not have
|
|
790
|
+
* loaded (the chart fetch just failed) and where inheriting the host's own
|
|
791
|
+
* styles is likelier to produce something unreadable than something on-brand.
|
|
713
792
|
*/
|
|
714
|
-
private
|
|
715
|
-
|
|
716
|
-
private clearPongTimer;
|
|
717
|
-
private clearTimers;
|
|
718
|
-
}
|
|
719
|
-
/**
|
|
720
|
-
* The slice of PickerController this module drives. Structural on purpose — it
|
|
721
|
-
* keeps this file free of an `@seatlayer/core` import, and every member here is
|
|
722
|
-
* public controller API, so nothing in the engine mirror has to change.
|
|
723
|
-
*/
|
|
724
|
-
interface PickerControllerLike {
|
|
725
|
-
idForLabel(label: string): string | undefined;
|
|
726
|
-
tableSelection(seatIdOrLabel: string): {
|
|
727
|
-
physicalSeatIds: string[];
|
|
728
|
-
} | null;
|
|
729
|
-
setStatus(ids: string[], status: 'free' | 'held' | 'booked' | 'not_for_sale'): void;
|
|
730
|
-
getStatus(id: string): string | undefined;
|
|
731
|
-
flashSeat(id: string, color?: string): void;
|
|
732
|
-
currentHold(): {
|
|
733
|
-
labels: string[];
|
|
734
|
-
} | null;
|
|
735
|
-
getSelection(): Array<{
|
|
736
|
-
id: string;
|
|
737
|
-
label: string;
|
|
738
|
-
}>;
|
|
739
|
-
deselect(ids: string[]): void;
|
|
740
|
-
refresh(): Promise<void>;
|
|
741
|
-
}
|
|
742
|
-
interface ControllerSinkOptions {
|
|
743
|
-
/** Pulse seats other buyers take, as the controller's own socket does. */
|
|
744
|
-
flashOnLiveChange?: boolean;
|
|
745
|
-
/** Selected-but-unheld units that stopped being selectable. */
|
|
746
|
-
onSelectedObjectUnavailable?: (labels: string[], reason: 'ineligible' | 'taken') => void;
|
|
747
|
-
/** Section availability changed and the chart itself needs rebuilding. */
|
|
748
|
-
onSections?: (hidden: string[], closed: string[]) => void;
|
|
749
|
-
onStatusChange?: () => void;
|
|
793
|
+
private showLoadFailure;
|
|
794
|
+
destroy(): void;
|
|
750
795
|
}
|
|
751
|
-
declare function createControllerSink(controller: PickerControllerLike, options?: ControllerSinkOptions): RealtimeSink;
|
|
752
796
|
|
|
753
797
|
/**
|
|
754
798
|
* A secure, framework-neutral host for the SeatLayer chart Designer.
|
|
@@ -1253,17 +1297,33 @@ interface SeatPickerOptions {
|
|
|
1253
1297
|
* 1. It needs the widget's own transport. A host-supplied `transport` owns its
|
|
1254
1298
|
* credentials and its backend, so hosted checkout stays off there (with one
|
|
1255
1299
|
* console warning) rather than reaching past it to api.seatlayer.io.
|
|
1256
|
-
* 2. WHERE A HOSTED GATEWAY RETURNS THE BUYER
|
|
1257
|
-
*
|
|
1258
|
-
*
|
|
1259
|
-
*
|
|
1260
|
-
*
|
|
1261
|
-
*
|
|
1262
|
-
*
|
|
1263
|
-
* accepts a caller-supplied return URL, treat a card payment from a
|
|
1264
|
-
* third-party embed as "the buyer finishes on our page".
|
|
1300
|
+
* 2. WHERE A HOSTED GATEWAY RETURNS THE BUYER is settled by {@link returnUrl}
|
|
1301
|
+
* and by the organizer. Without one — or from an origin the organizer has
|
|
1302
|
+
* not declared — the buyer comes back to SeatLayer's own buyer page and is
|
|
1303
|
+
* confirmed THERE, not in this widget. Declare the embedding site under
|
|
1304
|
+
* Embed domains in the dashboard and pass `returnUrl`, and the buyer
|
|
1305
|
+
* returns to your page instead. In-page gateways never navigate away at
|
|
1306
|
+
* all, so they are unaffected either way.
|
|
1265
1307
|
*/
|
|
1266
1308
|
checkout?: 'handoff' | 'hosted';
|
|
1309
|
+
/**
|
|
1310
|
+
* Where a redirecting gateway should send the buyer back to, for
|
|
1311
|
+
* `checkout: 'hosted'`.
|
|
1312
|
+
*
|
|
1313
|
+
* The server keeps this URL verbatim — path and query included — and only
|
|
1314
|
+
* stamps `?order=…&status=success|cancelled` onto it, so point it at
|
|
1315
|
+
* whichever of YOUR pages should confirm the purchase (often just
|
|
1316
|
+
* `window.location.href`). Mount a picker on that page and it resumes in
|
|
1317
|
+
* place from those parameters.
|
|
1318
|
+
*
|
|
1319
|
+
* It is validated, not trusted: the organizer declares their embed origins
|
|
1320
|
+
* in the dashboard, and an undeclared origin is ignored rather than
|
|
1321
|
+
* refused — the sale still completes, the buyer just finishes on
|
|
1322
|
+
* SeatLayer's page. Supplying a URL therefore cannot authorize it, which is
|
|
1323
|
+
* what stops a copied snippet from redirecting a paid buyer anywhere it
|
|
1324
|
+
* likes.
|
|
1325
|
+
*/
|
|
1326
|
+
returnUrl?: string;
|
|
1267
1327
|
/**
|
|
1268
1328
|
* Buyer pressed the CTA and the hold succeeded — hand off to YOUR checkout.
|
|
1269
1329
|
* `hold` and `seats` are the legacy args (unchanged since 0.6). `handoff` (P4)
|