@sensiblestats/widget-sdk 0.74.0 → 0.75.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.js CHANGED
@@ -60,6 +60,23 @@ function headersFor(cfg, json) {
60
60
  if (cfg.client) h["X-SS-Client"] = cfg.client;
61
61
  return h;
62
62
  }
63
+ var OPERATOR_USER_ID_HEADER = "X-SS-Operator-User-Id";
64
+ var ANONYMOUS_USER_ID_HEADER = "X-SS-Anonymous-User-Id";
65
+ var SENDABLE_ID = /^[\x20-\x7E]{1,128}$/;
66
+ function isSendableId(id) {
67
+ const v = id?.trim();
68
+ return !!v && SENDABLE_ID.test(v);
69
+ }
70
+ function identityHeaders(operatorUserId, anonymousUserId) {
71
+ const h = {};
72
+ if (isSendableId(operatorUserId)) h[OPERATOR_USER_ID_HEADER] = operatorUserId.trim();
73
+ if (isSendableId(anonymousUserId)) h[ANONYMOUS_USER_ID_HEADER] = anonymousUserId.trim();
74
+ return h;
75
+ }
76
+ function splitIdentity(body) {
77
+ const { operatorUserId, anonymousUserId, ...rest } = body;
78
+ return { identity: identityHeaders(operatorUserId, anonymousUserId), rest };
79
+ }
63
80
  function endpoint(cfg, path) {
64
81
  return `${cfg.baseUrl.replace(/\/+$/, "")}/v1/${encodeURIComponent(cfg.operatorId)}/${path}`;
65
82
  }
@@ -101,11 +118,11 @@ async function chatError(res) {
101
118
  function isAbort(error) {
102
119
  return error instanceof Error && error.name === "AbortError";
103
120
  }
104
- async function send(cfg, path, body, signal) {
121
+ async function send(cfg, path, body, signal, identity = {}) {
105
122
  try {
106
123
  return await cfg.fetch(endpoint(cfg, path), {
107
124
  method: "POST",
108
- headers: headersFor(cfg, true),
125
+ headers: { ...headersFor(cfg, true), ...identity },
109
126
  body: JSON.stringify(body),
110
127
  signal
111
128
  });
@@ -115,24 +132,35 @@ async function send(cfg, path, body, signal) {
115
132
  }
116
133
  }
117
134
  async function postChat(cfg, input, signal) {
118
- const res = await send(cfg, "chat", input, signal);
135
+ const { userContext, ...rest } = input;
136
+ let body = rest;
137
+ let identity = {};
138
+ if (userContext) {
139
+ const split = splitIdentity(userContext);
140
+ identity = split.identity;
141
+ body = Object.keys(split.rest).length > 0 ? { ...rest, userContext: split.rest } : rest;
142
+ }
143
+ const res = await send(cfg, "chat", body, signal, identity);
119
144
  if (!res.ok || res.body == null) throw await chatError(res);
120
145
  return res;
121
146
  }
122
147
  async function postSignal(cfg, wireBody, signal) {
123
- const res = await send(cfg, "signals", wireBody, signal);
148
+ const { identity, rest } = splitIdentity(wireBody);
149
+ const res = await send(cfg, "signals", rest, signal, identity);
124
150
  if (!res.ok) throw toWidgetError(res.status, res.headers.get("Retry-After"));
125
151
  }
126
152
  async function postEvents(cfg, batch) {
127
- const res = await send(cfg, "events", batch);
153
+ const { identity, rest } = splitIdentity(batch);
154
+ const res = await send(cfg, "events", rest, void 0, identity);
128
155
  if (!res.ok) throw toWidgetError(res.status, res.headers.get("Retry-After"));
129
156
  }
130
157
  function postEventsKeepalive(cfg, batch) {
131
158
  try {
159
+ const { identity, rest } = splitIdentity(batch);
132
160
  void cfg.fetch(endpoint(cfg, "events"), {
133
161
  method: "POST",
134
- headers: headersFor(cfg, true),
135
- body: JSON.stringify(batch),
162
+ headers: { ...headersFor(cfg, true), ...identity },
163
+ body: JSON.stringify(rest),
136
164
  keepalive: true
137
165
  }).catch(() => {
138
166
  });
@@ -182,8 +210,8 @@ async function postPlayerProps(cfg, matchId, sport) {
182
210
  async function postSchedule(cfg, localDate, utcOffsetMinutes, operatorUserId, anonymousUserId) {
183
211
  const res = await cfg.fetch(endpoint(cfg, "live-monitor/schedule"), {
184
212
  method: "POST",
185
- headers: headersFor(cfg, true),
186
- body: JSON.stringify({ localDate, utcOffsetMinutes, sports: ["basketball", "americanfootball"], operatorUserId, anonymousUserId })
213
+ headers: { ...headersFor(cfg, true), ...identityHeaders(operatorUserId, anonymousUserId) },
214
+ body: JSON.stringify({ localDate, utcOffsetMinutes, sports: ["basketball", "americanfootball"] })
187
215
  });
188
216
  if (res.status === 404) return null;
189
217
  if (!res.ok) throw new Error(`live-monitor/schedule ${res.status}`);
@@ -192,8 +220,8 @@ async function postSchedule(cfg, localDate, utcOffsetMinutes, operatorUserId, an
192
220
  async function postLeagueStar(cfg, competitionId, starred, operatorUserId, anonymousUserId) {
193
221
  const res = await cfg.fetch(endpoint(cfg, `leagues/${competitionId}/star`), {
194
222
  method: "POST",
195
- headers: headersFor(cfg, true),
196
- body: JSON.stringify({ starred, operatorUserId, anonymousUserId })
223
+ headers: { ...headersFor(cfg, true), ...identityHeaders(operatorUserId, anonymousUserId) },
224
+ body: JSON.stringify({ starred })
197
225
  });
198
226
  if (res.status === 404) return null;
199
227
  if (!res.ok) throw new Error(`leagues/star ${res.status}`);
@@ -218,7 +246,7 @@ function q(parts) {
218
246
  return kept.length ? `?${kept.join("&")}` : "";
219
247
  }
220
248
  async function getGames(cfg, operatorUserId, gameDay) {
221
- const res = await cfg.fetch(endpoint(cfg, "games" + q({ operatorUserId, gameDay })), { method: "GET", headers: headersFor(cfg, false) });
249
+ const res = await cfg.fetch(endpoint(cfg, "games" + q({ gameDay })), { method: "GET", headers: { ...headersFor(cfg, false), ...identityHeaders(operatorUserId) } });
222
250
  if (res.status === 404) return null;
223
251
  if (!res.ok) throw new Error(`games ${res.status}`);
224
252
  return await res.json();
@@ -230,23 +258,24 @@ async function getGameConfig(cfg, gameId, fixtureId, gameDay, locale) {
230
258
  return await res.json();
231
259
  }
232
260
  async function getGameResult(cfg, gameId, operatorUserId, fixtureId, gameDay, locale) {
233
- const res = await cfg.fetch(endpoint(cfg, `games/${encodeURIComponent(gameId)}/result` + q({ operatorUserId, fixtureId, gameDay, locale })), { method: "GET", headers: headersFor(cfg, false) });
261
+ const res = await cfg.fetch(endpoint(cfg, `games/${encodeURIComponent(gameId)}/result` + q({ fixtureId, gameDay, locale })), { method: "GET", headers: { ...headersFor(cfg, false), ...identityHeaders(operatorUserId) } });
234
262
  if (res.status === 404) return null;
235
263
  if (!res.ok) throw new Error(`game-result ${res.status}`);
236
264
  return await res.json();
237
265
  }
238
266
  async function postPlay(cfg, gameId, body, locale) {
267
+ const { identity, rest } = splitIdentity(body);
239
268
  const res = await cfg.fetch(endpoint(cfg, `games/${encodeURIComponent(gameId)}/plays` + q({ locale })), {
240
269
  method: "POST",
241
- headers: headersFor(cfg, true),
242
- body: JSON.stringify(body)
270
+ headers: { ...headersFor(cfg, true), ...identity },
271
+ body: JSON.stringify(rest)
243
272
  });
244
273
  if (res.status === 404) return null;
245
274
  if (!res.ok) throw new Error(`play ${res.status}`);
246
275
  return await res.json();
247
276
  }
248
277
  async function getLeaderboard(cfg, operatorUserId, week) {
249
- const res = await cfg.fetch(endpoint(cfg, "games/leaderboard" + q({ operatorUserId, week })), { method: "GET", headers: headersFor(cfg, false) });
278
+ const res = await cfg.fetch(endpoint(cfg, "games/leaderboard" + q({ week })), { method: "GET", headers: { ...headersFor(cfg, false), ...identityHeaders(operatorUserId) } });
250
279
  if (res.status === 404) return null;
251
280
  if (!res.ok) throw new Error(`leaderboard ${res.status}`);
252
281
  return await res.json();
@@ -288,11 +317,68 @@ async function getClubRecords(cfg, limit) {
288
317
  return await res.json();
289
318
  }
290
319
  async function getPricePresets(cfg, locale, operatorUserId, options) {
291
- const res = await cfg.fetch(endpoint(cfg, "price-presets" + q({ locale, operatorUserId, pool: options?.pool })), { method: "GET", headers: headersFor(cfg, false) });
320
+ const res = await cfg.fetch(endpoint(cfg, "price-presets" + q({ locale, pool: options?.pool })), { method: "GET", headers: { ...headersFor(cfg, false), ...identityHeaders(operatorUserId) } });
292
321
  if (res.status === 404) return null;
293
322
  if (!res.ok) throw new Error(`price-presets ${res.status}`);
294
323
  return await res.json();
295
324
  }
325
+ async function getBuilderGrid(cfg, locale, req) {
326
+ const query = q({
327
+ locale,
328
+ sport: req.sport,
329
+ legs: req.legs,
330
+ total: req.total,
331
+ marketKey: req.marketKey ?? void 0,
332
+ matchIds: req.widen ? void 0 : req.matchIds.join(","),
333
+ widen: req.widen ? "true" : void 0
334
+ });
335
+ const res = await cfg.fetch(endpoint(cfg, "price-presets" + query), {
336
+ method: "GET",
337
+ headers: { ...headersFor(cfg, false), ...identityHeaders(req.operatorUserId, req.anonymousUserId) }
338
+ });
339
+ if (res.status === 404) return null;
340
+ if (!res.ok) throw new Error(`price-presets ${res.status}`);
341
+ return await res.json();
342
+ }
343
+ function favouritePath(kind, id) {
344
+ return `favourites/${encodeURIComponent(kind)}/${encodeURIComponent(id)}`;
345
+ }
346
+ function favouritesFetch(cfg, method, path, operatorUserId, anonymousUserId) {
347
+ return cfg.fetch(endpoint(cfg, path), { method, headers: { ...headersFor(cfg, false), ...identityHeaders(operatorUserId, anonymousUserId) } });
348
+ }
349
+ async function getFavourites(cfg, sport, operatorUserId, anonymousUserId) {
350
+ const res = await favouritesFetch(cfg, "GET", "favourites" + q({ sport }), operatorUserId, anonymousUserId);
351
+ if (res.status === 400) {
352
+ const body = await res.json().catch(() => null);
353
+ return body?.code === "NoAcceptedIdentity" ? "noAcceptedIdentity" : null;
354
+ }
355
+ if (!res.ok) return null;
356
+ return await res.json();
357
+ }
358
+ var MIN_FAVOURITES_QUERY = 2;
359
+ async function searchFavourites(cfg, sport, query, kind, operatorUserId, anonymousUserId) {
360
+ const text = query.trim();
361
+ if (text.length < MIN_FAVOURITES_QUERY) return [];
362
+ const res = await favouritesFetch(cfg, "GET", "favourites/search" + q({ sport, q: text, kind }), operatorUserId, anonymousUserId);
363
+ if (!res.ok) return null;
364
+ return (await res.json()).results;
365
+ }
366
+ async function putFavourite(cfg, kind, id, operatorUserId, anonymousUserId) {
367
+ const res = await favouritesFetch(cfg, "PUT", favouritePath(kind, id), operatorUserId, anonymousUserId);
368
+ if (res.ok) return await res.json();
369
+ if (res.status !== 409) return null;
370
+ const body = await res.json().catch(() => null);
371
+ return body?.status === "atCap" ? { status: "atCap", count: body.count, cap: body.cap } : null;
372
+ }
373
+ async function deleteFavourite(cfg, kind, id, operatorUserId, anonymousUserId) {
374
+ return (await favouritesFetch(cfg, "DELETE", favouritePath(kind, id), operatorUserId, anonymousUserId)).ok;
375
+ }
376
+ async function clearFavourites(cfg, sport, operatorUserId, anonymousUserId) {
377
+ return (await favouritesFetch(cfg, "DELETE", "favourites" + q({ sport }), operatorUserId, anonymousUserId)).ok;
378
+ }
379
+ async function hideFavourite(cfg, kind, id, operatorUserId, anonymousUserId) {
380
+ return (await favouritesFetch(cfg, "POST", `${favouritePath(kind, id)}/hide`, operatorUserId, anonymousUserId)).ok;
381
+ }
296
382
 
297
383
  // src/ndjson.ts
298
384
  function parseLine(line) {
@@ -488,7 +574,7 @@ var Conversation = class {
488
574
  }
489
575
  sendSignal(input) {
490
576
  const operatorUserId = input.operatorUserId ?? this.context?.operatorUserId;
491
- const anonymousUserId = operatorUserId ? void 0 : input.anonymousUserId ?? this.context?.anonymousUserId;
577
+ const anonymousUserId = isSendableId(operatorUserId) ? void 0 : input.anonymousUserId ?? this.context?.anonymousUserId;
492
578
  if (!operatorUserId && !anonymousUserId) {
493
579
  throw new Error("operatorUserId or anonymousUserId is required (set it on conversation() or pass it to sendSignal)");
494
580
  }
@@ -512,12 +598,33 @@ var Conversation = class {
512
598
  getPricePresets(locale, options) {
513
599
  return getPricePresets(this.cfg, locale, this.context?.operatorUserId, options);
514
600
  }
601
+ getBuilderGrid(locale, req) {
602
+ return getBuilderGrid(this.cfg, locale, req);
603
+ }
515
604
  getSchedule(localDate, utcOffsetMinutes, operatorUserId, anonymousUserId) {
516
605
  return postSchedule(this.cfg, localDate, utcOffsetMinutes, operatorUserId, anonymousUserId);
517
606
  }
518
607
  starLeague(competitionId, starred, operatorUserId, anonymousUserId) {
519
608
  return postLeagueStar(this.cfg, competitionId, starred, operatorUserId, anonymousUserId);
520
609
  }
610
+ getFavourites(sport, operatorUserId, anonymousUserId) {
611
+ return getFavourites(this.cfg, sport, operatorUserId, anonymousUserId);
612
+ }
613
+ searchFavourites(sport, query, kind, operatorUserId, anonymousUserId) {
614
+ return searchFavourites(this.cfg, sport, query, kind, operatorUserId, anonymousUserId);
615
+ }
616
+ putFavourite(kind, id, operatorUserId, anonymousUserId) {
617
+ return putFavourite(this.cfg, kind, id, operatorUserId, anonymousUserId);
618
+ }
619
+ deleteFavourite(kind, id, operatorUserId, anonymousUserId) {
620
+ return deleteFavourite(this.cfg, kind, id, operatorUserId, anonymousUserId);
621
+ }
622
+ clearFavourites(sport, operatorUserId, anonymousUserId) {
623
+ return clearFavourites(this.cfg, sport, operatorUserId, anonymousUserId);
624
+ }
625
+ hideFavourite(kind, id, operatorUserId, anonymousUserId) {
626
+ return hideFavourite(this.cfg, kind, id, operatorUserId, anonymousUserId);
627
+ }
521
628
  getStates(matchIds, nbaGameIds, nflGameIds) {
522
629
  return postStates(this.cfg, matchIds, nbaGameIds, nflGameIds);
523
630
  }
@@ -567,7 +674,8 @@ var IMMEDIATE = /* @__PURE__ */ new Set([
567
674
  "slip_click",
568
675
  "slip_result",
569
676
  "slip_remove",
570
- "bet_placed"
677
+ "bet_placed",
678
+ "combo_add"
571
679
  ]);
572
680
  var DEFERRED = /* @__PURE__ */ new Set(["match_view"]);
573
681
  var DEFAULT_BATCH_INTERVAL_MS = 1e4;