@sportsdataverse/sdvplot 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/NOTICE.md +94 -0
- package/README.md +908 -2
- package/dist/_web-D7Aa5Rd8.d.ts +69 -0
- package/dist/aaf-GJ3SH34T.js +13 -0
- package/dist/aaf-HCYN5T6Y.js +37 -0
- package/dist/aaf-ZAE5KS2Y.js +13 -0
- package/dist/ahl-E66MDVCU.js +66 -0
- package/dist/ahl-NPQQWZEV.js +249 -0
- package/dist/ahl-QRDU73XX.js +309 -0
- package/dist/axis-CraWQRUJ.d.ts +49 -0
- package/dist/bins.d.ts +174 -0
- package/dist/bins.js +4 -0
- package/dist/bins.js.map +1 -0
- package/dist/cfb-F42SVS4X.js +5000 -0
- package/dist/cfb-P3SHEYRZ.js +692 -0
- package/dist/cfb-WXRZANLP.js +10226 -0
- package/dist/chartjs-surface.d.ts +42 -0
- package/dist/chartjs-surface.js +65 -0
- package/dist/chartjs-surface.js.map +1 -0
- package/dist/chartjs.d.ts +132 -0
- package/dist/chartjs.js +339 -0
- package/dist/chartjs.js.map +1 -0
- package/dist/chunk-4C3GX6QO.js +92 -0
- package/dist/chunk-4C3GX6QO.js.map +1 -0
- package/dist/chunk-C2CDY5OG.js +137 -0
- package/dist/chunk-C2CDY5OG.js.map +1 -0
- package/dist/chunk-E7G7VSLV.js +43 -0
- package/dist/chunk-E7G7VSLV.js.map +1 -0
- package/dist/chunk-J6R2LEJ7.js +58 -0
- package/dist/chunk-J6R2LEJ7.js.map +1 -0
- package/dist/chunk-JJFTD4VB.js +175 -0
- package/dist/chunk-JJFTD4VB.js.map +1 -0
- package/dist/chunk-KKH6ABMA.js +223 -0
- package/dist/chunk-KKH6ABMA.js.map +1 -0
- package/dist/chunk-NLPH2VZT.js +127 -0
- package/dist/chunk-NLPH2VZT.js.map +1 -0
- package/dist/chunk-OKEE4AH6.js +30 -0
- package/dist/chunk-OKEE4AH6.js.map +1 -0
- package/dist/chunk-QY65DUEF.js +306 -0
- package/dist/chunk-QY65DUEF.js.map +1 -0
- package/dist/chunk-RMT2GMFH.js +97 -0
- package/dist/chunk-RMT2GMFH.js.map +1 -0
- package/dist/chunk-SEBO35DV.js +76 -0
- package/dist/chunk-SEBO35DV.js.map +1 -0
- package/dist/chunk-TAXO3VXH.js +556 -0
- package/dist/chunk-TAXO3VXH.js.map +1 -0
- package/dist/chunk-TDNBAEQB.js +105 -0
- package/dist/chunk-TDNBAEQB.js.map +1 -0
- package/dist/cricket-KPCYG2HL.js +493 -0
- package/dist/cricket-NDXLE25B.js +116 -0
- package/dist/cricket-YUUF7P6M.js +160 -0
- package/dist/d3.d.ts +234 -0
- package/dist/d3.js +198 -0
- package/dist/d3.js.map +1 -0
- package/dist/echarts.d.ts +121 -0
- package/dist/echarts.js +228 -0
- package/dist/echarts.js.map +1 -0
- package/dist/echl-3CR4RGFC.js +241 -0
- package/dist/echl-NPC32C23.js +107 -0
- package/dist/echl-PI33DGJC.js +64 -0
- package/dist/export.d.ts +130 -0
- package/dist/export.js +258 -0
- package/dist/export.js.map +1 -0
- package/dist/headshots-B2J_LOO8.d.ts +40 -0
- package/dist/index.d.ts +182 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/interact.d.ts +567 -0
- package/dist/interact.js +809 -0
- package/dist/interact.js.map +1 -0
- package/dist/marks-DH-tQIP-.d.ts +75 -0
- package/dist/mbb-3TRLVW3H.js +3619 -0
- package/dist/mbb-DQUHCHZP.js +5111 -0
- package/dist/mbb-PRAIO2P5.js +371 -0
- package/dist/milb-6IQ6NCEW.js +291 -0
- package/dist/milb-EYY5MD4T.js +1752 -0
- package/dist/milb-EYZ4SZLI.js +797 -0
- package/dist/mlb-4PBUJSVQ.js +35 -0
- package/dist/mlb-FHPY3GQJ.js +701 -0
- package/dist/mlb-REVGL3N2.js +665 -0
- package/dist/nba-ATT775GS.js +485 -0
- package/dist/nba-LKM4UHVU.js +482 -0
- package/dist/nba-YX7PXM3R.js +35 -0
- package/dist/nbagl-3WGZCZ4S.js +37 -0
- package/dist/nbagl-LP4XJRAL.js +229 -0
- package/dist/nbagl-VTTOU7LN.js +499 -0
- package/dist/ncaa_baseball-UV75LNVR.js +2670 -0
- package/dist/ncaa_baseball-VD5J5C72.js +419 -0
- package/dist/ncaa_baseball-YRBS32D6.js +4592 -0
- package/dist/ncaa_mhockey-IMYHS7QS.js +1033 -0
- package/dist/ncaa_mhockey-TXTKWHE3.js +115 -0
- package/dist/ncaa_mhockey-U3MFW5VT.js +663 -0
- package/dist/ncaa_softball-6WR6KS64.js +420 -0
- package/dist/ncaa_softball-RZCTIE56.js +2654 -0
- package/dist/ncaa_softball-VOD4PVGY.js +4599 -0
- package/dist/ncaa_whockey-6XXGUTBK.js +607 -0
- package/dist/ncaa_whockey-AKKYKY67.js +53 -0
- package/dist/ncaa_whockey-KBEKXFBB.js +293 -0
- package/dist/nfl-BDB2HYAR.js +625 -0
- package/dist/nfl-EHYHDLJD.js +37 -0
- package/dist/nfl-VF4NJCHY.js +514 -0
- package/dist/nfl_gsis-UY64JO37.js +4 -0
- package/dist/nhl-6WPHME7H.js +511 -0
- package/dist/nhl-EMIJFZHY.js +884 -0
- package/dist/nhl-ISFUKRFU.js +37 -0
- package/dist/ohl-2FEM554G.js +113 -0
- package/dist/ohl-C5Q4W6JB.js +32 -0
- package/dist/ohl-TQ4J46VC.js +66 -0
- package/dist/phf-H7POU2KW.js +32 -0
- package/dist/phf-K5IFMZUM.js +13 -0
- package/dist/phf-TWJI3BOW.js +20 -0
- package/dist/plot.d.ts +449 -0
- package/dist/plot.js +711 -0
- package/dist/plot.js.map +1 -0
- package/dist/plotly.d.ts +129 -0
- package/dist/plotly.js +360 -0
- package/dist/plotly.js.map +1 -0
- package/dist/pwhl-OX3BO4JI.js +53 -0
- package/dist/pwhl-W44OC3C6.js +17 -0
- package/dist/pwhl-ZJGG3H7W.js +23 -0
- package/dist/qmjhl-5HISEHLL.js +35 -0
- package/dist/qmjhl-DA3UHKE5.js +125 -0
- package/dist/qmjhl-YG4BOQPF.js +614 -0
- package/dist/react.d.ts +101 -0
- package/dist/react.js +164 -0
- package/dist/react.js.map +1 -0
- package/dist/resolve-CQW-jzvr.d.ts +28 -0
- package/dist/selection-D4T3PkRt.d.ts +140 -0
- package/dist/shots.d.ts +364 -0
- package/dist/shots.js +85 -0
- package/dist/shots.js.map +1 -0
- package/dist/soccer-7BH3ZLVL.js +7898 -0
- package/dist/soccer-MRU25OGE.js +2636 -0
- package/dist/soccer-PF4IPPVA.js +12342 -0
- package/dist/surfaces-ChaD9nei.d.ts +38 -0
- package/dist/testing.d.ts +75 -0
- package/dist/testing.js +240 -0
- package/dist/testing.js.map +1 -0
- package/dist/tiers-B6pUBPcE.d.ts +93 -0
- package/dist/types-C7n6gJF3.d.ts +62 -0
- package/dist/ufl-7VLZTLTT.js +143 -0
- package/dist/ufl-CCMBLPSW.js +100 -0
- package/dist/ufl-F2BJRXME.js +16 -0
- package/dist/usfl-4CR5RMZJ.js +32 -0
- package/dist/usfl-LD7HP2YK.js +14 -0
- package/dist/usfl-VU4YSJQD.js +19 -0
- package/dist/ushl-CPG2LB4L.js +73 -0
- package/dist/ushl-PQL6HJAD.js +88 -0
- package/dist/ushl-ZT2ATPHK.js +22 -0
- package/dist/vega.d.ts +85 -0
- package/dist/vega.js +362 -0
- package/dist/vega.js.map +1 -0
- package/dist/wbb-3KHCDYTK.js +5087 -0
- package/dist/wbb-BMX53Q3Q.js +369 -0
- package/dist/wbb-QNRN2CJJ.js +3620 -0
- package/dist/whl-4ATYZWUB.js +97 -0
- package/dist/whl-RVFPL6MH.js +79 -0
- package/dist/whl-Y7ASKYMM.js +28 -0
- package/dist/wnba-FWX5SFVI.js +232 -0
- package/dist/wnba-GX6HZR4B.js +220 -0
- package/dist/wnba-NTMWIPP6.js +20 -0
- package/dist/xfl-43R2JMJK.js +30 -0
- package/dist/xfl-H3BXQPZZ.js +16 -0
- package/dist/xfl-XON7A7FI.js +69 -0
- package/package.json +154 -3
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { BasketballColorKey, FootballColorKey, HockeyColorKey, BasketballDisplayRange, FootballDisplayRange, HockeyDisplayRange, BasketballParamUpdates, FootballParamUpdates, HockeyParamUpdates } from '@sportsdataverse/sporty';
|
|
2
|
+
import { V as Value } from './resolve-CQW-jzvr.js';
|
|
3
|
+
import { S as SeasonInput } from './types-C7n6gJF3.js';
|
|
4
|
+
|
|
5
|
+
type SurfaceSport = "basketball" | "football" | "hockey";
|
|
6
|
+
/** Maps an sdv league to `[sporty sport, sporty league key]`. Baseball and soccer surfaces arrive in Phase 6; Python's team-less `fiba` is omitted (not an sdv League). */
|
|
7
|
+
declare const SURFACES: Readonly<Record<string, readonly [SurfaceSport, string]>>;
|
|
8
|
+
declare const SURFACE_BASE: {
|
|
9
|
+
readonly basketball: "#d2ab6f";
|
|
10
|
+
readonly football: "#196f0c";
|
|
11
|
+
readonly hockey: "#ffffff";
|
|
12
|
+
};
|
|
13
|
+
type Paint<K extends string> = Partial<Record<K, string>>;
|
|
14
|
+
/** Port of `_surface.color_updates`: the team colours a sport's surface takes, keyed by sporty colour key. */
|
|
15
|
+
declare function colorUpdates(sport: "basketball", primary: string, secondary?: string): Paint<BasketballColorKey>;
|
|
16
|
+
declare function colorUpdates(sport: "football", primary: string, secondary?: string): Paint<FootballColorKey>;
|
|
17
|
+
declare function colorUpdates(sport: "hockey", primary: string, secondary?: string): Paint<HockeyColorKey>;
|
|
18
|
+
declare function colorUpdates(sport: SurfaceSport, primary: string, secondary?: string): Record<string, string>;
|
|
19
|
+
type AnyRange = BasketballDisplayRange | FootballDisplayRange | HockeyDisplayRange;
|
|
20
|
+
interface SurfaceSceneOptions {
|
|
21
|
+
team?: Value;
|
|
22
|
+
season?: SeasonInput;
|
|
23
|
+
/** Overrides on top of the team painting, keyed by sporty colour key. */
|
|
24
|
+
colorUpdates?: Paint<BasketballColorKey | FootballColorKey | HockeyColorKey>;
|
|
25
|
+
rotation?: number;
|
|
26
|
+
/** A sporty display range for the league's sport; one the sport does not know throws sporty's UnknownDisplayRangeError. */
|
|
27
|
+
displayRange?: AnyRange;
|
|
28
|
+
xlim?: readonly [number, number];
|
|
29
|
+
ylim?: readonly [number, number];
|
|
30
|
+
units?: "ft" | "m" | "yd" | "in" | "cm" | "mm";
|
|
31
|
+
/** Sporty parameter overrides (intersected across sports so each sport's own keys type-check). */
|
|
32
|
+
updates?: BasketballParamUpdates & FootballParamUpdates & HockeyParamUpdates;
|
|
33
|
+
arcResolution?: number;
|
|
34
|
+
xTrans?: number;
|
|
35
|
+
yTrans?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { type SurfaceSceneOptions as S, SURFACES as a, SURFACE_BASE as b, colorUpdates as c };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { L as League } from './types-C7n6gJF3.js';
|
|
2
|
+
import { V as Value } from './resolve-CQW-jzvr.js';
|
|
3
|
+
import { E as EspnHeadshotLeague } from './headshots-B2J_LOO8.js';
|
|
4
|
+
import { A as Axis } from './axis-CraWQRUJ.js';
|
|
5
|
+
import '@observablehq/plot';
|
|
6
|
+
|
|
7
|
+
interface DrawnMark {
|
|
8
|
+
id: string;
|
|
9
|
+
x: Value;
|
|
10
|
+
y: Value;
|
|
11
|
+
/** Drawn image height as a fraction of the frame height the render saw. */
|
|
12
|
+
height: number;
|
|
13
|
+
url: string;
|
|
14
|
+
kind: string;
|
|
15
|
+
}
|
|
16
|
+
/** Every `<image data-sdv-id>` under `node`, in document order. */
|
|
17
|
+
declare function drawnMarks(node: ParentNode): DrawnMark[];
|
|
18
|
+
interface ContractMarkOptions {
|
|
19
|
+
league: League;
|
|
20
|
+
height?: number;
|
|
21
|
+
alpha?: number;
|
|
22
|
+
}
|
|
23
|
+
/** Adapters must accept typed arrays (e.g. `Float64Array`) for the x and y positions: rule 3 passes them. */
|
|
24
|
+
interface ContractAdapter<T> {
|
|
25
|
+
name: string;
|
|
26
|
+
addLogos(target: T, x: ArrayLike<Value>, y: ArrayLike<Value>, teams: readonly Value[], o: ContractMarkOptions): T | Promise<T>;
|
|
27
|
+
addWordmarks(target: T, x: ArrayLike<Value>, y: ArrayLike<Value>, teams: readonly Value[], o: ContractMarkOptions): T | Promise<T>;
|
|
28
|
+
/** `league` is narrowed to the ESPN-headshot leagues: the contract checks that before calling, so adapters need no cast. */
|
|
29
|
+
addHeadshots(target: T, x: ArrayLike<Value>, y: ArrayLike<Value>, players: readonly Value[], o: Omit<ContractMarkOptions, "league"> & {
|
|
30
|
+
league: EspnHeadshotLeague;
|
|
31
|
+
}): T | Promise<T>;
|
|
32
|
+
/** Throws `UnsupportedTargetError` when `supportsAxisLogos` is false. */
|
|
33
|
+
axisLogos(target: T, axis: "x" | "y", o: {
|
|
34
|
+
league: League;
|
|
35
|
+
height?: number;
|
|
36
|
+
}): T | Promise<T>;
|
|
37
|
+
supportsAxisLogos: boolean;
|
|
38
|
+
drawnMarks(target: T): readonly DrawnMark[];
|
|
39
|
+
drawnAxisMarks(target: T, axis: "x" | "y"): readonly {
|
|
40
|
+
id: string;
|
|
41
|
+
tick: number;
|
|
42
|
+
height: number;
|
|
43
|
+
}[];
|
|
44
|
+
visibleAxisLabels(target: T, axis: "x" | "y"): readonly string[];
|
|
45
|
+
}
|
|
46
|
+
/** `message` starts with "rule N (...): ..." so a test can match `/^rule N/`. */
|
|
47
|
+
declare class ContractError extends Error {
|
|
48
|
+
readonly rule: string;
|
|
49
|
+
constructor(rule: string, msg: string);
|
|
50
|
+
}
|
|
51
|
+
interface ContractOptions<T> {
|
|
52
|
+
makeTarget: () => T;
|
|
53
|
+
makeAxisTarget?: (categories: readonly string[]) => T;
|
|
54
|
+
league?: League;
|
|
55
|
+
known?: readonly [string, string];
|
|
56
|
+
knownWordmarks?: readonly [string, string];
|
|
57
|
+
players?: readonly [string, string];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Port of Python `sdvplot.testing.check_adapter_contract` (rules 0-8), plus rule 9: no verb mutates its input (the
|
|
61
|
+
* target's plain-data part and the position and value arrays, deep-frozen). Rejects with a `ContractError`
|
|
62
|
+
* naming the first rule broken. Warnings are once per call (one message listing every unresolved value).
|
|
63
|
+
* Resets the warning handler to null (the default) when done: the previous handler cannot be restored.
|
|
64
|
+
*/
|
|
65
|
+
declare function checkAdapterContract<T>(a: ContractAdapter<T>, o: ContractOptions<T>): Promise<void>;
|
|
66
|
+
/** Axis images (`<image data-sdv-axis>`), in tick-position order; `height` is a fraction of the frame. */
|
|
67
|
+
declare function drawnAxisMarks(node: ParentNode, axis: Axis): {
|
|
68
|
+
id: string;
|
|
69
|
+
tick: number;
|
|
70
|
+
height: number;
|
|
71
|
+
}[];
|
|
72
|
+
/** Non-empty tick `<text>` still on the axis (categories that did not resolve to an image). */
|
|
73
|
+
declare function visibleAxisLabels(node: ParentNode, axis: Axis): string[];
|
|
74
|
+
|
|
75
|
+
export { type ContractAdapter, ContractError, type ContractMarkOptions, type ContractOptions, type DrawnMark, checkAdapterContract, drawnAxisMarks, drawnMarks, visibleAxisLabels };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import './chunk-JJFTD4VB.js';
|
|
2
|
+
import './chunk-NLPH2VZT.js';
|
|
3
|
+
import './chunk-4C3GX6QO.js';
|
|
4
|
+
import './chunk-RMT2GMFH.js';
|
|
5
|
+
import { logoUrlSync } from './chunk-KKH6ABMA.js';
|
|
6
|
+
import './chunk-J6R2LEJ7.js';
|
|
7
|
+
import { resolveSync, ESPN_HEADSHOT_LEAGUES, headshotUrl } from './chunk-TAXO3VXH.js';
|
|
8
|
+
import { setWarningHandler, UnsupportedTargetError, resetWarnings, InputError } from './chunk-E7G7VSLV.js';
|
|
9
|
+
|
|
10
|
+
// src/testing/index.ts
|
|
11
|
+
var num = (s) => s === null || s === "" ? null : Number.isNaN(Number(s)) ? s : Number(s);
|
|
12
|
+
function drawnMarks(node) {
|
|
13
|
+
return Array.from(node.querySelectorAll("image[data-sdv-id]")).map((img) => ({
|
|
14
|
+
id: img.getAttribute("data-sdv-id") ?? "",
|
|
15
|
+
x: num(img.getAttribute("data-sdv-x")),
|
|
16
|
+
y: num(img.getAttribute("data-sdv-y")),
|
|
17
|
+
height: Number(img.getAttribute("height")) / Number(img.getAttribute("data-sdv-frame")),
|
|
18
|
+
url: img.getAttribute("href") ?? "",
|
|
19
|
+
kind: img.getAttribute("data-sdv-kind") ?? ""
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
var ContractError = class extends Error {
|
|
23
|
+
constructor(rule, msg) {
|
|
24
|
+
super(`${rule}: ${msg}`);
|
|
25
|
+
this.rule = rule;
|
|
26
|
+
this.name = "ContractError";
|
|
27
|
+
}
|
|
28
|
+
rule;
|
|
29
|
+
};
|
|
30
|
+
var fail = (rule, msg) => {
|
|
31
|
+
throw new ContractError(rule, msg);
|
|
32
|
+
};
|
|
33
|
+
var xyz = (m) => m.map((d) => [d.id, d.x, d.y]);
|
|
34
|
+
var same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
35
|
+
async function counted(fn) {
|
|
36
|
+
let n = 0;
|
|
37
|
+
resetWarnings();
|
|
38
|
+
setWarningHandler(() => {
|
|
39
|
+
n++;
|
|
40
|
+
});
|
|
41
|
+
try {
|
|
42
|
+
return [await fn(), n];
|
|
43
|
+
} finally {
|
|
44
|
+
setWarningHandler(null);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
var plain = (v) => Array.isArray(v) || typeof v === "object" && v !== null && [Object.prototype, null].includes(Object.getPrototypeOf(v));
|
|
48
|
+
function deepFreeze(v) {
|
|
49
|
+
if (plain(v) && !Object.isFrozen(v)) {
|
|
50
|
+
Object.freeze(v);
|
|
51
|
+
for (const x of Object.values(v)) deepFreeze(x);
|
|
52
|
+
}
|
|
53
|
+
return v;
|
|
54
|
+
}
|
|
55
|
+
var snapshot = (v) => JSON.stringify(v, (_k, x) => typeof x === "object" && x !== null && !plain(x) ? "[object]" : x);
|
|
56
|
+
async function notInputError(fn) {
|
|
57
|
+
try {
|
|
58
|
+
await fn();
|
|
59
|
+
} catch (e) {
|
|
60
|
+
if (e instanceof InputError) return null;
|
|
61
|
+
return `threw ${e instanceof Error ? `${e.name}: ${e.message}` : String(e)}`;
|
|
62
|
+
}
|
|
63
|
+
return "did not throw";
|
|
64
|
+
}
|
|
65
|
+
async function checkAdapterContract(a, o) {
|
|
66
|
+
const league = o.league ?? "nfl";
|
|
67
|
+
const [ka, kb] = o.known ?? ["LV", "LAR"];
|
|
68
|
+
const [wa, wb] = o.knownWordmarks ?? ["LV", "LAC"];
|
|
69
|
+
const [p, q] = o.players ?? ["3139477", "4241479"];
|
|
70
|
+
const xs = [10, 20];
|
|
71
|
+
const ys = [-3, -7];
|
|
72
|
+
for (const k of [
|
|
73
|
+
"addLogos",
|
|
74
|
+
"addWordmarks",
|
|
75
|
+
"addHeadshots",
|
|
76
|
+
"axisLogos",
|
|
77
|
+
"drawnMarks",
|
|
78
|
+
"drawnAxisMarks",
|
|
79
|
+
"visibleAxisLabels"
|
|
80
|
+
])
|
|
81
|
+
if (typeof a[k] !== "function") fail("rule 0 (shape)", `${a.name} lacks ${k}`);
|
|
82
|
+
if (typeof a.supportsAxisLogos !== "boolean") fail("rule 0 (shape)", "supportsAxisLogos must be boolean");
|
|
83
|
+
const checkVerb = async (verb, vals, ids, urls, r) => {
|
|
84
|
+
const want = [
|
|
85
|
+
[ids[0], 10, -3],
|
|
86
|
+
[ids[1], 20, -7]
|
|
87
|
+
];
|
|
88
|
+
const [t1, n1] = await counted(() => verb(o.makeTarget(), xs, ys, vals, {}));
|
|
89
|
+
const m1 = a.drawnMarks(t1);
|
|
90
|
+
if (!same(xyz(m1), want)) fail(r[0], `expected ${JSON.stringify(want)}, drew ${JSON.stringify(xyz(m1))}`);
|
|
91
|
+
if (n1 !== 0) fail(r[0], `known values warned ${n1} time(s)`);
|
|
92
|
+
m1.forEach((d, i) => {
|
|
93
|
+
const u = urls[i];
|
|
94
|
+
if (u && d.url !== u) fail(r[0], `${d.id} drew ${d.url}, its mark is ${u}`);
|
|
95
|
+
});
|
|
96
|
+
const [t2, n2] = await counted(() => verb(o.makeTarget(), xs, ys, ["XXX", vals[1]], {}));
|
|
97
|
+
if (!same(xyz(a.drawnMarks(t2)), [want[1]]))
|
|
98
|
+
fail(r[1], "an unknown value must be skipped with its own x/y");
|
|
99
|
+
if (n2 !== 1) fail(r[1], `one unknown value must warn exactly once, warned ${n2}`);
|
|
100
|
+
const [, n3] = await counted(
|
|
101
|
+
() => verb(o.makeTarget(), [1, 2, 3], [1, 2, 3], ["XXX", "YYY", vals[0]], {})
|
|
102
|
+
);
|
|
103
|
+
if (n3 !== 1)
|
|
104
|
+
fail(
|
|
105
|
+
r[1],
|
|
106
|
+
`two distinct unknown values must warn exactly once per call (one message listing both), warned ${n3}`
|
|
107
|
+
);
|
|
108
|
+
for (const h of [0.1, 0.25, 1]) {
|
|
109
|
+
const t = await verb(o.makeTarget(), xs, ys, vals, { height: h });
|
|
110
|
+
for (const d of a.drawnMarks(t))
|
|
111
|
+
if (Math.abs(d.height - h) / h > 0.01) fail(r[2], `asked height ${h}, drew ${d.height}`);
|
|
112
|
+
}
|
|
113
|
+
for (const bad of [0, 1.5, 40]) {
|
|
114
|
+
const why = await notInputError(() => verb(o.makeTarget(), xs, ys, vals, { height: bad }));
|
|
115
|
+
if (why) fail(r[2], `height ${bad} must throw InputError when the verb is called, ${why}`);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
const [ia = "", ib = ""] = resolveSync([ka, kb], league);
|
|
119
|
+
await checkVerb(
|
|
120
|
+
(t, x, y, v, opts) => a.addLogos(t, x, y, v, { league, ...opts }),
|
|
121
|
+
[ka, kb],
|
|
122
|
+
[ia, ib],
|
|
123
|
+
[logoUrlSync(ka, league), logoUrlSync(kb, league)],
|
|
124
|
+
["rule 1 (resolution)", "rule 2 (unknown team: warn and skip)", "rule 4 (height semantics)"]
|
|
125
|
+
);
|
|
126
|
+
const tA = await a.addLogos(o.makeTarget(), xs, ys, [ka, kb], { league });
|
|
127
|
+
const tB = await a.addLogos(o.makeTarget(), new Float64Array(xs), new Float64Array(ys), [ka, kb], {
|
|
128
|
+
league
|
|
129
|
+
});
|
|
130
|
+
if (!same(xyz(a.drawnMarks(tA)), xyz(a.drawnMarks(tB))))
|
|
131
|
+
fail("rule 3 (columnar parity)", "typed-array positions drew different marks");
|
|
132
|
+
const [wia = "", wib = ""] = resolveSync([wa, wb], league);
|
|
133
|
+
const wm = (t) => logoUrlSync(t, league, { markType: "wordmark" });
|
|
134
|
+
await checkVerb(
|
|
135
|
+
(t, x, y, v, opts) => a.addWordmarks(t, x, y, v, { league, ...opts }),
|
|
136
|
+
[wa, wb],
|
|
137
|
+
[wia, wib],
|
|
138
|
+
[wm(wa), wm(wb)],
|
|
139
|
+
["rule 5 (wordmarks: resolution)", "rule 5 (wordmarks: warn and skip)", "rule 5 (wordmarks: height)"]
|
|
140
|
+
);
|
|
141
|
+
if (!Object.hasOwn(ESPN_HEADSHOT_LEAGUES, league))
|
|
142
|
+
fail(
|
|
143
|
+
"rule 6 (headshots)",
|
|
144
|
+
`league ${JSON.stringify(league)} has no ESPN headshots; pass an ESPN headshot league`
|
|
145
|
+
);
|
|
146
|
+
const hl = league;
|
|
147
|
+
await checkVerb(
|
|
148
|
+
(t, x, y, v, opts) => a.addHeadshots(t, x, y, v, { league: hl, ...opts }),
|
|
149
|
+
[p, q],
|
|
150
|
+
[p, q],
|
|
151
|
+
[headshotUrl(p, hl), headshotUrl(q, hl)],
|
|
152
|
+
["rule 6 (headshots)", "rule 6 (headshots: unknown id)", "rule 6 (headshots: height)"]
|
|
153
|
+
);
|
|
154
|
+
const r7 = "rule 7 (axis logos)";
|
|
155
|
+
if (a.supportsAxisLogos) {
|
|
156
|
+
const mk2 = o.makeAxisTarget;
|
|
157
|
+
if (!mk2) return fail(r7, "makeAxisTarget is required for an adapter that supports axis logos");
|
|
158
|
+
const [[marks, shown], n] = await counted(async () => {
|
|
159
|
+
const t = await a.axisLogos(mk2([ka, "XXX", kb]), "x", { league });
|
|
160
|
+
return [a.drawnAxisMarks(t, "x"), a.visibleAxisLabels(t, "x")];
|
|
161
|
+
});
|
|
162
|
+
if (!same(
|
|
163
|
+
marks.map((m) => m.id),
|
|
164
|
+
[ia, ib]
|
|
165
|
+
))
|
|
166
|
+
fail(r7, `expected axis images for [${ia}, ${ib}] in tick order, drew ${JSON.stringify(marks)}`);
|
|
167
|
+
if (!((marks[0]?.tick ?? 0) < (marks[1]?.tick ?? 0)))
|
|
168
|
+
fail(r7, "axis images must sit at increasing tick positions");
|
|
169
|
+
if (n !== 1) fail(r7, `an unknown axis category must warn exactly once, warned ${n}`);
|
|
170
|
+
if (!shown.includes("XXX") || shown.includes(ka) || shown.includes(kb))
|
|
171
|
+
fail(r7, `only the unknown category may stay as text, visible labels are ${JSON.stringify(shown)}`);
|
|
172
|
+
for (const h of [0.1, 0.25])
|
|
173
|
+
for (const m of a.drawnAxisMarks(await a.axisLogos(mk2([ka, kb]), "x", { league, height: h }), "x"))
|
|
174
|
+
if (Math.abs(m.height - h) / h > 0.01) fail(`${r7} (height)`, `asked ${h}, drew ${m.height}`);
|
|
175
|
+
} else {
|
|
176
|
+
let threw = false;
|
|
177
|
+
try {
|
|
178
|
+
await a.axisLogos(o.makeTarget(), "x", { league });
|
|
179
|
+
} catch (e) {
|
|
180
|
+
threw = e instanceof UnsupportedTargetError;
|
|
181
|
+
}
|
|
182
|
+
if (!threw)
|
|
183
|
+
fail(r7, "an adapter without axis-logo support must throw UnsupportedTargetError from axisLogos");
|
|
184
|
+
}
|
|
185
|
+
const alphaVerbs = [
|
|
186
|
+
["addLogos", (alpha) => a.addLogos(o.makeTarget(), [10], [-3], [ka], { league, alpha })],
|
|
187
|
+
["addWordmarks", (alpha) => a.addWordmarks(o.makeTarget(), [10], [-3], [wa], { league, alpha })],
|
|
188
|
+
["addHeadshots", (alpha) => a.addHeadshots(o.makeTarget(), [10], [-3], [p], { league: hl, alpha })]
|
|
189
|
+
];
|
|
190
|
+
for (const [name, call] of alphaVerbs)
|
|
191
|
+
for (const bad of [-0.1, 1.1]) {
|
|
192
|
+
const why = await notInputError(() => call(bad));
|
|
193
|
+
if (why) fail("rule 8 (alpha)", `${name} with alpha ${bad} must throw InputError, ${why}`);
|
|
194
|
+
}
|
|
195
|
+
const frozen = (v) => deepFreeze(structuredClone(v));
|
|
196
|
+
const inputVerbs = [
|
|
197
|
+
["addLogos", o.makeTarget, (t) => a.addLogos(t, frozen(xs), frozen(ys), frozen([ka, kb]), { league })],
|
|
198
|
+
[
|
|
199
|
+
"addWordmarks",
|
|
200
|
+
o.makeTarget,
|
|
201
|
+
(t) => a.addWordmarks(t, frozen(xs), frozen(ys), frozen([wa, wb]), { league })
|
|
202
|
+
],
|
|
203
|
+
[
|
|
204
|
+
"addHeadshots",
|
|
205
|
+
o.makeTarget,
|
|
206
|
+
(t) => a.addHeadshots(t, frozen(xs), frozen(ys), frozen([p, q]), { league: hl })
|
|
207
|
+
]
|
|
208
|
+
];
|
|
209
|
+
const mk = o.makeAxisTarget;
|
|
210
|
+
if (a.supportsAxisLogos && mk)
|
|
211
|
+
inputVerbs.push(["axisLogos", () => mk([ka, kb]), (t) => a.axisLogos(t, "x", { league })]);
|
|
212
|
+
for (const [name, make, call] of inputVerbs) {
|
|
213
|
+
const t = deepFreeze(make());
|
|
214
|
+
const before = snapshot(t);
|
|
215
|
+
try {
|
|
216
|
+
await call(t);
|
|
217
|
+
} catch (e) {
|
|
218
|
+
fail(
|
|
219
|
+
"rule 9 (no input mutation)",
|
|
220
|
+
`${name} threw on a frozen input: ${e instanceof Error ? e.message : String(e)}`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
if (snapshot(t) !== before) fail("rule 9 (no input mutation)", `${name} changed its input target`);
|
|
224
|
+
}
|
|
225
|
+
setWarningHandler(null);
|
|
226
|
+
}
|
|
227
|
+
function drawnAxisMarks(node, axis) {
|
|
228
|
+
return Array.from(node.querySelectorAll(`image[data-sdv-axis="${axis}"]`)).map((img) => ({
|
|
229
|
+
id: img.getAttribute("data-sdv-id") ?? "",
|
|
230
|
+
tick: Number(img.getAttribute("data-sdv-tick")),
|
|
231
|
+
height: Number(img.getAttribute("height")) / Number(img.getAttribute("data-sdv-frame"))
|
|
232
|
+
})).sort((a, b) => a.tick - b.tick);
|
|
233
|
+
}
|
|
234
|
+
function visibleAxisLabels(node, axis) {
|
|
235
|
+
return Array.from(node.querySelectorAll(`[aria-label="${axis}-axis tick label"] text`)).map((t) => t.textContent ?? "").filter((s) => s !== "");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export { ContractError, checkAdapterContract, drawnAxisMarks, drawnMarks, visibleAxisLabels };
|
|
239
|
+
//# sourceMappingURL=testing.js.map
|
|
240
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing/index.ts"],"names":["mk"],"mappings":"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","file":"testing.js","sourcesContent":["import {\n ESPN_HEADSHOT_LEAGUES,\n type EspnHeadshotLeague,\n InputError,\n type League,\n UnsupportedTargetError,\n headshotUrl,\n logoUrlSync,\n resetWarnings,\n resolveSync,\n setWarningHandler,\n} from \"../index.js\";\nimport type { Axis } from \"../plot/axis.js\";\nimport type { Value } from \"../resolve.js\";\n\nexport interface DrawnMark {\n id: string;\n x: Value;\n y: Value;\n /** Drawn image height as a fraction of the frame height the render saw. */\n height: number;\n url: string;\n kind: string;\n}\nconst num = (s: string | null): Value =>\n s === null || s === \"\" ? null : Number.isNaN(Number(s)) ? s : Number(s);\n\n/** Every `<image data-sdv-id>` under `node`, in document order. */\nexport function drawnMarks(node: ParentNode): DrawnMark[] {\n return Array.from(node.querySelectorAll(\"image[data-sdv-id]\")).map((img) => ({\n id: img.getAttribute(\"data-sdv-id\") ?? \"\",\n x: num(img.getAttribute(\"data-sdv-x\")),\n y: num(img.getAttribute(\"data-sdv-y\")),\n height: Number(img.getAttribute(\"height\")) / Number(img.getAttribute(\"data-sdv-frame\")),\n url: img.getAttribute(\"href\") ?? \"\",\n kind: img.getAttribute(\"data-sdv-kind\") ?? \"\",\n }));\n}\n\nexport interface ContractMarkOptions {\n league: League;\n height?: number;\n alpha?: number;\n}\n/** Adapters must accept typed arrays (e.g. `Float64Array`) for the x and y positions: rule 3 passes them. */\nexport interface ContractAdapter<T> {\n name: string;\n addLogos(\n target: T,\n x: ArrayLike<Value>,\n y: ArrayLike<Value>,\n teams: readonly Value[],\n o: ContractMarkOptions,\n ): T | Promise<T>;\n addWordmarks(\n target: T,\n x: ArrayLike<Value>,\n y: ArrayLike<Value>,\n teams: readonly Value[],\n o: ContractMarkOptions,\n ): T | Promise<T>;\n /** `league` is narrowed to the ESPN-headshot leagues: the contract checks that before calling, so adapters need no cast. */\n addHeadshots(\n target: T,\n x: ArrayLike<Value>,\n y: ArrayLike<Value>,\n players: readonly Value[],\n o: Omit<ContractMarkOptions, \"league\"> & { league: EspnHeadshotLeague },\n ): T | Promise<T>;\n /** Throws `UnsupportedTargetError` when `supportsAxisLogos` is false. */\n axisLogos(target: T, axis: \"x\" | \"y\", o: { league: League; height?: number }): T | Promise<T>;\n supportsAxisLogos: boolean;\n drawnMarks(target: T): readonly DrawnMark[];\n drawnAxisMarks(target: T, axis: \"x\" | \"y\"): readonly { id: string; tick: number; height: number }[];\n visibleAxisLabels(target: T, axis: \"x\" | \"y\"): readonly string[];\n}\n\n/** `message` starts with \"rule N (...): ...\" so a test can match `/^rule N/`. */\nexport class ContractError extends Error {\n constructor(\n readonly rule: string,\n msg: string,\n ) {\n super(`${rule}: ${msg}`);\n this.name = \"ContractError\";\n }\n}\nconst fail = (rule: string, msg: string): never => {\n throw new ContractError(rule, msg);\n};\nconst xyz = (m: readonly DrawnMark[]): unknown[][] => m.map((d) => [d.id, d.x, d.y]);\nconst same = (a: unknown, b: unknown): boolean => JSON.stringify(a) === JSON.stringify(b);\n\n// There is no getter for the current warning handler, so a previous one cannot be restored:\n// this leaves the default handler (null) installed.\nasync function counted<R>(fn: () => R | Promise<R>): Promise<[R, number]> {\n let n = 0;\n resetWarnings();\n setWarningHandler(() => {\n n++;\n });\n try {\n return [await fn(), n];\n } finally {\n setWarningHandler(null);\n }\n}\n/** Plain data: an array, or an object whose prototype is Object.prototype or null (not a class instance or DOM node). */\nconst plain = (v: unknown): v is object =>\n Array.isArray(v) ||\n (typeof v === \"object\" && v !== null && [Object.prototype, null].includes(Object.getPrototypeOf(v)));\n/** Freezes the plain-data part of `v` (plain objects and arrays, recursively); class instances are left alone. */\nfunction deepFreeze<V>(v: V): V {\n if (plain(v) && !Object.isFrozen(v)) {\n Object.freeze(v);\n for (const x of Object.values(v)) deepFreeze(x);\n }\n return v;\n}\n/** The plain-data part of `v` as JSON, class instances as \"[object]\". */\nconst snapshot = (v: unknown): string =>\n JSON.stringify(v, (_k, x: unknown) => (typeof x === \"object\" && x !== null && !plain(x) ? \"[object]\" : x));\n\n/** null when `fn` threw InputError; otherwise what went wrong, for the failure message. */\nasync function notInputError(fn: () => unknown): Promise<string | null> {\n try {\n await fn();\n } catch (e) {\n if (e instanceof InputError) return null;\n return `threw ${e instanceof Error ? `${e.name}: ${e.message}` : String(e)}`;\n }\n return \"did not throw\";\n}\n\nexport interface ContractOptions<T> {\n makeTarget: () => T;\n makeAxisTarget?: (categories: readonly string[]) => T;\n league?: League;\n known?: readonly [string, string];\n knownWordmarks?: readonly [string, string];\n players?: readonly [string, string];\n}\n\n/**\n * Port of Python `sdvplot.testing.check_adapter_contract` (rules 0-8), plus rule 9: no verb mutates its input (the\n * target's plain-data part and the position and value arrays, deep-frozen). Rejects with a `ContractError`\n * naming the first rule broken. Warnings are once per call (one message listing every unresolved value).\n * Resets the warning handler to null (the default) when done: the previous handler cannot be restored.\n */\nexport async function checkAdapterContract<T>(a: ContractAdapter<T>, o: ContractOptions<T>): Promise<void> {\n const league = o.league ?? \"nfl\";\n const [ka, kb] = o.known ?? [\"LV\", \"LAR\"];\n const [wa, wb] = o.knownWordmarks ?? [\"LV\", \"LAC\"];\n const [p, q] = o.players ?? [\"3139477\", \"4241479\"];\n const xs = [10, 20];\n const ys = [-3, -7];\n // rule 0: shape (JS has no dispatch front door)\n for (const k of [\n \"addLogos\",\n \"addWordmarks\",\n \"addHeadshots\",\n \"axisLogos\",\n \"drawnMarks\",\n \"drawnAxisMarks\",\n \"visibleAxisLabels\",\n ] as const)\n if (typeof a[k] !== \"function\") fail(\"rule 0 (shape)\", `${a.name} lacks ${k}`);\n if (typeof a.supportsAxisLogos !== \"boolean\") fail(\"rule 0 (shape)\", \"supportsAxisLogos must be boolean\");\n\n type Verb = (\n t: T,\n x: ArrayLike<Value>,\n y: ArrayLike<Value>,\n v: readonly Value[],\n opts: { height?: number; alpha?: number },\n ) => T | Promise<T>;\n // rules 1, 2, 4 for one image verb; wordmarks (5) and headshots (6) reuse it with their own rule labels\n const checkVerb = async (\n verb: Verb,\n vals: readonly [string, string],\n ids: readonly [string, string],\n urls: readonly [string | undefined, string | undefined],\n r: readonly [string, string, string],\n ): Promise<void> => {\n const want = [\n [ids[0], 10, -3],\n [ids[1], 20, -7],\n ];\n const [t1, n1] = await counted(() => verb(o.makeTarget(), xs, ys, vals, {}));\n const m1 = a.drawnMarks(t1);\n if (!same(xyz(m1), want)) fail(r[0], `expected ${JSON.stringify(want)}, drew ${JSON.stringify(xyz(m1))}`);\n if (n1 !== 0) fail(r[0], `known values warned ${n1} time(s)`);\n m1.forEach((d, i) => {\n const u = urls[i];\n if (u && d.url !== u) fail(r[0], `${d.id} drew ${d.url}, its mark is ${u}`);\n });\n const [t2, n2] = await counted(() => verb(o.makeTarget(), xs, ys, [\"XXX\", vals[1]], {}));\n if (!same(xyz(a.drawnMarks(t2)), [want[1]]))\n fail(r[1], \"an unknown value must be skipped with its own x/y\");\n if (n2 !== 1) fail(r[1], `one unknown value must warn exactly once, warned ${n2}`);\n const [, n3] = await counted(() =>\n verb(o.makeTarget(), [1, 2, 3], [1, 2, 3], [\"XXX\", \"YYY\", vals[0]], {}),\n );\n if (n3 !== 1)\n fail(\n r[1],\n `two distinct unknown values must warn exactly once per call (one message listing both), warned ${n3}`,\n );\n for (const h of [0.1, 0.25, 1]) {\n const t = await verb(o.makeTarget(), xs, ys, vals, { height: h });\n for (const d of a.drawnMarks(t))\n if (Math.abs(d.height - h) / h > 0.01) fail(r[2], `asked height ${h}, drew ${d.height}`);\n }\n for (const bad of [0, 1.5, 40]) {\n const why = await notInputError(() => verb(o.makeTarget(), xs, ys, vals, { height: bad }));\n if (why) fail(r[2], `height ${bad} must throw InputError when the verb is called, ${why}`);\n }\n };\n const [ia = \"\", ib = \"\"] = resolveSync([ka, kb], league);\n await checkVerb(\n (t, x, y, v, opts) => a.addLogos(t, x, y, v, { league, ...opts }),\n [ka, kb],\n [ia, ib],\n [logoUrlSync(ka, league), logoUrlSync(kb, league)],\n [\"rule 1 (resolution)\", \"rule 2 (unknown team: warn and skip)\", \"rule 4 (height semantics)\"],\n );\n // rule 3: columnar parity (real Float64Array positions draw what plain arrays draw)\n const tA = await a.addLogos(o.makeTarget(), xs, ys, [ka, kb], { league });\n const tB = await a.addLogos(o.makeTarget(), new Float64Array(xs), new Float64Array(ys), [ka, kb], {\n league,\n });\n if (!same(xyz(a.drawnMarks(tA)), xyz(a.drawnMarks(tB))))\n fail(\"rule 3 (columnar parity)\", \"typed-array positions drew different marks\");\n // rule 5: wordmarks\n const [wia = \"\", wib = \"\"] = resolveSync([wa, wb], league);\n const wm = (t: string): string | undefined => logoUrlSync(t, league, { markType: \"wordmark\" });\n await checkVerb(\n (t, x, y, v, opts) => a.addWordmarks(t, x, y, v, { league, ...opts }),\n [wa, wb],\n [wia, wib],\n [wm(wa), wm(wb)],\n [\"rule 5 (wordmarks: resolution)\", \"rule 5 (wordmarks: warn and skip)\", \"rule 5 (wordmarks: height)\"],\n );\n // rule 6: headshots (ESPN leagues only; the narrowing keeps adapters cast-free)\n if (!Object.hasOwn(ESPN_HEADSHOT_LEAGUES, league))\n fail(\n \"rule 6 (headshots)\",\n `league ${JSON.stringify(league)} has no ESPN headshots; pass an ESPN headshot league`,\n );\n const hl = league as EspnHeadshotLeague;\n await checkVerb(\n (t, x, y, v, opts) => a.addHeadshots(t, x, y, v, { league: hl, ...opts }),\n [p, q],\n [p, q],\n [headshotUrl(p, hl), headshotUrl(q, hl)],\n [\"rule 6 (headshots)\", \"rule 6 (headshots: unknown id)\", \"rule 6 (headshots: height)\"],\n );\n // rule 7: axis logos\n const r7 = \"rule 7 (axis logos)\";\n if (a.supportsAxisLogos) {\n const mk = o.makeAxisTarget;\n if (!mk) return fail(r7, \"makeAxisTarget is required for an adapter that supports axis logos\");\n // an adapter may resolve at construction or only when the target renders: count across both\n const [[marks, shown], n] = await counted(async () => {\n const t = await a.axisLogos(mk([ka, \"XXX\", kb]), \"x\", { league });\n return [a.drawnAxisMarks(t, \"x\"), a.visibleAxisLabels(t, \"x\")] as const;\n });\n if (\n !same(\n marks.map((m) => m.id),\n [ia, ib],\n )\n )\n fail(r7, `expected axis images for [${ia}, ${ib}] in tick order, drew ${JSON.stringify(marks)}`);\n if (!((marks[0]?.tick ?? 0) < (marks[1]?.tick ?? 0)))\n fail(r7, \"axis images must sit at increasing tick positions\");\n if (n !== 1) fail(r7, `an unknown axis category must warn exactly once, warned ${n}`);\n if (!shown.includes(\"XXX\") || shown.includes(ka) || shown.includes(kb))\n fail(r7, `only the unknown category may stay as text, visible labels are ${JSON.stringify(shown)}`);\n for (const h of [0.1, 0.25])\n for (const m of a.drawnAxisMarks(await a.axisLogos(mk([ka, kb]), \"x\", { league, height: h }), \"x\"))\n if (Math.abs(m.height - h) / h > 0.01) fail(`${r7} (height)`, `asked ${h}, drew ${m.height}`);\n } else {\n let threw = false;\n try {\n await a.axisLogos(o.makeTarget(), \"x\", { league });\n } catch (e) {\n threw = e instanceof UnsupportedTargetError;\n }\n if (!threw)\n fail(r7, \"an adapter without axis-logo support must throw UnsupportedTargetError from axisLogos\");\n }\n // rule 8: alpha outside [0, 1] throws on every verb\n const alphaVerbs: [string, (alpha: number) => unknown][] = [\n [\"addLogos\", (alpha) => a.addLogos(o.makeTarget(), [10], [-3], [ka], { league, alpha })],\n [\"addWordmarks\", (alpha) => a.addWordmarks(o.makeTarget(), [10], [-3], [wa], { league, alpha })],\n [\"addHeadshots\", (alpha) => a.addHeadshots(o.makeTarget(), [10], [-3], [p], { league: hl, alpha })],\n ];\n for (const [name, call] of alphaVerbs)\n for (const bad of [-0.1, 1.1]) {\n const why = await notInputError(() => call(bad));\n if (why) fail(\"rule 8 (alpha)\", `${name} with alpha ${bad} must throw InputError, ${why}`);\n }\n // rule 9: no input mutation. A write into a frozen input throws (strict mode); the snapshot catches the rest.\n const frozen = <V>(v: V): V => deepFreeze(structuredClone(v));\n const inputVerbs: [string, () => T, (t: T) => unknown][] = [\n [\"addLogos\", o.makeTarget, (t) => a.addLogos(t, frozen(xs), frozen(ys), frozen([ka, kb]), { league })],\n [\n \"addWordmarks\",\n o.makeTarget,\n (t) => a.addWordmarks(t, frozen(xs), frozen(ys), frozen([wa, wb]), { league }),\n ],\n [\n \"addHeadshots\",\n o.makeTarget,\n (t) => a.addHeadshots(t, frozen(xs), frozen(ys), frozen([p, q]), { league: hl }),\n ],\n ];\n const mk = o.makeAxisTarget;\n if (a.supportsAxisLogos && mk)\n inputVerbs.push([\"axisLogos\", () => mk([ka, kb]), (t) => a.axisLogos(t, \"x\", { league })]);\n for (const [name, make, call] of inputVerbs) {\n const t = deepFreeze(make());\n const before = snapshot(t);\n try {\n await call(t);\n } catch (e) {\n fail(\n \"rule 9 (no input mutation)\",\n `${name} threw on a frozen input: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n if (snapshot(t) !== before) fail(\"rule 9 (no input mutation)\", `${name} changed its input target`);\n }\n setWarningHandler(null);\n}\n\n/** Axis images (`<image data-sdv-axis>`), in tick-position order; `height` is a fraction of the frame. */\nexport function drawnAxisMarks(node: ParentNode, axis: Axis): { id: string; tick: number; height: number }[] {\n return Array.from(node.querySelectorAll(`image[data-sdv-axis=\"${axis}\"]`))\n .map((img) => ({\n id: img.getAttribute(\"data-sdv-id\") ?? \"\",\n tick: Number(img.getAttribute(\"data-sdv-tick\")),\n height: Number(img.getAttribute(\"height\")) / Number(img.getAttribute(\"data-sdv-frame\")),\n }))\n .sort((a, b) => a.tick - b.tick);\n}\n/** Non-empty tick `<text>` still on the axis (categories that did not resolve to an image). */\nexport function visibleAxisLabels(node: ParentNode, axis: Axis): string[] {\n return Array.from(node.querySelectorAll(`[aria-label=\"${axis}-axis tick label\"] text`))\n .map((t) => t.textContent ?? \"\")\n .filter((s) => s !== \"\");\n}\n"]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { L as League, S as SeasonInput, V as Variant, I as IdSystem, H as HeadshotIdSystem, a as MarkRow, T as TeamId } from './types-C7n6gJF3.js';
|
|
2
|
+
import { V as Value } from './resolve-CQW-jzvr.js';
|
|
3
|
+
|
|
4
|
+
type Kind = "logo" | "wordmark" | "headshot";
|
|
5
|
+
/** One point to draw: the caller's own x/y, the image URL and its width / height ratio (null when unknown). `index` is the row's position in the input arrays (skipped rows leave gaps). */
|
|
6
|
+
interface Placement {
|
|
7
|
+
index: number;
|
|
8
|
+
id: string;
|
|
9
|
+
x: Value;
|
|
10
|
+
y: Value;
|
|
11
|
+
url: string;
|
|
12
|
+
aspect: number | null;
|
|
13
|
+
mark: MarkRow | null;
|
|
14
|
+
}
|
|
15
|
+
interface PlaceOptions {
|
|
16
|
+
league: League;
|
|
17
|
+
season?: SeasonInput | readonly SeasonInput[];
|
|
18
|
+
kind?: Kind;
|
|
19
|
+
variant?: Variant;
|
|
20
|
+
/** Team id system for logos/wordmarks (default "auto"); "espn" or "gsis" for headshots (default "espn"). */
|
|
21
|
+
idSystem?: IdSystem | HeadshotIdSystem;
|
|
22
|
+
strict?: boolean;
|
|
23
|
+
warn?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
declare function checkHeight(height: unknown): number;
|
|
27
|
+
declare function checkAlpha(alpha: unknown): number;
|
|
28
|
+
/** Port of `_placement.place` (post-preload): pairs each (x, y) with its team's mark or player's headshot, dropping and warning (once per reason) on rows that cannot be drawn. Needs `loadLeague(league)` (or `preloadAll()`) first; gsis headshots also need `loadGsis()`. */
|
|
29
|
+
declare function placeSync(xs: readonly Value[], ys: readonly Value[], teams: readonly Value[], o: PlaceOptions): Placement[];
|
|
30
|
+
declare function place(xs: readonly Value[], ys: readonly Value[], teams: readonly Value[], o: PlaceOptions): Promise<Placement[]>;
|
|
31
|
+
|
|
32
|
+
declare const TIER_DESC: Readonly<Record<number, string>>;
|
|
33
|
+
declare const TIERS_SUBTITLE = "created with the #sdvplot Tiermaker";
|
|
34
|
+
declare const TIER_THEMES: {
|
|
35
|
+
readonly dark: {
|
|
36
|
+
readonly bg: "#1e1e1e";
|
|
37
|
+
readonly line: "#e0e0e0";
|
|
38
|
+
readonly text: "#ffffff";
|
|
39
|
+
readonly muted: "#8e8e93";
|
|
40
|
+
};
|
|
41
|
+
readonly light: {
|
|
42
|
+
readonly bg: "#ffffff";
|
|
43
|
+
readonly line: "#3a3a3c";
|
|
44
|
+
readonly text: "#1e1e1e";
|
|
45
|
+
readonly muted: "#636366";
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
interface TierRow {
|
|
49
|
+
tierNo?: Value;
|
|
50
|
+
tier_no?: Value;
|
|
51
|
+
team: Value;
|
|
52
|
+
tierRank?: Value;
|
|
53
|
+
tier_rank?: Value;
|
|
54
|
+
}
|
|
55
|
+
interface TiersOptions {
|
|
56
|
+
/** `undefined` gives the league name plus "Team Tiers"; `null` or an empty string gives no title. */
|
|
57
|
+
title?: string | null;
|
|
58
|
+
/** `undefined` gives the Tiermaker line; `null` or an empty string gives none. */
|
|
59
|
+
subtitle?: string | null;
|
|
60
|
+
caption?: string | null;
|
|
61
|
+
tierDesc?: Readonly<Record<number, string>>;
|
|
62
|
+
presort?: boolean;
|
|
63
|
+
alpha?: number;
|
|
64
|
+
/** Fraction of the plot height in (0, 1]; default 0.1. */
|
|
65
|
+
height?: number;
|
|
66
|
+
noLineBelowTier?: number | readonly number[];
|
|
67
|
+
theme?: "dark" | "light";
|
|
68
|
+
variant?: Variant | "auto";
|
|
69
|
+
}
|
|
70
|
+
interface Tiers {
|
|
71
|
+
x: number[];
|
|
72
|
+
y: number[];
|
|
73
|
+
teamIds: TeamId[];
|
|
74
|
+
labels: string[];
|
|
75
|
+
lines: number[];
|
|
76
|
+
breaks: number[];
|
|
77
|
+
breakLabels: string[];
|
|
78
|
+
xlim: [number, number];
|
|
79
|
+
ylim: [number, number];
|
|
80
|
+
title: string;
|
|
81
|
+
subtitle: string | null;
|
|
82
|
+
caption: string | null;
|
|
83
|
+
height: number;
|
|
84
|
+
alpha: number;
|
|
85
|
+
variant: Variant;
|
|
86
|
+
theme: (typeof TIER_THEMES)[keyof typeof TIER_THEMES];
|
|
87
|
+
}
|
|
88
|
+
/** `textwrap.wrap(text, 14, break_long_words=False, break_on_hyphens=False)` joined by newlines: lines of at most 14 characters, a longer word kept whole on its own line. */
|
|
89
|
+
declare function wrapLabel(text: string): string;
|
|
90
|
+
/** Port of `sdvplot._tiers.prepare`: validate the rows and compute everything a tier plot draws. */
|
|
91
|
+
declare function prepareTiers(rows: readonly TierRow[], league: League, o?: TiersOptions): Tiers;
|
|
92
|
+
|
|
93
|
+
export { type Kind as K, type Placement as P, type TiersOptions as T, type TierRow as a, type PlaceOptions as b, TIERS_SUBTITLE as c, TIER_DESC as d, TIER_THEMES as e, type Tiers as f, checkAlpha as g, checkHeight as h, placeSync as i, prepareTiers as j, place as p, wrapLabel as w };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
declare const LEAGUES: readonly ["aaf", "ahl", "cfb", "cricket", "echl", "mbb", "milb", "mlb", "nba", "nbagl", "ncaa_baseball", "ncaa_mhockey", "ncaa_softball", "ncaa_whockey", "nfl", "nhl", "ohl", "phf", "pwhl", "qmjhl", "soccer", "ufl", "usfl", "ushl", "wbb", "whl", "wnba", "xfl"];
|
|
2
|
+
type League = (typeof LEAGUES)[number];
|
|
3
|
+
declare const INDEX_VERSION: string;
|
|
4
|
+
declare const VARIANTS: readonly ["alternate", "cap_on_dark", "cap_on_light", "cricinfo", "crop_64px", "dark", "default", "grayscale", "large", "logo2", "medium", "on_dark", "on_light", "primary_logo_black", "primary_logo_on_black_color", "primary_logo_on_primary_color", "primary_logo_on_secondary_color", "primary_logo_on_white_color", "primary_logo_white", "primary_on_dark", "primary_on_light", "scoreboard", "scoreboard_dark", "secondary_logo_black", "secondary_logo_on_black_color", "secondary_logo_on_primary_color", "secondary_logo_on_secondary_color", "secondary_logo_on_white_color", "secondary_logo_white", "sport_pictogram", "squared", "team_strip"];
|
|
5
|
+
interface Team {
|
|
6
|
+
league: League;
|
|
7
|
+
team_id: string;
|
|
8
|
+
abbr: string | null;
|
|
9
|
+
name: string | null;
|
|
10
|
+
short_name: string | null;
|
|
11
|
+
location: string | null;
|
|
12
|
+
program: string | null;
|
|
13
|
+
conference_id: string | null;
|
|
14
|
+
conference: string | null;
|
|
15
|
+
color_primary: string | null;
|
|
16
|
+
color_secondary: string | null;
|
|
17
|
+
color_source: string | null;
|
|
18
|
+
}
|
|
19
|
+
interface Alias {
|
|
20
|
+
id_system: string;
|
|
21
|
+
value: string;
|
|
22
|
+
team_id: string;
|
|
23
|
+
valid_from: number | null;
|
|
24
|
+
valid_to: number | null;
|
|
25
|
+
}
|
|
26
|
+
interface MarkRow {
|
|
27
|
+
team_id: string;
|
|
28
|
+
mark_type: "logo" | "wordmark";
|
|
29
|
+
variant: string;
|
|
30
|
+
valid_from: number | null;
|
|
31
|
+
valid_to: number | null;
|
|
32
|
+
source: string;
|
|
33
|
+
source_rank: number;
|
|
34
|
+
first_seen: string;
|
|
35
|
+
sha256: string;
|
|
36
|
+
ext: string;
|
|
37
|
+
width: number | null;
|
|
38
|
+
height: number | null;
|
|
39
|
+
archive_url: string;
|
|
40
|
+
}
|
|
41
|
+
interface LeagueData {
|
|
42
|
+
teams: readonly Team[];
|
|
43
|
+
aliases: readonly Alias[];
|
|
44
|
+
marks: readonly MarkRow[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
declare const brand: unique symbol;
|
|
48
|
+
/** A canonical SDV team id. Only `resolve()` makes one; a raw abbreviation is a `string`, not a `TeamId`. */
|
|
49
|
+
type TeamId = string & {
|
|
50
|
+
readonly [brand]: "TeamId";
|
|
51
|
+
};
|
|
52
|
+
declare const PRIORITY: readonly ["team_id", "espn", "espn_abbr", "nhl", "nflverse", "mlbstats", "nba_api", "hockeytech", "ncaa", "pff", "cricinfo", "cfbd", "bref", "sportsipy", "fangraphs", "sdvplotr", "name"];
|
|
53
|
+
declare const EXPLICIT_ONLY: readonly ["nhl_id"];
|
|
54
|
+
type IdSystem = "auto" | (typeof PRIORITY)[number] | (typeof EXPLICIT_ONLY)[number];
|
|
55
|
+
type MarkType = "logo" | "wordmark";
|
|
56
|
+
type Which = "primary" | "secondary";
|
|
57
|
+
/** "default" and "dark" always exist; other archive variant names are accepted and checked at runtime. */
|
|
58
|
+
type Variant = "default" | "dark" | (string & {});
|
|
59
|
+
type SeasonInput = number | string | null | undefined;
|
|
60
|
+
type HeadshotIdSystem = "espn" | "gsis";
|
|
61
|
+
|
|
62
|
+
export { type Alias as A, EXPLICIT_ONLY as E, type HeadshotIdSystem as H, type IdSystem as I, type League as L, type MarkType as M, PRIORITY as P, type SeasonInput as S, type TeamId as T, type Variant as V, type Which as W, type MarkRow as a, type LeagueData as b, type Team as c, INDEX_VERSION as d, LEAGUES as e, VARIANTS as f };
|