@seatlayer/js 0.48.1 → 0.49.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/README.md +16 -2
- package/dist/{channelsMode-NCH5GYHN.js → channelsMode-S4GEWCWS.js} +2 -3
- package/dist/{chunk-KNMZQZXR.js → chunk-5URCWBHM.js} +490 -5
- package/dist/chunk-5URCWBHM.js.map +1 -0
- package/dist/{hostedCheckout-F6C6VCCG.js → hostedCheckout-GVS6UKYA.js} +3 -1
- package/dist/hostedCheckout-GVS6UKYA.js.map +1 -0
- package/dist/index.cjs +5155 -10827
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -679
- package/dist/index.d.ts +80 -679
- package/dist/index.js +755 -100
- package/dist/index.js.map +1 -1
- package/dist/manager.cjs +140 -21
- package/dist/manager.cjs.map +1 -1
- package/dist/manager.d.cts +2275 -1
- package/dist/manager.d.ts +2275 -1
- package/dist/manager.js +2771 -6
- package/dist/manager.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-H4OJF6LE.js +0 -492
- package/dist/chunk-H4OJF6LE.js.map +0 -1
- package/dist/chunk-KNMZQZXR.js.map +0 -1
- package/dist/chunk-Q5QT3YLA.js +0 -2670
- package/dist/chunk-Q5QT3YLA.js.map +0 -1
- package/dist/hostedCheckout-F6C6VCCG.js.map +0 -1
- package/dist/manager-DEjbKiqJ.d.cts +0 -1602
- package/dist/manager-DEjbKiqJ.d.ts +0 -1602
- /package/dist/{channelsMode-NCH5GYHN.js.map → channelsMode-S4GEWCWS.js.map} +0 -0
package/README.md
CHANGED
|
@@ -57,7 +57,9 @@ Svelte → `onMount` / `onDestroy`. Angular → `ngAfterViewInit` / `ngOnDestroy
|
|
|
57
57
|
`SeatManager` gives first-party dashboards and external platforms the same
|
|
58
58
|
realtime organizer cockpit. Mint a short-lived, event-scoped `mse_` token from
|
|
59
59
|
your backend, bound to the browser's exact origin and only the capabilities it
|
|
60
|
-
needs.
|
|
60
|
+
needs. `SeatManager.token` accepts only that browser grant; passing a tenant
|
|
61
|
+
`sk_` secret to browser code is unsupported. Keep the grant in memory—never in
|
|
62
|
+
a URL, browser storage, or logs. The shared surface owns Monitor, Inspect, Block/unblock, fullscreen,
|
|
61
63
|
presence, exact configured booked value, booking velocity, and a clearly explained
|
|
62
64
|
**Booking momentum** overlay.
|
|
63
65
|
Block mode exposes explicit multi-select category state and a searchable,
|
|
@@ -65,7 +67,7 @@ section-filtered blocked-inventory list, so an organizer can put specific seats
|
|
|
65
67
|
back on sale without resetting the entire event.
|
|
66
68
|
|
|
67
69
|
```js
|
|
68
|
-
import { SeatManager } from '@seatlayer/js';
|
|
70
|
+
import { SeatManager } from '@seatlayer/js/manager';
|
|
69
71
|
|
|
70
72
|
const manager = new SeatManager({
|
|
71
73
|
container: '#control-room',
|
|
@@ -78,6 +80,16 @@ const manager = new SeatManager({
|
|
|
78
80
|
await manager.render();
|
|
79
81
|
```
|
|
80
82
|
|
|
83
|
+
The package root is the buyer runtime entry. Organizer JavaScript values—
|
|
84
|
+
`SeatManager`, `ChannelsMode`, channel planning helpers, and `ManageApi`—come
|
|
85
|
+
from `@seatlayer/js/manager`, keeping buyer imports free of control-room code.
|
|
86
|
+
Type-only organizer imports from the root remain supported for TypeScript
|
|
87
|
+
source compatibility, but new code should use the manager subpath consistently.
|
|
88
|
+
|
|
89
|
+
`SeatManager` does not expose a browser-side box-office booking method. Create
|
|
90
|
+
managed sales from trusted server code through the inventory booking API; the
|
|
91
|
+
cockpit's public methods are limited to operations it can actually complete.
|
|
92
|
+
|
|
81
93
|
Switching tools and rotating tokens happen in place: the renderer, camera,
|
|
82
94
|
selection, WebSocket, and DOM are not remounted. Give the container a height;
|
|
83
95
|
the cockpit responds to its container rather than the browser viewport.
|
|
@@ -95,6 +107,8 @@ const detail = await inventory.retrieveBooking('ev_9f3a', page.bookings[0].booki
|
|
|
95
107
|
|
|
96
108
|
These records contain labels, lifecycle activity and configured-value snapshots.
|
|
97
109
|
They do not contain buyer, payment, ticket, email or refund data.
|
|
110
|
+
`ManageApi` retains `sk_` compatibility only for trusted server runtimes; browser
|
|
111
|
+
callers must use an event-scoped `mse_` grant.
|
|
98
112
|
|
|
99
113
|
## Embed the chart Designer
|
|
100
114
|
|
|
@@ -2,12 +2,11 @@ import {
|
|
|
2
2
|
CHANNELS_CSS,
|
|
3
3
|
ChannelsMode,
|
|
4
4
|
bucketRowsHtml
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-H4OJF6LE.js";
|
|
5
|
+
} from "./chunk-5URCWBHM.js";
|
|
7
6
|
import "./chunk-HMLY7DHA.js";
|
|
8
7
|
export {
|
|
9
8
|
CHANNELS_CSS,
|
|
10
9
|
ChannelsMode,
|
|
11
10
|
bucketRowsHtml
|
|
12
11
|
};
|
|
13
|
-
//# sourceMappingURL=channelsMode-
|
|
12
|
+
//# sourceMappingURL=channelsMode-S4GEWCWS.js.map
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ManageApiError
|
|
3
|
-
} from "./chunk-H4OJF6LE.js";
|
|
4
|
-
|
|
5
1
|
// src/channelPlan.ts
|
|
6
2
|
var PUBLIC_CHANNEL_ID = "public";
|
|
7
3
|
var PUBLIC_CHANNEL_NAME = "Public sale";
|
|
@@ -355,6 +351,493 @@ function stateBadge(state) {
|
|
|
355
351
|
return state === "builtin" ? "Built-in" : state === "active" ? "Active" : state === "paused" ? "Paused" : "Archived";
|
|
356
352
|
}
|
|
357
353
|
|
|
354
|
+
// src/manageApi.ts
|
|
355
|
+
var ManageApiError = class extends Error {
|
|
356
|
+
constructor(status, message, code, conflicts, details, serverMessage) {
|
|
357
|
+
super(message);
|
|
358
|
+
this.name = "ManageApiError";
|
|
359
|
+
this.status = status;
|
|
360
|
+
this.code = code;
|
|
361
|
+
this.conflicts = conflicts;
|
|
362
|
+
this.details = details;
|
|
363
|
+
this.serverMessage = serverMessage;
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
async function parse(res) {
|
|
367
|
+
const isJson = (res.headers.get("content-type") ?? "").includes("application/json");
|
|
368
|
+
const data = isJson ? await res.json().catch(() => null) : null;
|
|
369
|
+
if (!res.ok) {
|
|
370
|
+
const err = data;
|
|
371
|
+
throw new ManageApiError(
|
|
372
|
+
res.status,
|
|
373
|
+
err?.error ?? `request_failed_${res.status}`,
|
|
374
|
+
err?.code,
|
|
375
|
+
err?.conflicts,
|
|
376
|
+
err?.details,
|
|
377
|
+
typeof err?.message === "string" ? err.message : void 0
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
return data;
|
|
381
|
+
}
|
|
382
|
+
function record(value) {
|
|
383
|
+
return value && typeof value === "object" ? value : {};
|
|
384
|
+
}
|
|
385
|
+
function finite(primary, legacy, fallback = 0) {
|
|
386
|
+
if (typeof primary === "number" && Number.isFinite(primary)) return primary;
|
|
387
|
+
if (typeof legacy === "number" && Number.isFinite(legacy)) return legacy;
|
|
388
|
+
return fallback;
|
|
389
|
+
}
|
|
390
|
+
function nullableFinite(primary, legacy) {
|
|
391
|
+
if (primary === null) return null;
|
|
392
|
+
if (typeof primary === "number" && Number.isFinite(primary)) return primary;
|
|
393
|
+
if (primary !== void 0) return null;
|
|
394
|
+
if (typeof legacy === "number" && Number.isFinite(legacy)) return legacy;
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
function normalizeSection(value) {
|
|
398
|
+
const row = record(value);
|
|
399
|
+
const bookedValue = finite(row.bookedValue, row.bookedRevenue);
|
|
400
|
+
return { ...row, bookedValue, bookedRevenue: bookedValue };
|
|
401
|
+
}
|
|
402
|
+
function normalizeReportResult(value) {
|
|
403
|
+
const source = record(value);
|
|
404
|
+
const report = record(source.report);
|
|
405
|
+
const byCategory = Array.isArray(report.byCategory) ? report.byCategory.map((value2) => {
|
|
406
|
+
const row = record(value2);
|
|
407
|
+
const bookedValue = finite(row.bookedValue, row.bookedRevenue);
|
|
408
|
+
return { ...row, bookedValue, bookedRevenue: bookedValue };
|
|
409
|
+
}) : [];
|
|
410
|
+
const bySection = Array.isArray(report.bySection) ? report.bySection.map(normalizeSection) : void 0;
|
|
411
|
+
return {
|
|
412
|
+
...source,
|
|
413
|
+
report: { ...report, byCategory, ...bySection ? { bySection } : {} }
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
function normalizeControlRoomSnapshot(value) {
|
|
417
|
+
const source = record(value);
|
|
418
|
+
const canonical = record(source.bookedValue);
|
|
419
|
+
const legacy = record(source.revenue);
|
|
420
|
+
const selected = Object.keys(canonical).length ? canonical : legacy;
|
|
421
|
+
const bySectionSource = Array.isArray(canonical.bySection) ? canonical.bySection : Array.isArray(legacy.bySection) ? legacy.bySection : [];
|
|
422
|
+
const bookedValue = {
|
|
423
|
+
...selected,
|
|
424
|
+
gross: finite(canonical.gross, legacy.gross),
|
|
425
|
+
bySection: bySectionSource.map(normalizeSection)
|
|
426
|
+
};
|
|
427
|
+
const velocity = record(source.velocity);
|
|
428
|
+
const velocityRows = Array.isArray(velocity.bySection) ? velocity.bySection.map((value2) => {
|
|
429
|
+
const row = record(value2);
|
|
430
|
+
const rowValue = finite(row.bookedValue, row.grossRevenue);
|
|
431
|
+
return { ...row, bookedValue: rowValue, grossRevenue: rowValue };
|
|
432
|
+
}) : [];
|
|
433
|
+
return {
|
|
434
|
+
...source,
|
|
435
|
+
bookedValue,
|
|
436
|
+
revenue: bookedValue,
|
|
437
|
+
velocity: { ...velocity, bySection: velocityRows }
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
function normalizeChannelReportResult(value) {
|
|
441
|
+
const source = record(value);
|
|
442
|
+
const report = record(source.report);
|
|
443
|
+
const includesBookedValue = typeof report.includesBookedValue === "boolean" ? report.includesBookedValue : report.includesRevenue === true;
|
|
444
|
+
const rows = Array.isArray(report.rows) ? report.rows.map((value2) => {
|
|
445
|
+
const row = record(value2);
|
|
446
|
+
const attribution = record(row.attribution);
|
|
447
|
+
const bookedValue = nullableFinite(attribution.bookedValue, attribution.revenue);
|
|
448
|
+
return {
|
|
449
|
+
...row,
|
|
450
|
+
attribution: { ...attribution, bookedValue, revenue: bookedValue }
|
|
451
|
+
};
|
|
452
|
+
}) : [];
|
|
453
|
+
const totals = record(report.totals);
|
|
454
|
+
const totalBookedValue = nullableFinite(totals.bookedValue, totals.revenue);
|
|
455
|
+
return {
|
|
456
|
+
...source,
|
|
457
|
+
report: {
|
|
458
|
+
...report,
|
|
459
|
+
includesBookedValue,
|
|
460
|
+
includesRevenue: includesBookedValue,
|
|
461
|
+
rows,
|
|
462
|
+
totals: { ...totals, bookedValue: totalBookedValue, revenue: totalBookedValue }
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
function normalizeChannelReportLink(value) {
|
|
467
|
+
const link = record(value);
|
|
468
|
+
const includesBookedValue = typeof link.includesBookedValue === "boolean" ? link.includesBookedValue : link.includesRevenue === true;
|
|
469
|
+
return {
|
|
470
|
+
...link,
|
|
471
|
+
includesBookedValue,
|
|
472
|
+
includesRevenue: includesBookedValue
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
var ManageApi = class {
|
|
476
|
+
constructor(apiBase, token) {
|
|
477
|
+
this.base = apiBase.replace(/\/+$/, "");
|
|
478
|
+
this.token = token;
|
|
479
|
+
}
|
|
480
|
+
/** Swap the Bearer token in place (SeatManager re-mints on 401). */
|
|
481
|
+
setToken(token) {
|
|
482
|
+
this.token = token;
|
|
483
|
+
}
|
|
484
|
+
auth(path, init = {}) {
|
|
485
|
+
const method = init.method ?? "GET";
|
|
486
|
+
const headers = { Authorization: `Bearer ${this.token}` };
|
|
487
|
+
let body;
|
|
488
|
+
if (init.body !== void 0) {
|
|
489
|
+
headers["Content-Type"] = "application/json";
|
|
490
|
+
body = JSON.stringify(init.body);
|
|
491
|
+
}
|
|
492
|
+
return fetch(`${this.base}${path}`, { method, headers, body, credentials: "omit" }).then((r) => parse(r));
|
|
493
|
+
}
|
|
494
|
+
async authBlob(path) {
|
|
495
|
+
const res = await fetch(`${this.base}${path}`, {
|
|
496
|
+
method: "GET",
|
|
497
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
498
|
+
credentials: "omit"
|
|
499
|
+
});
|
|
500
|
+
if (!res.ok) await parse(res);
|
|
501
|
+
return res.blob();
|
|
502
|
+
}
|
|
503
|
+
// ---- realtime read ----
|
|
504
|
+
/** Event-pinned organizer geometry. A manage token is never sent to `/pub`. */
|
|
505
|
+
chart(key) {
|
|
506
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/chart`);
|
|
507
|
+
}
|
|
508
|
+
/** Authenticated bytes for an Event-scoped organizer chart asset. */
|
|
509
|
+
asset(key, asset) {
|
|
510
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(asset)) {
|
|
511
|
+
return Promise.reject(new ManageApiError(404, "not_found", "not_found"));
|
|
512
|
+
}
|
|
513
|
+
return this.authBlob(
|
|
514
|
+
`/v1/events/${encodeURIComponent(key)}/assets/${encodeURIComponent(asset)}`
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* The ORGANIZER's seat map: physical state, token-authed.
|
|
519
|
+
*
|
|
520
|
+
* This used to read `/pub/events/:key/objects` with no credential, which
|
|
521
|
+
* answers with the BUYER projection — every unit the caller may not buy
|
|
522
|
+
* collapses to a neutral `blocked`. An anonymous caller may buy only Public
|
|
523
|
+
* sale inventory, so the cockpit rendered every channel-allocated seat as
|
|
524
|
+
* blocked and then computed its KPIs, sell-through and (worse) its
|
|
525
|
+
* block/unblock target sets from that. `/v1/events/:key/objects` returns the
|
|
526
|
+
* unprojected snapshot the control-room read model already trusts.
|
|
527
|
+
*/
|
|
528
|
+
objects(key) {
|
|
529
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/objects`);
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Exchange the manage token for a one-use organizer socket ticket.
|
|
533
|
+
*
|
|
534
|
+
* A browser `WebSocket` cannot send an Authorization header, so the socket's
|
|
535
|
+
* scope is established here, over ordinary HTTPS. Without it the DO treats a
|
|
536
|
+
* manager socket as an anonymous public buyer and projects its deltas — so a
|
|
537
|
+
* hold inside a private allocation is structurally suppressed and the map
|
|
538
|
+
* drifts away from the truth `objects()` just established.
|
|
539
|
+
*
|
|
540
|
+
* Tickets are single-redemption and expire in ~30s: mint one per connect.
|
|
541
|
+
*/
|
|
542
|
+
subscribeTicket(key) {
|
|
543
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/subscribe-tickets`, { method: "POST" });
|
|
544
|
+
}
|
|
545
|
+
socketUrl(key) {
|
|
546
|
+
return `${this.base.replace(/^http/, "ws")}/pub/events/${encodeURIComponent(key)}/subscribe?surface=manager`;
|
|
547
|
+
}
|
|
548
|
+
// ---- inventory writes (token) ----
|
|
549
|
+
/** Take FREE seats off sale in one batched call. Optional `releaseAt` (epoch
|
|
550
|
+
* ms, future) auto-returns them to sale; `reason` tags the block (M3 uses it).
|
|
551
|
+
* Throws ManageApiError 409 (conflicts) if any seat was just taken. */
|
|
552
|
+
block(key, labels, opts = {}) {
|
|
553
|
+
const body = { labels };
|
|
554
|
+
if (typeof opts.releaseAt === "number") body.releaseAt = opts.releaseAt;
|
|
555
|
+
if (opts.reason) body.reason = opts.reason;
|
|
556
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/block`, { method: "POST", body });
|
|
557
|
+
}
|
|
558
|
+
/** Return specific blocked seats to sale (one batched call). */
|
|
559
|
+
unblock(key, labels) {
|
|
560
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/unblock`, { method: "POST", body: { labels } });
|
|
561
|
+
}
|
|
562
|
+
/** Return every blocked seat to sale; resolves with the freed count. */
|
|
563
|
+
unblockAll(key) {
|
|
564
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/unblock-all`, { method: "POST" });
|
|
565
|
+
}
|
|
566
|
+
/** Cancel bookings — return BOOKED seats to free (credit not refunded).
|
|
567
|
+
* Guarded by the original booking reference. */
|
|
568
|
+
unbook(key, labels, bookingRef) {
|
|
569
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/unbook`, { method: "POST", body: { labels, bookingRef } });
|
|
570
|
+
}
|
|
571
|
+
/** Set (ms, clamped 1–60 min server-side) or clear (null) the hold TTL. */
|
|
572
|
+
setHoldTtl(key, holdTtlMs) {
|
|
573
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/hold-ttl`, { method: "POST", body: { holdTtlMs } });
|
|
574
|
+
}
|
|
575
|
+
// ---- Platform inventory booking history (token) ----
|
|
576
|
+
/** Inventory lifecycle by stable integrator bookingRef. Absent on Managed. */
|
|
577
|
+
bookings(key, query = {}) {
|
|
578
|
+
const params = new URLSearchParams();
|
|
579
|
+
if (query.q) params.set("q", query.q);
|
|
580
|
+
if (query.state) params.set("state", query.state);
|
|
581
|
+
if (query.cursor) params.set("cursor", query.cursor);
|
|
582
|
+
if (query.limit != null) params.set("limit", String(query.limit));
|
|
583
|
+
const qs = params.toString();
|
|
584
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/bookings${qs ? `?${qs}` : ""}`);
|
|
585
|
+
}
|
|
586
|
+
/** Exact configured-value snapshot plus book/replay/cancellation audit. */
|
|
587
|
+
booking(key, bookingRef) {
|
|
588
|
+
return this.auth(
|
|
589
|
+
`/v1/events/${encodeURIComponent(key)}/bookings/${encodeURIComponent(bookingRef)}`
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
/** Alias matching the server SDK vocabulary. */
|
|
593
|
+
listBookings(key, query = {}) {
|
|
594
|
+
return this.bookings(key, query);
|
|
595
|
+
}
|
|
596
|
+
/** Alias matching the server SDK vocabulary. */
|
|
597
|
+
retrieveBooking(key, bookingRef) {
|
|
598
|
+
return this.booking(key, bookingRef);
|
|
599
|
+
}
|
|
600
|
+
// ---- availability windows (token) ----
|
|
601
|
+
/** The organizer's current per section/zone availability windows (needs
|
|
602
|
+
* `event:view`). Ids absent from `rules` are open / on sale. */
|
|
603
|
+
availability(key) {
|
|
604
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/availability`);
|
|
605
|
+
}
|
|
606
|
+
/** Replace the availability windows for a set of section/zone ids (needs
|
|
607
|
+
* `event:block`). Ids absent from `rules` become open / on sale; a zone rule
|
|
608
|
+
* cascades to its sections. The worker derives each id's seat labels, so
|
|
609
|
+
* `labels` on the sent rules is best-effort. Resolves with the authoritative
|
|
610
|
+
* effective `hidden` set (a due rule may fire at once) and the server-cleaned
|
|
611
|
+
* `rules` map (fired timed/threshold windows dropped). */
|
|
612
|
+
setAvailability(key, rules) {
|
|
613
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/availability`, { method: "POST", body: { rules } });
|
|
614
|
+
}
|
|
615
|
+
// ---- sales channels (token, capability-gated) ----
|
|
616
|
+
// Reads need `event:channels:view`, mutations `event:channels:manage`.
|
|
617
|
+
// `event:block` grants NEITHER (spec §10), so a Block-only cockpit token gets
|
|
618
|
+
// a 403 here and Channels mode never renders.
|
|
619
|
+
/** Allocation list with exact per-channel counts. `includeArchived` adds the
|
|
620
|
+
* read-only archived rows behind the rail's "Show archived" control. */
|
|
621
|
+
channels(key, opts = {}) {
|
|
622
|
+
const qs = opts.includeArchived ? "?includeArchived=1" : "";
|
|
623
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/channels${qs}`);
|
|
624
|
+
}
|
|
625
|
+
/** One page of the label → channel map that paints the allocation overlay.
|
|
626
|
+
* Paged by label; follow `nextAfterLabel` until it is null. */
|
|
627
|
+
channelAllocation(key, opts = {}) {
|
|
628
|
+
const params = new URLSearchParams();
|
|
629
|
+
if (opts.afterLabel) params.set("afterLabel", opts.afterLabel);
|
|
630
|
+
if (opts.limit != null) params.set("limit", String(opts.limit));
|
|
631
|
+
const qs = params.toString();
|
|
632
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/channels/allocation${qs ? `?${qs}` : ""}`);
|
|
633
|
+
}
|
|
634
|
+
channelAudit(key, opts = {}) {
|
|
635
|
+
const params = new URLSearchParams();
|
|
636
|
+
if (opts.limit != null) params.set("limit", String(opts.limit));
|
|
637
|
+
if (opts.before != null) params.set("before", String(opts.before));
|
|
638
|
+
const qs = params.toString();
|
|
639
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/channels/audit${qs ? `?${qs}` : ""}`);
|
|
640
|
+
}
|
|
641
|
+
createChannel(key, input) {
|
|
642
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/channels`, { method: "POST", body: input });
|
|
643
|
+
}
|
|
644
|
+
renameChannel(key, channelId, name) {
|
|
645
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/channels/${encodeURIComponent(channelId)}`, {
|
|
646
|
+
method: "PATCH",
|
|
647
|
+
body: { name }
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
setChannelPaused(key, channelId, paused) {
|
|
651
|
+
const path = paused ? "pause" : "unpause";
|
|
652
|
+
return this.auth(
|
|
653
|
+
`/v1/events/${encodeURIComponent(key)}/channels/${encodeURIComponent(channelId)}/${path}`,
|
|
654
|
+
{ method: "POST", body: {} }
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
/** Archive with a mandatory destination for the remaining allocation.
|
|
658
|
+
* Throws ManageApiError 409 `channel_archive_blocked_by_holds` while any hold
|
|
659
|
+
* is live; `err.details` carries the exact counts + retry window. */
|
|
660
|
+
archiveChannel(key, channelId, destination) {
|
|
661
|
+
return this.auth(
|
|
662
|
+
`/v1/events/${encodeURIComponent(key)}/channels/${encodeURIComponent(channelId)}/archive`,
|
|
663
|
+
{ method: "POST", body: { destination } }
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Versioned Apply. A stale `assignmentVersion` mutates NOTHING and throws
|
|
668
|
+
* ManageApiError 409 `channel_assignment_conflict` — the caller keeps its
|
|
669
|
+
* selection and offers "Refresh and review". There is no dry-run: the review
|
|
670
|
+
* sheet previews locally, this call returns the authoritative buckets.
|
|
671
|
+
*/
|
|
672
|
+
applyChannelAssignment(key, input) {
|
|
673
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/channels/assignments`, {
|
|
674
|
+
method: "POST",
|
|
675
|
+
body: {
|
|
676
|
+
targetChannelId: input.targetChannelId || null,
|
|
677
|
+
labels: input.labels,
|
|
678
|
+
assignmentVersion: input.assignmentVersion
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Read-only buyer projection for an audience (§8.6) — the SAME scoped server
|
|
684
|
+
* view the buyer SDK receives, never a local approximation.
|
|
685
|
+
*
|
|
686
|
+
* Ships on the access-hardening branch. Older workers 404/405 here; callers
|
|
687
|
+
* MUST feature-detect and quietly say the preview needs a newer server rather
|
|
688
|
+
* than faking a projection client-side.
|
|
689
|
+
*/
|
|
690
|
+
channelPreview(key, channelIds, opts = {}) {
|
|
691
|
+
const params = new URLSearchParams();
|
|
692
|
+
if (channelIds.length) params.set("channelIds", channelIds.join(","));
|
|
693
|
+
if (opts.includePublic != null) params.set("includePublic", opts.includePublic ? "1" : "0");
|
|
694
|
+
const qs = params.toString();
|
|
695
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/channels/preview${qs ? `?${qs}` : ""}`);
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Choose which sale route this channel opens.
|
|
699
|
+
*
|
|
700
|
+
* Since the server's 2026-08-06 change this is AUTHORIZATION, not a label:
|
|
701
|
+
* exactly one of the four routes may mint buyer access for the channel and the
|
|
702
|
+
* other three refuse with 409 `channel_access_intent_forbids`. The default is
|
|
703
|
+
* `none`, which refuses all four — so a route has to be declared before any
|
|
704
|
+
* buyer-facing action on the channel can succeed.
|
|
705
|
+
*
|
|
706
|
+
* Switching the route while buyers are already inside the current one is
|
|
707
|
+
* refused with 409 `channel_intent_switch_blocked`, whose `details` name what
|
|
708
|
+
* is live (`liveAccessLinks`, `activeSessions`). Retry with
|
|
709
|
+
* `acknowledgeLiveAccess: true`: hosted links on the channel are revoked,
|
|
710
|
+
* while sessions already minted keep their holds and drain on their own.
|
|
711
|
+
* `intentSwitch` is present on the response ONLY when the switch disturbed
|
|
712
|
+
* something, so the ordinary case stays the two-key body it has always been.
|
|
713
|
+
*/
|
|
714
|
+
setChannelAccessIntent(key, channelId, accessIntent, opts = {}) {
|
|
715
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/channels/${encodeURIComponent(channelId)}`, {
|
|
716
|
+
method: "PATCH",
|
|
717
|
+
body: {
|
|
718
|
+
accessIntent,
|
|
719
|
+
...opts.acknowledgeLiveAccess ? { acknowledgeLiveAccess: true } : {},
|
|
720
|
+
...opts.reason ? { reason: opts.reason } : {}
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
// ---- hosted access links (M8) ----
|
|
725
|
+
/**
|
|
726
|
+
* Mint a hosted access link. The 201 is the ONE and ONLY time `url` and
|
|
727
|
+
* `capability` exist outside the buyer's browser — SeatLayer keeps a hash, so
|
|
728
|
+
* there is no route, cache, or support escalation that can produce this string
|
|
729
|
+
* again. Callers must reveal it immediately and then let it go.
|
|
730
|
+
*
|
|
731
|
+
* Every omitted field takes the server's default: expiry = when the event
|
|
732
|
+
* starts, 100 redemptions, 4 seats per buyer, this channel's allocation only.
|
|
733
|
+
* Platform bounds are enforced server-side and reported as 422 with the rule
|
|
734
|
+
* spelled out in `ManageApiError.serverMessage`.
|
|
735
|
+
*
|
|
736
|
+
* NOT a side effect any more. This used to SET the channel's access intent to
|
|
737
|
+
* `hosted_link`; since 2026-08-06 it REQUIRES it, and a channel declaring any
|
|
738
|
+
* other route refuses with 409 `channel_access_intent_forbids`. Callers must
|
|
739
|
+
* declare the route first — `ChannelsMode` does exactly that before it
|
|
740
|
+
* creates, so a first buyer link on a fresh channel is still one gesture.
|
|
741
|
+
*/
|
|
742
|
+
createAccessLink(key, channelId, input = {}) {
|
|
743
|
+
return this.auth(
|
|
744
|
+
`/v1/events/${encodeURIComponent(key)}/channels/${encodeURIComponent(channelId)}/access-links`,
|
|
745
|
+
{ method: "POST", body: input }
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
/** Status only — label, expiry, redemptions, per-buyer cap, lineage, and the
|
|
749
|
+
* live session count. Never the url, never the capability. Needs `:view`. */
|
|
750
|
+
accessLinks(key, channelId) {
|
|
751
|
+
return this.auth(
|
|
752
|
+
`/v1/events/${encodeURIComponent(key)}/channels/${encodeURIComponent(channelId)}/access-links`
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Rotate — the ONLY recovery for a link nobody kept. The old URL stops opening
|
|
757
|
+
* immediately and the response is a fresh one-time reveal.
|
|
758
|
+
*
|
|
759
|
+
* `endActiveSessions` is REQUIRED, not defaulted: the organizer must say
|
|
760
|
+
* whether buyers already inside finish their checkout or lose access now. The
|
|
761
|
+
* server answers 422 `end_active_sessions_required` if it is omitted, and that
|
|
762
|
+
* refusal is correct — a UI must not pick either branch on their behalf.
|
|
763
|
+
*/
|
|
764
|
+
rotateAccessLink(key, channelId, linkId, endActiveSessions) {
|
|
765
|
+
return this.auth(
|
|
766
|
+
`/v1/events/${encodeURIComponent(key)}/channels/${encodeURIComponent(channelId)}/access-links/${encodeURIComponent(linkId)}/rotate`,
|
|
767
|
+
{ method: "POST", body: { endActiveSessions } }
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
/** Revoke. The link stops opening immediately; `endActiveSessions` decides
|
|
771
|
+
* whether the buyers already inside keep their sessions. */
|
|
772
|
+
revokeAccessLink(key, channelId, linkId, endActiveSessions = false) {
|
|
773
|
+
const qs = endActiveSessions ? "?endActiveSessions=1" : "";
|
|
774
|
+
return this.auth(
|
|
775
|
+
`/v1/events/${encodeURIComponent(key)}/channels/${encodeURIComponent(channelId)}/access-links/${encodeURIComponent(linkId)}${qs}`,
|
|
776
|
+
{ method: "DELETE" }
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
// ---- reports (token) ----
|
|
780
|
+
report(key) {
|
|
781
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/report`).then(normalizeReportResult);
|
|
782
|
+
}
|
|
783
|
+
controlRoom(key, windowMinutes = 15) {
|
|
784
|
+
return this.auth(
|
|
785
|
+
`/v1/events/${encodeURIComponent(key)}/control-room?window=${windowMinutes}`
|
|
786
|
+
).then(normalizeControlRoomSnapshot);
|
|
787
|
+
}
|
|
788
|
+
/** Allocation beside immutable booking-time channel attribution. */
|
|
789
|
+
channelReport(key) {
|
|
790
|
+
return this.auth(
|
|
791
|
+
`/v1/events/${encodeURIComponent(key)}/channels/report`
|
|
792
|
+
).then(normalizeChannelReportResult);
|
|
793
|
+
}
|
|
794
|
+
createChannelReportLink(key, channelId, input = {}) {
|
|
795
|
+
return this.auth(
|
|
796
|
+
`/v1/events/${encodeURIComponent(key)}/channels/${encodeURIComponent(channelId)}/report-links`,
|
|
797
|
+
{ method: "POST", body: input }
|
|
798
|
+
).then((value) => {
|
|
799
|
+
const reveal = record(value);
|
|
800
|
+
return { ...reveal, link: normalizeChannelReportLink(reveal.link) };
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
channelReportLinks(key, channelId) {
|
|
804
|
+
return this.auth(
|
|
805
|
+
`/v1/events/${encodeURIComponent(key)}/channels/${encodeURIComponent(channelId)}/report-links`
|
|
806
|
+
).then((value) => {
|
|
807
|
+
const result = record(value);
|
|
808
|
+
return {
|
|
809
|
+
links: Array.isArray(result.links) ? result.links.map(normalizeChannelReportLink) : []
|
|
810
|
+
};
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
revokeChannelReportLink(key, channelId, linkId) {
|
|
814
|
+
return this.auth(
|
|
815
|
+
`/v1/events/${encodeURIComponent(key)}/channels/${encodeURIComponent(channelId)}/report-links/${encodeURIComponent(linkId)}`,
|
|
816
|
+
{ method: "DELETE" }
|
|
817
|
+
).then((value) => {
|
|
818
|
+
const result = record(value);
|
|
819
|
+
return { ...result, link: normalizeChannelReportLink(result.link) };
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
log(key, opts = {}) {
|
|
823
|
+
const params = new URLSearchParams();
|
|
824
|
+
if (opts.limit != null) params.set("limit", String(opts.limit));
|
|
825
|
+
if (opts.before != null) params.set("before", String(opts.before));
|
|
826
|
+
const qs = params.toString();
|
|
827
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/log${qs ? `?${qs}` : ""}`);
|
|
828
|
+
}
|
|
829
|
+
/** CSV report as a Blob (Bearer auth can't ride a plain <a href>). Host builds
|
|
830
|
+
* an object URL for download. */
|
|
831
|
+
async reportCsv(key) {
|
|
832
|
+
const res = await fetch(`${this.base}/v1/events/${encodeURIComponent(key)}/report.csv`, {
|
|
833
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
834
|
+
credentials: "omit"
|
|
835
|
+
});
|
|
836
|
+
if (!res.ok) throw new ManageApiError(res.status, `request_failed_${res.status}`);
|
|
837
|
+
return res.blob();
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
|
|
358
841
|
// src/channelsMode.ts
|
|
359
842
|
var POLL_MS = 3e4;
|
|
360
843
|
var MAX_FLAGS = 8;
|
|
@@ -3167,6 +3650,8 @@ var ChannelsMode = class {
|
|
|
3167
3650
|
};
|
|
3168
3651
|
|
|
3169
3652
|
export {
|
|
3653
|
+
ManageApiError,
|
|
3654
|
+
ManageApi,
|
|
3170
3655
|
PUBLIC_CHANNEL_ID,
|
|
3171
3656
|
PUBLIC_CHANNEL_NAME,
|
|
3172
3657
|
isPublicChannelId,
|
|
@@ -3195,4 +3680,4 @@ export {
|
|
|
3195
3680
|
bucketRowsHtml,
|
|
3196
3681
|
ChannelsMode
|
|
3197
3682
|
};
|
|
3198
|
-
//# sourceMappingURL=chunk-
|
|
3683
|
+
//# sourceMappingURL=chunk-5URCWBHM.js.map
|