@seatlayer/js 0.37.0 → 0.39.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.d.ts CHANGED
@@ -239,6 +239,52 @@ interface BestAvailableResult {
239
239
  items?: HoldResult['items'];
240
240
  zoneId?: string;
241
241
  }
242
+ /** A gateway the organizer can be connected to. */
243
+ type PaymentProviderName = 'stripe' | 'razorpay';
244
+ /**
245
+ * Why `payment-options` came back with an empty list.
246
+ *
247
+ * The three are NOT interchangeable and two of them give opposite advice:
248
+ * `not_configured` means the organizer takes payment somewhere else,
249
+ * `payments_off_for_event` means they deliberately do not sell THIS event
250
+ * online, and `unavailable_for_event` means they switched it on and it is
251
+ * broken. Collapsing them makes the widget blame a working integration for a
252
+ * decision that was made on purpose.
253
+ */
254
+ type PaymentOptionsReason = 'not_configured' | 'payments_off_for_event' | 'unavailable_for_event';
255
+ /**
256
+ * What this event can take money through. Since the per-event gateway column
257
+ * landed, `providers` holds AT MOST ONE entry — the gateway the organizer
258
+ * assigned — so no browser can choose which one charges.
259
+ *
260
+ * `reason` is optional on the wire: a widget pinned against an older worker
261
+ * still parses, and its absence means what that worker meant by an empty list.
262
+ */
263
+ interface PaymentOptionsResult {
264
+ providers: PaymentProviderName[];
265
+ currency: string | null;
266
+ reason?: PaymentOptionsReason | null;
267
+ }
268
+ /** A started payment. Exactly one of the two handoffs comes back. */
269
+ interface CheckoutSessionResult {
270
+ orderId: string;
271
+ totalMinor: number;
272
+ currency: string;
273
+ expiresAt: number;
274
+ /** Hosted gateway page — navigate to it. */
275
+ redirectUrl?: string;
276
+ /** In-page modal gateway — open it without leaving the page. */
277
+ clientPayload?: Record<string, unknown>;
278
+ }
279
+ /** An order's state while its gateway webhook is in flight. */
280
+ interface OrderStatusResult {
281
+ orderId: string;
282
+ status: string;
283
+ totalMinor: number;
284
+ currency: string;
285
+ amountFormatted: string;
286
+ seatCount: number;
287
+ }
242
288
  interface PubApiOptions {
243
289
  /**
244
290
  * Buyer access session. When present, EVERY scoped operation on this client
@@ -1183,13 +1229,82 @@ interface SeatPickerOptions {
1183
1229
  * photo when a seat carries one. Default true; set false to hide the affordance.
1184
1230
  */
1185
1231
  seatView?: boolean;
1232
+ /**
1233
+ * WHERE the buyer goes once their seats are held. Default `'handoff'`.
1234
+ *
1235
+ * 'handoff' (default, and every integration that has ever existed) the
1236
+ * widget fires {@link onCheckout} with a holdId and priced line
1237
+ * items, and YOUR server takes the money. Nothing about this path
1238
+ * changes, and no payment code is even downloaded.
1239
+ * 'hosted' the widget takes the money through the gateway the ORGANIZER
1240
+ * connected, on their account — the "sell tickets with no
1241
+ * backend" path. Requires the org to be on hosted checkout and
1242
+ * the event to have a gateway assigned; when it does not, this
1243
+ * falls back to `'handoff'` for that buyer rather than dead-ending
1244
+ * them, and reports why through {@link onCheckoutUnavailable}.
1245
+ *
1246
+ * Named for the destination rather than as a boolean flag because there is a
1247
+ * real third answer coming and `hostedCheckout: true` would have no room for
1248
+ * it; spelling the default out also makes a host's intent legible in their own
1249
+ * source instead of hiding it in an absent option.
1250
+ *
1251
+ * TWO THINGS ARE WORTH KNOWING BEFORE YOU SWITCH THIS ON:
1252
+ *
1253
+ * 1. It needs the widget's own transport. A host-supplied `transport` owns its
1254
+ * credentials and its backend, so hosted checkout stays off there (with one
1255
+ * console warning) rather than reaching past it to api.seatlayer.io.
1256
+ * 2. WHERE A HOSTED GATEWAY RETURNS THE BUYER IS THE SERVER'S CHOICE. The
1257
+ * checkout session's return URL is built from the deployment's own allowed
1258
+ * origins, so a buyer paying by card from an embed on your domain comes back
1259
+ * to SeatLayer's buyer page and is confirmed THERE, not in this widget. The
1260
+ * widget resumes in place only when it is mounted on a page that actually
1261
+ * receives `?order=…&status=success` (a page on an allowed origin, and the
1262
+ * in-page gateways, which never navigate away at all). Until the server
1263
+ * accepts a caller-supplied return URL, treat a card payment from a
1264
+ * third-party embed as "the buyer finishes on our page".
1265
+ */
1266
+ checkout?: 'handoff' | 'hosted';
1186
1267
  /**
1187
1268
  * Buyer pressed the CTA and the hold succeeded — hand off to YOUR checkout.
1188
1269
  * `hold` and `seats` are the legacy args (unchanged since 0.6). `handoff` (P4)
1189
1270
  * is the stable, self-contained {@link CheckoutHandoff} to build your order
1190
1271
  * against — holdId, expiry, currency and priced line items. Prefer it.
1272
+ *
1273
+ * Under `checkout: 'hosted'` this fires ONLY when hosted checkout cannot run
1274
+ * for this event, so a host can keep one code path for both. It never fires
1275
+ * alongside a payment the widget is taking itself.
1191
1276
  */
1192
1277
  onCheckout?: (hold: HoldResult, seats: PickerSeat[], handoff: CheckoutHandoff) => void;
1278
+ /**
1279
+ * `checkout: 'hosted'` was asked for and this event cannot take money.
1280
+ * The seats ARE held — the buyer is mid-journey — so this is a routing
1281
+ * decision, not an error, and it is never collapsed into {@link onError}.
1282
+ *
1283
+ * `reason` carries the server's three-way answer verbatim, because two of the
1284
+ * three give opposite advice: `payments_off_for_event` means the organizer
1285
+ * deliberately does not sell this event online (nothing is wrong), while
1286
+ * `unavailable_for_event` means they switched it on and it is broken. Anything
1287
+ * unreadable — a failed lookup, an older worker — reads as `not_configured`,
1288
+ * which asserts the least about them.
1289
+ *
1290
+ * `onCheckout` fires immediately after this with the same hold. Supply either
1291
+ * (or both) and you own the next screen; supply NEITHER and the widget shows
1292
+ * the buyer an honest card of its own rather than swallowing the press.
1293
+ */
1294
+ onCheckoutUnavailable?: (event: {
1295
+ reason: PaymentOptionsReason;
1296
+ handoff: CheckoutHandoff;
1297
+ }) => void;
1298
+ /**
1299
+ * `checkout: 'hosted'` only — the gateway's webhook landed and the order is
1300
+ * PAID. The one signal a host with no backend actually needs, and the only
1301
+ * place a receipt can come from on a page that has no server of its own.
1302
+ *
1303
+ * Distinct from {@link onBooked}, which reports the same sale seen from the
1304
+ * seat map over the realtime channel and cannot fire at all for a buyer whose
1305
+ * widget was torn down by a redirect to the gateway.
1306
+ */
1307
+ onOrderConfirmed?: (order: OrderStatusResult) => void;
1193
1308
  /**
1194
1309
  * The held seats were BOOKED (P4) — your server completed payment and the
1195
1310
  * booking landed over the realtime channel while the widget was still open.
@@ -1265,6 +1380,20 @@ declare class SeatPicker {
1265
1380
  private handedOff;
1266
1381
  /** Guards single onBooked + single success overlay per hold. */
1267
1382
  private bookedShown;
1383
+ /**
1384
+ * `'hosted'` only when the host asked for it AND the widget owns its own
1385
+ * transport. Resolved once in the constructor so every later read is a field
1386
+ * comparison rather than a re-derivation that could drift.
1387
+ */
1388
+ private readonly checkoutMode;
1389
+ /**
1390
+ * In-flight or settled `payment-options` for this event, started at render in
1391
+ * hosted mode. One request, kicked off while the buyer is still choosing, so
1392
+ * pressing Pay does not wait on a lookup whose answer never changes mid-session.
1393
+ */
1394
+ private paymentOptions;
1395
+ /** The mounted payment card, while one is up. */
1396
+ private checkoutPanel;
1268
1397
  private extendEl;
1269
1398
  private bookedEl;
1270
1399
  private gaQty;
@@ -1426,6 +1555,22 @@ declare class SeatPicker {
1426
1555
  static open(options: Omit<SeatPickerOptions, 'container'>): Promise<SeatPicker>;
1427
1556
  constructor(options: SeatPickerOptions);
1428
1557
  render(): Promise<this>;
1558
+ /**
1559
+ * A hosted gateway returned this buyer to a page that runs the widget, with
1560
+ * `?order=…&status=…` in the URL. Pick the order up and finish the story.
1561
+ *
1562
+ * Only `success` resumes. `cancelled` means the buyer backed out at the
1563
+ * gateway and their seats are still held — the map they are looking at IS the
1564
+ * right screen, and opening a card to say "you cancelled" would be noise.
1565
+ *
1566
+ * The two parameters are then stripped with `replaceState`, because they are a
1567
+ * one-shot instruction: leaving them in place would re-open the confirmation
1568
+ * on every later navigation, and would carry an order id into browser history
1569
+ * and any Referer this page later sends. `status` is only ever removed
1570
+ * alongside an `order` we actually consumed, so a host page that uses a
1571
+ * `status` parameter of its own keeps it.
1572
+ */
1573
+ private resumeHostedOrder;
1429
1574
  /**
1430
1575
  * Move layout-dependent chrome between its wide dock (map regions / zoom
1431
1576
  * column) and its narrow dock (the sheet's consolidated Filters row), and
@@ -1589,6 +1734,10 @@ declare class SeatPicker {
1589
1734
  * uploaded photo (seat.viewUrl) when present, else a panorama generated from
1590
1735
  * the chart geometry — the stage placed at this seat's true bearing + size.
1591
1736
  * Zero extra dependencies: an equirectangular image panned with `repeat-x`.
1737
+ *
1738
+ * Async only because the generator is a lazy chunk (see `loadPanorama`); an
1739
+ * organizer photo needs no generator and never waits on it. The two callers
1740
+ * are click handlers, so nothing observes the promise.
1592
1741
  */
1593
1742
  private openSeatView;
1594
1743
  private closeSeatView;
@@ -1637,6 +1786,38 @@ declare class SeatPicker {
1637
1786
  */
1638
1787
  private detectBooked;
1639
1788
  private showBooked;
1789
+ /**
1790
+ * The seats are held. Send the buyer wherever this picker's `checkout` option
1791
+ * says they go.
1792
+ *
1793
+ * The default branch is the literal call that stood here before hosted
1794
+ * checkout existed, unchanged, so nothing about an existing integration moves.
1795
+ */
1796
+ private checkoutHandoff;
1797
+ /**
1798
+ * Take the money ourselves, through the organizer's own gateway.
1799
+ *
1800
+ * Order of operations matters: ASK FIRST, load second. `payment-options` is
1801
+ * already in flight from render, and its answer decides whether any payment
1802
+ * code is fetched at all — an event that cannot charge never downloads the
1803
+ * card that would have charged it.
1804
+ *
1805
+ * An empty list is not a failure and never dead-ends the buyer. It routes them
1806
+ * to whatever the host has: `onCheckoutUnavailable` (with the server's reason,
1807
+ * so the host can say the right one of three very different sentences), then
1808
+ * `onCheckout` with the ordinary handoff. A host that supplied neither gets
1809
+ * the widget's own honest card instead of a press that did nothing.
1810
+ */
1811
+ private startHostedCheckout;
1812
+ /**
1813
+ * Fetch the checkout chunk and put its card over the map.
1814
+ *
1815
+ * Every failure here lands the buyer back on a map with their seats still
1816
+ * held, which is a place they can act from — a blocked chunk request must not
1817
+ * leave them staring at a CTA that no longer does anything.
1818
+ */
1819
+ private openCheckoutPanel;
1820
+ private closeCheckoutPanel;
1640
1821
  /** Assemble the stable {@link CheckoutHandoff} from a hold's server line items. */
1641
1822
  private buildHandoff;
1642
1823
  private emitHoldChange;
@@ -2765,13 +2946,39 @@ declare class SeatManager {
2765
2946
  private labelToId;
2766
2947
  private labelToSeat;
2767
2948
  private allIds;
2949
+ /**
2950
+ * GA inventory units — real sellable labels the server counts, with NO seat
2951
+ * geometry and therefore no renderer binding. They live here rather than in
2952
+ * `labelToId`/`allIds` so every paint path keeps addressing paintable nodes
2953
+ * only, while the tally denominator finally covers the same universe the
2954
+ * numerator does. Without them a GA sale hit `booked` but not `total`:
2955
+ * Free under-reported by GA capacity and SOLD% could exceed 100%.
2956
+ */
2957
+ private gaUnitLabelSet;
2768
2958
  private status;
2959
+ /** Live non-free counters, moved by each delta rather than re-walked. */
2960
+ private counts;
2961
+ /** Bumped whenever the seat model is replaced wholesale (a full snapshot). */
2962
+ private modelVersion;
2769
2963
  private currency;
2770
2964
  private authoritativeGrossRevenue;
2771
2965
  private revenueStatus;
2772
2966
  private revenueRequest;
2773
- private revenueRefreshTimer;
2774
2967
  private controlRoomSnapshot;
2968
+ /**
2969
+ * The server's own totals, pinned to the client model they were read against.
2970
+ * Display = server baseline + (client now − client then), so the authoritative
2971
+ * numbers land exactly on arrival and deltas still move them between reads.
2972
+ * A wholesale model replacement invalidates the pairing (`model`), and the
2973
+ * client tallies — themselves a fresh authenticated read — take over.
2974
+ */
2975
+ private serverBaseline;
2976
+ /** Latest presence frame, held whether or not a snapshot has landed yet. */
2977
+ private livePresence;
2978
+ /** Latest cumulative booked gross pushed on a delta frame. */
2979
+ private liveGross;
2980
+ /** Coalesces a burst of deltas into one KPI/rail repaint. */
2981
+ private paintHandle;
2775
2982
  private trendWindowMinutes;
2776
2983
  private heatEnabled;
2777
2984
  private followLive;
@@ -2901,6 +3108,22 @@ declare class SeatManager {
2901
3108
  private selectableStatuses;
2902
3109
  private updateRendererInteraction;
2903
3110
  private handleSeatSelect;
3111
+ /**
3112
+ * Build the client's inventory universe from the chart.
3113
+ *
3114
+ * `expandChart` yields SEATS — it has no output for a GA area, whose capacity
3115
+ * is sold as N synthetic unit labels. The server's seat map keys, its deltas
3116
+ * and its `totals` all speak those labels, so a client that only knows seats
3117
+ * counts GA sales in the numerator (every key of the snapshot is written into
3118
+ * `status`) while leaving them out of the denominator. Registering the GA
3119
+ * units here — labels only, never a render binding — is what makes the two
3120
+ * agree.
3121
+ */
3122
+ private buildUnitUniverse;
3123
+ /** Every sellable unit the client knows: seats + GA capacity. */
3124
+ private unitTotal;
3125
+ /** Every label the client models, whether or not it can be painted. */
3126
+ private knownLabels;
2904
3127
  private repaintAll;
2905
3128
  /**
2906
3129
  * Open the cockpit's realtime socket AS THE ORGANIZER.
@@ -2920,6 +3143,16 @@ declare class SeatManager {
2920
3143
  private connect;
2921
3144
  private scheduleReconnect;
2922
3145
  private onMessage;
3146
+ /**
3147
+ * Adopt the cumulative booked gross a delta frame carried.
3148
+ *
3149
+ * Stashed with its arrival time so an in-flight control-room read can decide
3150
+ * whether it is holding the newer number: a frame that landed after the
3151
+ * request started is newer than the response, one that landed before is not.
3152
+ */
3153
+ private applyLiveGross;
3154
+ /** The single writer for a label's status, so the counters never drift. */
3155
+ private setStatusLabel;
2923
3156
  private resnapshot;
2924
3157
  /**
2925
3158
  * Replace the whole seat model.
@@ -2930,6 +3163,8 @@ declare class SeatManager {
2930
3163
  * the mode really is free, wrong the moment it is not.
2931
3164
  */
2932
3165
  private applySnapshot;
3166
+ /** The one O(n) walk left: a wholesale model replacement re-bases the counters. */
3167
+ private recountAll;
2933
3168
  /** Optimistic local write shared by organizer actions. Paint and tally once,
2934
3169
  * even when an arena-sized operation changes hundreds of seats. */
2935
3170
  private setSeatsLocal;
@@ -2944,9 +3179,39 @@ declare class SeatManager {
2944
3179
  private locateActivity;
2945
3180
  private showLiveEvent;
2946
3181
  private applyReportRevenue;
3182
+ /**
3183
+ * Read the server's own control-room projection.
3184
+ *
3185
+ * Called on mount, on every socket (re)connect and after an organizer action —
3186
+ * never on a timer and never per delta frame. Presence and gross that arrived
3187
+ * on the socket AFTER this request started are newer than the response, so
3188
+ * they survive it; anything older defers to the read.
3189
+ */
2947
3190
  private refreshControlRoom;
2948
- private scheduleRevenueRefresh;
3191
+ /** Pin the server's totals to the client model they were read against. */
3192
+ private rebaseServerTotals;
3193
+ /** What the client's own model says — GA units included since `render()`. */
3194
+ private clientTallies;
3195
+ /**
3196
+ * The numbers the KPI bar and rail render.
3197
+ *
3198
+ * The server is the authority: its totals land exactly as read, and the
3199
+ * delta-driven client model carries them forward until the next read. Before
3200
+ * the first snapshot — and after a wholesale model replacement invalidates the
3201
+ * pairing — the client model stands alone.
3202
+ */
3203
+ private buildTallies;
3204
+ /**
3205
+ * Queue one KPI/rail repaint for this burst of changes.
3206
+ *
3207
+ * A delta frame can carry hundreds of seats and `paintKpis` rebuilds eight
3208
+ * nodes from scratch, so painting per change is what made an arena-sized
3209
+ * frame expensive. Coalescing on a frame keeps the burst to a single rebuild;
3210
+ * without `requestAnimationFrame` (SSR, an older test env) it paints inline
3211
+ * rather than dropping the update.
3212
+ */
2949
3213
  private recomputeTallies;
3214
+ private flushTallies;
2950
3215
  private verbFor;
2951
3216
  private pushActivity;
2952
3217
  private seedFeed;
@@ -2969,6 +3234,9 @@ declare class SeatManager {
2969
3234
  private paintKpis;
2970
3235
  private paintRail;
2971
3236
  private renderViewRail;
3237
+ /** Live presence wins over the snapshot's copy — it is the fresher channel,
3238
+ * and it exists from the first frame rather than the first fetch. */
3239
+ private presenceCounts;
2972
3240
  private paintMonitorInsights;
2973
3241
  private applyHeatOverlay;
2974
3242
  private renderInspectRail;
@@ -3443,4 +3711,4 @@ declare class ChannelsMode {
3443
3711
  handleBack(): boolean;
3444
3712
  }
3445
3713
 
3446
- export { ACCESS_LINK_DEFAULTS, type AccessLinkRecord, type AccessLinkReveal, type AccessLinkState, type AccessLinkStatus, type AccessLinkStatusRecord, ApiError, type ArchiveBlockedDetails, type AssignmentBuckets, type AssignmentDropDetails, type AssignmentResult, type AttachPickerFrameOptions, type BestAvailableResult, type BucketRow, BuyerAccessContext, type BuyerAccessExpiredEvent, type BuyerAccessRefreshReason, type BuyerAccessToken, type BuyerAccessTokenProvider, BuyerAccessUnavailableError, type BuyerAccessUnavailableEvent, type BuyerAccessUnavailableReason, BuyerRealtimeClient, type BuyerRealtimeOptions, type ChannelAccessIntent, type ChannelAccessSummary, type ChannelAllocationPage, type ChannelAuditEntry, type ChannelAuditPage, type ChannelCounts, type ChannelListResult, type ChannelPreviewProjection, type ChannelRecord, type ChannelSeatStatus, type ChannelState, type ChannelsCapabilities, type ChannelsClient, ChannelsMode, type ChannelsModeHost, type ChannelsSeatView, type CheckoutHandoff, type CheckoutLineItem, type ControlRoomActivityEntry, type ControlRoomSectionMetric, type ControlRoomSnapshot, EmbeddedDesigner, type EmbeddedDesignerEventType, type EmbeddedDesignerMessage, type EmbeddedDesignerOptions, type GAAreaAvailability, type HoldConflict, type HoldLineItem, type HoldResult, type LogEntry, type LogPage, ManageApi, ManageApiError, PUBLIC_CHANNEL_ID, PUBLIC_CHANNEL_NAME, type Projection, type PubApiOptions, type RealtimeSink, type ReportByStatus, type ReportCategoryMeta, type ReportCategoryRow, type ReportResult, type ResumedHoldResult, SeatManager, type SeatManagerActionResult, type SeatManagerActivity, type SeatManagerCapability, type SeatManagerConnection, type SeatManagerMode, type SeatManagerOptions, type SeatManagerTallies, SeatPicker, type SeatPickerOptions, type SeatPickerTheme, SeatingChart, type SeatingChartOptions, type SelectedObjectUnavailableEvent, type SelectedSeat, type SelectionSourceRow, type StatusChange, type SubscribeTicket$1 as SubscribeTicket, accessIntentLabel, accessLine, accessLinkBadge, accessLinkErrorCopy, accessLinkIsLive, accessLinkPolicyLines, attachPickerFrame, bucketRows, bucketRowsHtml, createBuyerAccessContext, createControllerSink, dropReviewRows, isPublicChannelId, markerLetter, markerOf, mutationCount, needsMoveConfirmation, planAssignment, retryAfterCopy, selectionSources, stateBadge, suggestMarker };
3714
+ export { ACCESS_LINK_DEFAULTS, type AccessLinkRecord, type AccessLinkReveal, type AccessLinkState, type AccessLinkStatus, type AccessLinkStatusRecord, ApiError, type ArchiveBlockedDetails, type AssignmentBuckets, type AssignmentDropDetails, type AssignmentResult, type AttachPickerFrameOptions, type BestAvailableResult, type BucketRow, BuyerAccessContext, type BuyerAccessExpiredEvent, type BuyerAccessRefreshReason, type BuyerAccessToken, type BuyerAccessTokenProvider, BuyerAccessUnavailableError, type BuyerAccessUnavailableEvent, type BuyerAccessUnavailableReason, BuyerRealtimeClient, type BuyerRealtimeOptions, type ChannelAccessIntent, type ChannelAccessSummary, type ChannelAllocationPage, type ChannelAuditEntry, type ChannelAuditPage, type ChannelCounts, type ChannelListResult, type ChannelPreviewProjection, type ChannelRecord, type ChannelSeatStatus, type ChannelState, type ChannelsCapabilities, type ChannelsClient, ChannelsMode, type ChannelsModeHost, type ChannelsSeatView, type CheckoutHandoff, type CheckoutLineItem, type CheckoutSessionResult, type ControlRoomActivityEntry, type ControlRoomSectionMetric, type ControlRoomSnapshot, EmbeddedDesigner, type EmbeddedDesignerEventType, type EmbeddedDesignerMessage, type EmbeddedDesignerOptions, type GAAreaAvailability, type HoldConflict, type HoldLineItem, type HoldResult, type LogEntry, type LogPage, ManageApi, ManageApiError, type OrderStatusResult, PUBLIC_CHANNEL_ID, PUBLIC_CHANNEL_NAME, type PaymentOptionsReason, type PaymentOptionsResult, type PaymentProviderName, type Projection, type PubApiOptions, type RealtimeSink, type ReportByStatus, type ReportCategoryMeta, type ReportCategoryRow, type ReportResult, type ResumedHoldResult, SeatManager, type SeatManagerActionResult, type SeatManagerActivity, type SeatManagerCapability, type SeatManagerConnection, type SeatManagerMode, type SeatManagerOptions, type SeatManagerTallies, SeatPicker, type SeatPickerOptions, type SeatPickerTheme, SeatingChart, type SeatingChartOptions, type SelectedObjectUnavailableEvent, type SelectedSeat, type SelectionSourceRow, type StatusChange, type SubscribeTicket$1 as SubscribeTicket, accessIntentLabel, accessLine, accessLinkBadge, accessLinkErrorCopy, accessLinkIsLive, accessLinkPolicyLines, attachPickerFrame, bucketRows, bucketRowsHtml, createBuyerAccessContext, createControllerSink, dropReviewRows, isPublicChannelId, markerLetter, markerOf, mutationCount, needsMoveConfirmation, planAssignment, retryAfterCopy, selectionSources, stateBadge, suggestMarker };