@seatlayer/js 0.48.2 → 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/dist/manager.cjs CHANGED
@@ -836,6 +836,16 @@ function accessLinkErrorCopy(err) {
836
836
  return fromServer || "That did not go through. Try again.";
837
837
  }
838
838
  }
839
+ function dropReviewRows(details) {
840
+ return (details?.channels ?? []).map((channel) => ({
841
+ kind: "skip",
842
+ icon: "\u26A0",
843
+ count: channel.count,
844
+ text: `${channel.count.toLocaleString()} would leave ${channel.name ?? "a channel"}`,
845
+ why: "the new chart no longer has these seats",
846
+ peek: channel.labels?.length ? peekOf({ count: channel.count, labels: channel.labels, truncated: channel.truncated ?? false }) : void 0
847
+ }));
848
+ }
839
849
  function stateBadge(state) {
840
850
  return state === "builtin" ? "Built-in" : state === "active" ? "Active" : state === "paused" ? "Paused" : "Archived";
841
851
  }
@@ -3693,9 +3703,35 @@ var init_channelsMode = __esm({
3693
3703
  // src/manager.ts
3694
3704
  var manager_exports = {};
3695
3705
  __export(manager_exports, {
3706
+ ACCESS_LINK_DEFAULTS: () => ACCESS_LINK_DEFAULTS,
3707
+ ChannelsMode: () => ChannelsMode,
3696
3708
  ManageApi: () => ManageApi,
3697
3709
  ManageApiError: () => ManageApiError,
3698
- SeatManager: () => SeatManager
3710
+ PUBLIC_CHANNEL_ID: () => PUBLIC_CHANNEL_ID,
3711
+ PUBLIC_CHANNEL_NAME: () => PUBLIC_CHANNEL_NAME,
3712
+ SeatManager: () => SeatManager,
3713
+ accessIntentDescription: () => accessIntentDescription,
3714
+ accessIntentLabel: () => accessIntentLabel,
3715
+ accessLine: () => accessLine,
3716
+ accessLinkBadge: () => accessLinkBadge,
3717
+ accessLinkErrorCopy: () => accessLinkErrorCopy,
3718
+ accessLinkIsLive: () => accessLinkIsLive,
3719
+ accessLinkPolicyLines: () => accessLinkPolicyLines,
3720
+ bucketRows: () => bucketRows,
3721
+ bucketRowsHtml: () => bucketRowsHtml,
3722
+ dropReviewRows: () => dropReviewRows,
3723
+ intentForbidsCopy: () => intentForbidsCopy,
3724
+ intentSwitchBlockedCopy: () => intentSwitchBlockedCopy,
3725
+ isPublicChannelId: () => isPublicChannelId,
3726
+ markerLetter: () => markerLetter,
3727
+ markerOf: () => markerOf,
3728
+ mutationCount: () => mutationCount,
3729
+ needsMoveConfirmation: () => needsMoveConfirmation,
3730
+ planAssignment: () => planAssignment,
3731
+ retryAfterCopy: () => retryAfterCopy,
3732
+ selectionSources: () => selectionSources,
3733
+ stateBadge: () => stateBadge,
3734
+ suggestMarker: () => suggestMarker
3699
3735
  });
3700
3736
  module.exports = __toCommonJS(manager_exports);
3701
3737
 
@@ -3836,6 +3872,13 @@ function resolveContainer(container) {
3836
3872
  }
3837
3873
  return container;
3838
3874
  }
3875
+ function assertBrowserManageToken(token) {
3876
+ if (!token.startsWith("mse_")) {
3877
+ throw new Error(
3878
+ "seatmanager: token must be a short-lived event-scoped mse_ grant minted by your backend; tenant secret keys are unsupported in browsers"
3879
+ );
3880
+ }
3881
+ }
3839
3882
  function toRenderStatus(s) {
3840
3883
  return s === "blocked" ? "not_for_sale" : s;
3841
3884
  }
@@ -4184,6 +4227,8 @@ var SeatManager = class {
4184
4227
  /** Bumped whenever the seat model is replaced wholesale (a full snapshot). */
4185
4228
  this.modelVersion = 0;
4186
4229
  this.currency = "USD";
4230
+ /** Currency read from the event/control-room projection; host currency is fallback only. */
4231
+ this.authoritativeCurrency = null;
4187
4232
  this.authoritativeGrossRevenue = 0;
4188
4233
  this.revenueStatus = "loading";
4189
4234
  this.revenueRequest = 0;
@@ -4260,6 +4305,8 @@ var SeatManager = class {
4260
4305
  */
4261
4306
  this.channels = null;
4262
4307
  this.channelCaps = { view: false, manage: false };
4308
+ /** Supersedes an async capability probe after token/capability rotation. */
4309
+ this.channelCapabilityResolution = 0;
4263
4310
  /** In-flight `import('./channelsMode')`, so concurrent entries load once. */
4264
4311
  this.channelsLoading = null;
4265
4312
  this.onFullscreenChange = () => {
@@ -4296,6 +4343,7 @@ var SeatManager = class {
4296
4343
  if (feedButton?.dataset.feedId) this.locateActivity(feedButton.dataset.feedId);
4297
4344
  };
4298
4345
  this.sectionOptions = [];
4346
+ assertBrowserManageToken(options.token);
4299
4347
  this.opts = options;
4300
4348
  this.key = options.eventKey;
4301
4349
  this.mode = options.mode ?? "view";
@@ -4317,7 +4365,8 @@ var SeatManager = class {
4317
4365
  try {
4318
4366
  const res = await this.withAuthRetry(() => this.api.chart(this.key));
4319
4367
  this.doc = await this.organizerAssetUrls.prepareRendererChart(res.doc);
4320
- this.currency = res.event.currency ?? this.opts.currency ?? this.currency;
4368
+ this.authoritativeCurrency = res.event.currency ?? null;
4369
+ this.currency = this.authoritativeCurrency ?? this.opts.currency ?? this.currency;
4321
4370
  this.buildUnitUniverse(this.doc);
4322
4371
  this.buildRenderer();
4323
4372
  this.buildSectionOptions();
@@ -4362,36 +4411,38 @@ var SeatManager = class {
4362
4411
  * Decide what this token may do with sales channels.
4363
4412
  *
4364
4413
  * Declared capabilities win — a host that mints an `mse_…` grant knows exactly
4365
- * what it asked for. Otherwise a tenant secret (`sk_…`) is org authority the
4366
- * worker never narrows, so it is fully capable; and a delegated token with no
4367
- * declaration is probed for read access and then treated as READ-ONLY, because
4368
- * "we could not tell" must never render mutation controls.
4414
+ * what it asked for. A delegated token with no declaration is probed for read
4415
+ * access and then treated as READ-ONLY, because "we could not tell" must never
4416
+ * render mutation controls.
4369
4417
  */
4370
4418
  async resolveChannelCapabilities() {
4419
+ const resolution = ++this.channelCapabilityResolution;
4371
4420
  const declared = this.opts.capabilities;
4421
+ let next;
4372
4422
  if (declared) {
4373
4423
  const set = new Set(declared);
4374
- this.channelCaps = {
4424
+ next = {
4375
4425
  view: set.has("event:channels:view"),
4376
4426
  manage: set.has("event:channels:view") && set.has("event:channels:manage")
4377
4427
  };
4378
- } else if (/^sk_/.test(this.opts.token)) {
4379
- this.channelCaps = { view: true, manage: true };
4380
4428
  } else {
4381
- this.channelCaps = { view: false, manage: false };
4429
+ next = { view: false, manage: false };
4382
4430
  }
4383
- if (!this.channelCaps.view && !declared && !/^sk_/.test(this.opts.token)) {
4431
+ if (!next.view && !declared) {
4384
4432
  try {
4385
4433
  await this.api.channels(this.key);
4386
- this.channelCaps = { view: true, manage: false };
4434
+ next = { view: true, manage: false };
4387
4435
  } catch {
4388
- this.channelCaps = { view: false, manage: false };
4436
+ next = { view: false, manage: false };
4389
4437
  }
4390
4438
  }
4439
+ if (resolution !== this.channelCapabilityResolution) return;
4440
+ this.channelCaps = next;
4391
4441
  if (!this.channelCaps.view) {
4392
4442
  this.channels?.destroy();
4393
4443
  this.channels = null;
4394
- this.paintModeTabs();
4444
+ if (this.mode === "channels") this.setMode("view");
4445
+ else this.paintModeTabs();
4395
4446
  return;
4396
4447
  }
4397
4448
  this.channels?.setCapabilities(this.channelCaps);
@@ -4550,6 +4601,36 @@ var SeatManager = class {
4550
4601
  this.paintFollowLiveButton();
4551
4602
  if (changed) this.opts.onFollowLiveChange?.(enabled);
4552
4603
  }
4604
+ /** Update background-tab repaint policy without rebuilding the board. */
4605
+ setKeepLiveWhileHidden(enabled) {
4606
+ this.keepLive = enabled ?? true;
4607
+ this.opts.keepLiveWhileHidden = enabled;
4608
+ }
4609
+ /** Update the host fallback currency; an event/control-room currency still wins. */
4610
+ setCurrency(currency) {
4611
+ this.opts.currency = currency;
4612
+ if (this.authoritativeCurrency) return;
4613
+ this.currency = currency ?? "USD";
4614
+ if (this.ready) this.recomputeTallies();
4615
+ }
4616
+ /** Apply organizer chrome tokens in place without losing camera or selection. */
4617
+ setTheme(theme) {
4618
+ this.opts.theme = theme;
4619
+ if (!this.root) return;
4620
+ for (const [property, value] of Object.entries(themeVars(theme))) {
4621
+ this.root.style.setProperty(property, value);
4622
+ }
4623
+ }
4624
+ /** Replace the declared authority for the current token and fail closed. */
4625
+ setCapabilities(capabilities) {
4626
+ this.opts.capabilities = capabilities;
4627
+ if (this.ready) void this.resolveChannelCapabilities();
4628
+ }
4629
+ /** Change proactive token-refresh policy without rebuilding the manager. */
4630
+ setTokenRefresh(onTokenRefresh) {
4631
+ this.opts.onTokenRefresh = onTokenRefresh;
4632
+ this.scheduleTokenRefresh();
4633
+ }
4553
4634
  /** Change the current-vs-previous sales window and refresh the private projection. */
4554
4635
  setTrendWindow(windowMinutes) {
4555
4636
  const normalized = Number.isFinite(windowMinutes) ? Math.floor(windowMinutes) : 15;
@@ -4575,7 +4656,10 @@ var SeatManager = class {
4575
4656
  }
4576
4657
  /** Rotate the delegated credential without rebuilding DOM, canvas or socket. */
4577
4658
  setToken(token, expiresAt) {
4659
+ assertBrowserManageToken(token);
4578
4660
  this.api.setToken(token);
4661
+ this.opts.token = token;
4662
+ this.opts.tokenExpiresAt = expiresAt;
4579
4663
  this.tokenExpiresAt = expiresAt ?? null;
4580
4664
  this.scheduleTokenRefresh();
4581
4665
  if (this.ready) void this.resolveChannelCapabilities();
@@ -4727,11 +4811,6 @@ var SeatManager = class {
4727
4811
  this.opts.onError?.(err);
4728
4812
  }
4729
4813
  }
4730
- /** M2 — box-office booking from free seats. Stubbed (route is session-only today). */
4731
- boxBook(_labels, _bookingRef) {
4732
- this.toastErr("Box office ships in a later milestone.");
4733
- return Promise.resolve();
4734
- }
4735
4814
  zoomToFit() {
4736
4815
  this.renderer?.clearSectionFocus();
4737
4816
  this.renderer?.zoomToFit();
@@ -5248,6 +5327,7 @@ var SeatManager = class {
5248
5327
  this.rebaseServerTotals(snapshot);
5249
5328
  this.lastSyncedAt = Date.now();
5250
5329
  this.authoritativeGrossRevenue = snapshot.bookedValue.gross;
5330
+ this.authoritativeCurrency = snapshot.currency;
5251
5331
  this.currency = snapshot.currency;
5252
5332
  this.revenueStatus = "current";
5253
5333
  this.recomputeTallies();
@@ -6366,11 +6446,39 @@ var SeatManager = class {
6366
6446
  };
6367
6447
 
6368
6448
  // src/manager.ts
6449
+ init_channelsMode();
6450
+ init_channelPlan();
6369
6451
  init_manageApi();
6370
6452
  // Annotate the CommonJS export names for ESM import in node:
6371
6453
  0 && (module.exports = {
6454
+ ACCESS_LINK_DEFAULTS,
6455
+ ChannelsMode,
6372
6456
  ManageApi,
6373
6457
  ManageApiError,
6374
- SeatManager
6458
+ PUBLIC_CHANNEL_ID,
6459
+ PUBLIC_CHANNEL_NAME,
6460
+ SeatManager,
6461
+ accessIntentDescription,
6462
+ accessIntentLabel,
6463
+ accessLine,
6464
+ accessLinkBadge,
6465
+ accessLinkErrorCopy,
6466
+ accessLinkIsLive,
6467
+ accessLinkPolicyLines,
6468
+ bucketRows,
6469
+ bucketRowsHtml,
6470
+ dropReviewRows,
6471
+ intentForbidsCopy,
6472
+ intentSwitchBlockedCopy,
6473
+ isPublicChannelId,
6474
+ markerLetter,
6475
+ markerOf,
6476
+ mutationCount,
6477
+ needsMoveConfirmation,
6478
+ planAssignment,
6479
+ retryAfterCopy,
6480
+ selectionSources,
6481
+ stateBadge,
6482
+ suggestMarker
6375
6483
  });
6376
6484
  //# sourceMappingURL=manager.cjs.map