@seatlayer/js 0.36.0 → 0.36.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.cjs +123 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +98 -8
- package/dist/index.d.ts +98 -8
- package/dist/index.js +121 -28
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -59,6 +59,8 @@ __export(index_exports, {
|
|
|
59
59
|
createBuyerAccessContext: () => createBuyerAccessContext,
|
|
60
60
|
createControllerSink: () => createControllerSink,
|
|
61
61
|
dropReviewRows: () => dropReviewRows,
|
|
62
|
+
isPublicChannelId: () => isPublicChannelId,
|
|
63
|
+
markerLetter: () => markerLetter,
|
|
62
64
|
markerOf: () => markerOf,
|
|
63
65
|
mutationCount: () => mutationCount,
|
|
64
66
|
needsMoveConfirmation: () => needsMoveConfirmation,
|
|
@@ -5989,8 +5991,11 @@ function attachPickerFrame(iframe, opts = {}) {
|
|
|
5989
5991
|
var import_core3 = require("@seatlayer/core");
|
|
5990
5992
|
|
|
5991
5993
|
// src/channelPlan.ts
|
|
5992
|
-
var PUBLIC_CHANNEL_ID = "";
|
|
5994
|
+
var PUBLIC_CHANNEL_ID = "public";
|
|
5993
5995
|
var PUBLIC_CHANNEL_NAME = "Public sale";
|
|
5996
|
+
function isPublicChannelId(id) {
|
|
5997
|
+
return id == null || id === "" || id === PUBLIC_CHANNEL_ID;
|
|
5998
|
+
}
|
|
5994
5999
|
var CHANNEL_COLORS = [
|
|
5995
6000
|
"#a78bfa",
|
|
5996
6001
|
"#2dd4bf",
|
|
@@ -6005,27 +6010,35 @@ var CHANNEL_COLORS = [
|
|
|
6005
6010
|
];
|
|
6006
6011
|
var PUBLIC_CHANNEL_COLOR = "#f4b740";
|
|
6007
6012
|
var LETTERS = "ABCDEFGHJKLMNPQRSTUVWXYZ";
|
|
6013
|
+
function markerLetter(raw, fallback) {
|
|
6014
|
+
const text = (raw ?? "").trim();
|
|
6015
|
+
const letter = /\p{L}/u.exec(text)?.[0] ?? text[0] ?? "";
|
|
6016
|
+
return (letter || fallback).toUpperCase().slice(0, 1);
|
|
6017
|
+
}
|
|
6008
6018
|
function suggestMarker(name, taken) {
|
|
6009
|
-
const used = new Set([...taken].map((m) => m
|
|
6010
|
-
const first = (name
|
|
6019
|
+
const used = new Set([...taken].map((m) => markerLetter(m, "")).filter(Boolean));
|
|
6020
|
+
const first = markerLetter(name, "");
|
|
6011
6021
|
const letter = LETTERS.includes(first) && !used.has(first) ? first : [...LETTERS].find((candidate) => !used.has(candidate)) ?? (first || "X");
|
|
6012
6022
|
return { letter, color: CHANNEL_COLORS[used.size % CHANNEL_COLORS.length] };
|
|
6013
6023
|
}
|
|
6014
6024
|
function markerOf(channel, index = 0) {
|
|
6015
|
-
if (channel.id
|
|
6016
|
-
return {
|
|
6025
|
+
if (isPublicChannelId(channel.id)) {
|
|
6026
|
+
return {
|
|
6027
|
+
letter: markerLetter(channel.marker, "P"),
|
|
6028
|
+
color: channel.color || PUBLIC_CHANNEL_COLOR
|
|
6029
|
+
};
|
|
6017
6030
|
}
|
|
6018
|
-
const letter = (channel.marker || channel.name
|
|
6031
|
+
const letter = markerLetter(channel.marker || channel.name, "?");
|
|
6019
6032
|
return { letter, color: channel.color || CHANNEL_COLORS[index % CHANNEL_COLORS.length] };
|
|
6020
6033
|
}
|
|
6021
6034
|
function selectionSources(labels, allocation, list) {
|
|
6022
6035
|
const counts = /* @__PURE__ */ new Map();
|
|
6023
6036
|
for (const label of labels) {
|
|
6024
|
-
const channelId = allocation.get(label)
|
|
6037
|
+
const channelId = normalizeChannelId(allocation.get(label));
|
|
6025
6038
|
counts.set(channelId, (counts.get(channelId) ?? 0) + 1);
|
|
6026
6039
|
}
|
|
6027
6040
|
const order = [
|
|
6028
|
-
{ id: PUBLIC_CHANNEL_ID, name: list?.publicSale
|
|
6041
|
+
{ id: PUBLIC_CHANNEL_ID, name: list?.publicSale?.name ?? PUBLIC_CHANNEL_NAME },
|
|
6029
6042
|
...(list?.channels ?? []).map((channel) => ({ id: channel.id, name: channel.name }))
|
|
6030
6043
|
];
|
|
6031
6044
|
const rows = [];
|
|
@@ -6035,10 +6048,17 @@ function selectionSources(labels, allocation, list) {
|
|
|
6035
6048
|
counts.delete(entry.id);
|
|
6036
6049
|
}
|
|
6037
6050
|
for (const [channelId, count] of counts) {
|
|
6038
|
-
rows.push({
|
|
6051
|
+
rows.push({
|
|
6052
|
+
channelId,
|
|
6053
|
+
name: isPublicChannelId(channelId) ? PUBLIC_CHANNEL_NAME : "Another channel",
|
|
6054
|
+
count
|
|
6055
|
+
});
|
|
6039
6056
|
}
|
|
6040
6057
|
return rows;
|
|
6041
6058
|
}
|
|
6059
|
+
function normalizeChannelId(id) {
|
|
6060
|
+
return isPublicChannelId(id) ? PUBLIC_CHANNEL_ID : id;
|
|
6061
|
+
}
|
|
6042
6062
|
var SKIP_SAMPLE = 12;
|
|
6043
6063
|
function skipBucket(labels) {
|
|
6044
6064
|
return {
|
|
@@ -6048,7 +6068,8 @@ function skipBucket(labels) {
|
|
|
6048
6068
|
};
|
|
6049
6069
|
}
|
|
6050
6070
|
function planAssignment(input) {
|
|
6051
|
-
const { labels,
|
|
6071
|
+
const { labels, allocation, statusOf, nameOf } = input;
|
|
6072
|
+
const targetChannelId = normalizeChannelId(input.targetChannelId);
|
|
6052
6073
|
const seen = /* @__PURE__ */ new Set();
|
|
6053
6074
|
let fromPublic = 0;
|
|
6054
6075
|
let alreadyIn = 0;
|
|
@@ -6064,7 +6085,7 @@ function planAssignment(input) {
|
|
|
6064
6085
|
missing.push(label);
|
|
6065
6086
|
continue;
|
|
6066
6087
|
}
|
|
6067
|
-
const current = allocation.get(label)
|
|
6088
|
+
const current = normalizeChannelId(allocation.get(label));
|
|
6068
6089
|
if (current === targetChannelId) {
|
|
6069
6090
|
alreadyIn += 1;
|
|
6070
6091
|
continue;
|
|
@@ -6247,12 +6268,38 @@ var ManageApi = class {
|
|
|
6247
6268
|
pub(path) {
|
|
6248
6269
|
return fetch(`${this.base}${path}`, { credentials: "omit" }).then((r) => parse(r));
|
|
6249
6270
|
}
|
|
6250
|
-
// ---- realtime read
|
|
6271
|
+
// ---- realtime read ----
|
|
6272
|
+
/** The chart geometry. Genuinely public — it is the same map buyers see. */
|
|
6251
6273
|
chart(key) {
|
|
6252
6274
|
return this.pub(`/pub/events/${encodeURIComponent(key)}/chart`);
|
|
6253
6275
|
}
|
|
6276
|
+
/**
|
|
6277
|
+
* The ORGANIZER's seat map: physical state, token-authed.
|
|
6278
|
+
*
|
|
6279
|
+
* This used to read `/pub/events/:key/objects` with no credential, which
|
|
6280
|
+
* answers with the BUYER projection — every unit the caller may not buy
|
|
6281
|
+
* collapses to a neutral `blocked`. An anonymous caller may buy only Public
|
|
6282
|
+
* sale inventory, so the cockpit rendered every channel-allocated seat as
|
|
6283
|
+
* blocked and then computed its KPIs, sell-through and (worse) its
|
|
6284
|
+
* block/unblock target sets from that. `/v1/events/:key/objects` returns the
|
|
6285
|
+
* unprojected snapshot the control-room read model already trusts.
|
|
6286
|
+
*/
|
|
6254
6287
|
objects(key) {
|
|
6255
|
-
return this.
|
|
6288
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/objects`);
|
|
6289
|
+
}
|
|
6290
|
+
/**
|
|
6291
|
+
* Exchange the manage token for a one-use organizer socket ticket.
|
|
6292
|
+
*
|
|
6293
|
+
* A browser `WebSocket` cannot send an Authorization header, so the socket's
|
|
6294
|
+
* scope is established here, over ordinary HTTPS. Without it the DO treats a
|
|
6295
|
+
* manager socket as an anonymous public buyer and projects its deltas — so a
|
|
6296
|
+
* hold inside a private allocation is structurally suppressed and the map
|
|
6297
|
+
* drifts away from the truth `objects()` just established.
|
|
6298
|
+
*
|
|
6299
|
+
* Tickets are single-redemption and expire in ~30s: mint one per connect.
|
|
6300
|
+
*/
|
|
6301
|
+
subscribeTicket(key) {
|
|
6302
|
+
return this.auth(`/v1/events/${encodeURIComponent(key)}/subscribe-tickets`, { method: "POST" });
|
|
6256
6303
|
}
|
|
6257
6304
|
socketUrl(key) {
|
|
6258
6305
|
return `${this.base.replace(/^http/, "ws")}/pub/events/${encodeURIComponent(key)}/subscribe?surface=manager`;
|
|
@@ -6749,7 +6796,7 @@ var ChannelsMode = class {
|
|
|
6749
6796
|
limit: 1e3
|
|
6750
6797
|
});
|
|
6751
6798
|
for (const row of res.allocations) {
|
|
6752
|
-
if (row.channelId
|
|
6799
|
+
if (!isPublicChannelId(row.channelId)) next.set(row.label, row.channelId);
|
|
6753
6800
|
}
|
|
6754
6801
|
this.assignmentVersion = res.assignmentVersion;
|
|
6755
6802
|
if (!res.nextAfterLabel) break;
|
|
@@ -6759,8 +6806,13 @@ var ChannelsMode = class {
|
|
|
6759
6806
|
}
|
|
6760
6807
|
// ---- lookups --------------------------------------------------------------
|
|
6761
6808
|
channelById(id) {
|
|
6762
|
-
if (id
|
|
6763
|
-
return {
|
|
6809
|
+
if (isPublicChannelId(id)) {
|
|
6810
|
+
return {
|
|
6811
|
+
id: PUBLIC_CHANNEL_ID,
|
|
6812
|
+
name: this.list?.publicSale?.name ?? PUBLIC_CHANNEL_NAME,
|
|
6813
|
+
marker: "P",
|
|
6814
|
+
color: null
|
|
6815
|
+
};
|
|
6764
6816
|
}
|
|
6765
6817
|
const found = this.list?.channels.find((channel) => channel.id === id);
|
|
6766
6818
|
return found ? { id, name: found.name, marker: found.marker, color: found.color } : null;
|
|
@@ -6981,7 +7033,10 @@ var ChannelsMode = class {
|
|
|
6981
7033
|
this.setBanner(true, names);
|
|
6982
7034
|
try {
|
|
6983
7035
|
this.previewProjection = await this.host.api.channelPreview(this.host.eventKey, audience, {
|
|
6984
|
-
|
|
7036
|
+
// Naming Public sale as the audience IS asking for public inventory; the
|
|
7037
|
+
// route filters the 'public' sentinel out of `channelIds`, so without
|
|
7038
|
+
// this the request would resolve to an empty scope and 422.
|
|
7039
|
+
includePublic: this.previewIncludePublic || audience.some(isPublicChannelId)
|
|
6985
7040
|
});
|
|
6986
7041
|
this.previewSupported = true;
|
|
6987
7042
|
} catch (err) {
|
|
@@ -7182,7 +7237,7 @@ var ChannelsMode = class {
|
|
|
7182
7237
|
const counts = this.previewProjection?.counts;
|
|
7183
7238
|
const summary = counts?.eligible != null && this.previewProjection?.available !== false ? `<div class="slm-ch-alert warn"><span>\u2139</span><span>${counts.eligible.toLocaleString()} seats are buyable
|
|
7184
7239
|
through this access.${this.previewProjection?.includePublic === false ? " Public sale seats are <b>not</b> included in this grant." : ""}</span></div>` : "";
|
|
7185
|
-
const includePublic = current
|
|
7240
|
+
const includePublic = isPublicChannelId(current) ? "" : `
|
|
7186
7241
|
<label class="slm-note" style="display:flex;gap:8px;align-items:center;margin:10px 0">
|
|
7187
7242
|
<input type="checkbox" data-ch-includepublic ${this.previewIncludePublic ? "checked" : ""} />
|
|
7188
7243
|
Also include Public sale seats in this grant
|
|
@@ -7394,7 +7449,7 @@ var ChannelsMode = class {
|
|
|
7394
7449
|
this.lastFocus = null;
|
|
7395
7450
|
}
|
|
7396
7451
|
renderCreateDialog(state) {
|
|
7397
|
-
const taken = (this.list?.channels ?? []).map((channel) => channel.marker
|
|
7452
|
+
const taken = (this.list?.channels ?? []).map((channel) => markerLetter(channel.marker || channel.name, ""));
|
|
7398
7453
|
const suggestion = suggestMarker("", taken);
|
|
7399
7454
|
this.renderScrim(`
|
|
7400
7455
|
<h3 id="slm-ch-dlg-title">Create channel</h3>
|
|
@@ -7452,7 +7507,8 @@ var ChannelsMode = class {
|
|
|
7452
7507
|
try {
|
|
7453
7508
|
const res = await this.host.api.createChannel(this.host.eventKey, {
|
|
7454
7509
|
name: trimmed,
|
|
7455
|
-
|
|
7510
|
+
// The column is free text; we only ever store what the chip can draw.
|
|
7511
|
+
marker: markerLetter(letter, "") || null,
|
|
7456
7512
|
color: color || null,
|
|
7457
7513
|
externalRef: externalRef.trim() || null
|
|
7458
7514
|
});
|
|
@@ -7521,7 +7577,9 @@ var ChannelsMode = class {
|
|
|
7521
7577
|
this.renderDialog();
|
|
7522
7578
|
try {
|
|
7523
7579
|
const result = await this.host.api.applyChannelAssignment(this.host.eventKey, {
|
|
7524
|
-
|
|
7580
|
+
// `null` is the wire spelling of "back to public sale" every worker
|
|
7581
|
+
// accepts, including ones that predate the 'public' sentinel.
|
|
7582
|
+
targetChannelId: isPublicChannelId(this.targetChannelId) ? null : this.targetChannelId,
|
|
7525
7583
|
labels,
|
|
7526
7584
|
assignmentVersion: this.assignmentVersion
|
|
7527
7585
|
});
|
|
@@ -8199,7 +8257,7 @@ var SeatManager = class {
|
|
|
8199
8257
|
if (controlRoom?.activity) this.seedFeed(controlRoom.activity);
|
|
8200
8258
|
else this.api.log(this.key, { limit: 24 }).then((page) => this.seedFeed(page.entries)).catch(() => {
|
|
8201
8259
|
});
|
|
8202
|
-
this.connect();
|
|
8260
|
+
void this.connect();
|
|
8203
8261
|
this.startFeedClock();
|
|
8204
8262
|
this.ready = true;
|
|
8205
8263
|
await this.resolveChannelCapabilities();
|
|
@@ -8623,11 +8681,33 @@ var SeatManager = class {
|
|
|
8623
8681
|
});
|
|
8624
8682
|
}
|
|
8625
8683
|
// ---- realtime -------------------------------------------------------------
|
|
8626
|
-
|
|
8684
|
+
/**
|
|
8685
|
+
* Open the cockpit's realtime socket AS THE ORGANIZER.
|
|
8686
|
+
*
|
|
8687
|
+
* The scope has to be established before the upgrade, because a browser
|
|
8688
|
+
* `WebSocket` cannot send an Authorization header: the manage token is traded
|
|
8689
|
+
* over HTTPS for a one-use ticket which rides in `Sec-WebSocket-Protocol`.
|
|
8690
|
+
* Without it the server treats this socket as an anonymous public buyer and
|
|
8691
|
+
* projects its deltas, so any change inside a private channel allocation is
|
|
8692
|
+
* structurally suppressed and the map silently drifts.
|
|
8693
|
+
*
|
|
8694
|
+
* If the mint fails (an expired token, a worker that predates the route) we
|
|
8695
|
+
* still connect unticketed rather than going dark — the public-sale stream is
|
|
8696
|
+
* worth having, and every `resnapshot()` re-establishes physical truth from
|
|
8697
|
+
* the authenticated HTTP read.
|
|
8698
|
+
*/
|
|
8699
|
+
async connect() {
|
|
8700
|
+
if (this.closed) return;
|
|
8701
|
+
let protocols;
|
|
8702
|
+
try {
|
|
8703
|
+
protocols = (await this.api.subscribeTicket(this.key)).protocols;
|
|
8704
|
+
} catch {
|
|
8705
|
+
protocols = void 0;
|
|
8706
|
+
}
|
|
8627
8707
|
if (this.closed) return;
|
|
8628
8708
|
let ws;
|
|
8629
8709
|
try {
|
|
8630
|
-
ws = new WebSocket(this.api.socketUrl(this.key));
|
|
8710
|
+
ws = protocols ? new WebSocket(this.api.socketUrl(this.key), protocols) : new WebSocket(this.api.socketUrl(this.key));
|
|
8631
8711
|
} catch {
|
|
8632
8712
|
this.scheduleReconnect();
|
|
8633
8713
|
return;
|
|
@@ -8657,7 +8737,7 @@ var SeatManager = class {
|
|
|
8657
8737
|
const delay = Math.min(1e3 * 2 ** Math.min(this.attempt++, 5), 15e3);
|
|
8658
8738
|
this.reconnectTimer = setTimeout(() => {
|
|
8659
8739
|
this.reconnectTimer = null;
|
|
8660
|
-
this.connect();
|
|
8740
|
+
void this.connect();
|
|
8661
8741
|
}, delay);
|
|
8662
8742
|
}
|
|
8663
8743
|
onMessage(e) {
|
|
@@ -8687,7 +8767,7 @@ var SeatManager = class {
|
|
|
8687
8767
|
}
|
|
8688
8768
|
if (m.type === "hidden") return;
|
|
8689
8769
|
if (m.seats && typeof m.seats === "object") {
|
|
8690
|
-
this.applySnapshot(m.seats);
|
|
8770
|
+
this.applySnapshot(m.seats, typeof m.default === "string" ? m.default : void 0);
|
|
8691
8771
|
} else if (Array.isArray(m.changes)) {
|
|
8692
8772
|
const ids = [];
|
|
8693
8773
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -8727,10 +8807,23 @@ var SeatManager = class {
|
|
|
8727
8807
|
} catch {
|
|
8728
8808
|
}
|
|
8729
8809
|
}
|
|
8730
|
-
|
|
8810
|
+
/**
|
|
8811
|
+
* Replace the whole seat model.
|
|
8812
|
+
*
|
|
8813
|
+
* `fallback` is the compact frame's modal status: those snapshots list only
|
|
8814
|
+
* the seats that DIFFER from it, so every other known label takes it. Without
|
|
8815
|
+
* this the omitted majority would silently fall back to `free` — fine when
|
|
8816
|
+
* the mode really is free, wrong the moment it is not.
|
|
8817
|
+
*/
|
|
8818
|
+
applySnapshot(seats, fallback) {
|
|
8819
|
+
const known = (st) => ["free", "held", "booked", "blocked"].includes(st) ? st : "free";
|
|
8731
8820
|
const next = /* @__PURE__ */ new Map();
|
|
8821
|
+
if (fallback !== void 0) {
|
|
8822
|
+
const base = known(fallback);
|
|
8823
|
+
for (const label of this.labelToId.keys()) next.set(label, base);
|
|
8824
|
+
}
|
|
8732
8825
|
for (const [label, st] of Object.entries(seats)) {
|
|
8733
|
-
next.set(label,
|
|
8826
|
+
next.set(label, known(st));
|
|
8734
8827
|
}
|
|
8735
8828
|
this.status = next;
|
|
8736
8829
|
this.lastSyncedAt = Date.now();
|
|
@@ -9951,6 +10044,8 @@ var SeatManager = class {
|
|
|
9951
10044
|
createBuyerAccessContext,
|
|
9952
10045
|
createControllerSink,
|
|
9953
10046
|
dropReviewRows,
|
|
10047
|
+
isPublicChannelId,
|
|
10048
|
+
markerLetter,
|
|
9954
10049
|
markerOf,
|
|
9955
10050
|
mutationCount,
|
|
9956
10051
|
needsMoveConfirmation,
|