@mxfb/tools 0.2.3 → 1.0.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/agnostic/scrapers/@meteociel/index.d.ts +36 -0
- package/agnostic/scrapers/@meteociel/index.js +194 -0
- package/agnostic/scrapers/@meteociel/index.test.d.ts +1 -0
- package/agnostic/scrapers/@meteociel/locations/index.d.ts +11 -0
- package/agnostic/scrapers/@meteociel/locations/index.js +6 -0
- package/agnostic/scrapers/@meteociel/types.d.ts +1 -0
- package/agnostic/weather/types.d.ts +181 -0
- package/agnostic/weather/types.js +0 -0
- package/chunks/chunk-JQLUR5LO.js +12 -0
- package/components/Comp/index.d.ts +1 -0
- package/components/Comp/index.js +0 -0
- package/node/index.d.ts +1 -2
- package/node/index.js +0 -0
- package/package.json +45 -22
- package/agnostic/_WIP_tennis/index.d.ts +0 -2
- package/agnostic/_WIP_tennis/index.js +0 -62
- package/agnostic/index.d.ts +0 -4
- package/agnostic/misc/elo/index.d.ts +0 -31
- package/agnostic/misc/elo/index.js +0 -6
- package/agnostic/misc/index.d.ts +0 -6
- package/agnostic/misc/index.js +0 -16
- package/agnostic/misc/tennis/index.d.ts +0 -60
- package/agnostic/misc/tennis/index.js +0 -6
- package/chunks/chunk-TTZHPIH7.js +0 -237
- package/chunks/chunk-ZC5RZBNY.js +0 -33
- package/components/index.d.ts +0 -1
- package/node/mxfb-cli-config/index.d.ts +0 -19
- package/node/mxfb-cli-config/index.js +0 -113
- package/shared/errors/index.d.ts +0 -138
- /package/{shared/index.d.ts → agnostic/scrapers/@meteociel/types.js} +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type CityName } from './locations/index.js';
|
|
2
|
+
import type { WeatherForecast } from '../../weather/types.js';
|
|
3
|
+
import type { ForecastMode } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Builds the Meteociel forecast page URL for a given city and forecast mode.
|
|
6
|
+
*
|
|
7
|
+
* @param cityName - The city identifier used to resolve Meteociel location data.
|
|
8
|
+
* @param mode - The forecast mode to use when building the URL.
|
|
9
|
+
* @returns The fully qualified Meteociel URL for the requested city and mode.
|
|
10
|
+
* @throws If the provided forecast mode is not supported.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getCityAndModeUrl(cityName: CityName, mode: ForecastMode): string;
|
|
13
|
+
/**
|
|
14
|
+
* Fetches a remote HTML page and decodes it using ISO-8859-1 encoding.
|
|
15
|
+
*
|
|
16
|
+
* @param url - The URL of the page to fetch.
|
|
17
|
+
* @returns The decoded HTML content of the fetched page.
|
|
18
|
+
*/
|
|
19
|
+
export declare function fetchPage(url: string): Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* Retrieves and scrapes weather forecast data for a given city and forecast mode.
|
|
22
|
+
*
|
|
23
|
+
* @param cityName - The city for which to retrieve weather forecasts.
|
|
24
|
+
* @param mode - The forecast mode determining the scraping strategy.
|
|
25
|
+
* @returns An array of weather forecasts enriched with city location metadata.
|
|
26
|
+
* @throws If the forecast mode is unknown or unsupported.
|
|
27
|
+
*/
|
|
28
|
+
export declare function scrap(cityName: CityName, mode: ForecastMode): Promise<WeatherForecast[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Scrapes 3-day GFS weather simple forecasts from a Meteociel HTML page.
|
|
31
|
+
*
|
|
32
|
+
* @param pageContent - The raw HTML content of the Meteociel forecast page.
|
|
33
|
+
* @returns An array of weather forecasts parsed from the page content.
|
|
34
|
+
* @throws If required data such as issue time or geolocation cannot be extracted.
|
|
35
|
+
*/
|
|
36
|
+
export declare function scrapSimpleGfsPrevisions(pageContent: string): WeatherForecast[];
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import {
|
|
2
|
+
citiesData
|
|
3
|
+
} from "../../../chunks/chunk-JQLUR5LO.js";
|
|
4
|
+
|
|
5
|
+
// src/agnostic/scrapers/@meteociel/index.ts
|
|
6
|
+
import { JSDOM } from "jsdom";
|
|
7
|
+
function getCityAndModeUrl(cityName, mode) {
|
|
8
|
+
const modeUrlChunk = mode === "simple-gfs" ? "previsions" : null;
|
|
9
|
+
if (modeUrlChunk === null) throw "invalid mode";
|
|
10
|
+
return `https://www.meteociel.fr/${modeUrlChunk}/${citiesData[cityName].id}/${citiesData[cityName].urlSuffix}.htm`;
|
|
11
|
+
}
|
|
12
|
+
async function fetchPage(url) {
|
|
13
|
+
const fetched = await fetch(url);
|
|
14
|
+
const buffer = await fetched.arrayBuffer();
|
|
15
|
+
const html = new TextDecoder("iso-8859-1").decode(buffer);
|
|
16
|
+
return html;
|
|
17
|
+
}
|
|
18
|
+
async function scrap(cityName, mode) {
|
|
19
|
+
const url = getCityAndModeUrl(cityName, mode);
|
|
20
|
+
const fetched = await fetchPage(url);
|
|
21
|
+
switch (mode) {
|
|
22
|
+
case "simple-gfs": {
|
|
23
|
+
const scraped = scrapSimpleGfsPrevisions(fetched);
|
|
24
|
+
return scraped.map((i) => ({
|
|
25
|
+
...i,
|
|
26
|
+
location: {
|
|
27
|
+
...i.location,
|
|
28
|
+
name: cityName
|
|
29
|
+
}
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
default:
|
|
33
|
+
throw "unknown-mode";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function scrapSimpleGfsPrevisions(pageContent) {
|
|
37
|
+
const dom = new JSDOM(pageContent);
|
|
38
|
+
const { document } = dom.window;
|
|
39
|
+
const COLLECTED_ON = /* @__PURE__ */ new Date();
|
|
40
|
+
const h3Parent = Array.from(document.querySelectorAll("h3")).find((n) => n.textContent.includes("Pr\xE9visions m\xE9t\xE9o \xE0 3 jours"))?.parentElement;
|
|
41
|
+
const issueTimeNode = Array.from(h3Parent?.childNodes ?? []).find((n) => n.textContent?.includes("R\xE9actualis\xE9 \xE0"));
|
|
42
|
+
const dataTableNode = Array.from(document.querySelectorAll("tbody")).find((n) => {
|
|
43
|
+
const trChldCnt = n.querySelectorAll("tr").length;
|
|
44
|
+
return trChldCnt > 25 && trChldCnt < 35;
|
|
45
|
+
});
|
|
46
|
+
const forecastTableRows = Array.from(dataTableNode?.querySelectorAll("tr") ?? []).slice(2);
|
|
47
|
+
const locationNode = Array.from(
|
|
48
|
+
Array.from(document.querySelectorAll("p")).find((n) => n.textContent.includes("sont exp\xE9rimentales et d\xE9termin\xE9es \xE0 partir des donn\xE9es brutes de GFS, un mod\xE8le num\xE9rique am\xE9ricain"))?.childNodes ?? []
|
|
49
|
+
).find((n) => n.textContent?.startsWith("Localisation de"));
|
|
50
|
+
const issuedOnRaw = issueTimeNode?.textContent?.match(/\b([0-9]{1,2}):([0-5][0-9])\b/)?.[0];
|
|
51
|
+
if (issuedOnRaw === void 0) throw "no-refresh-time-found";
|
|
52
|
+
const [issuedOnH, issuedOnM] = issuedOnRaw.split(":");
|
|
53
|
+
const issuedOnYesteredayMaybe = /* @__PURE__ */ new Date();
|
|
54
|
+
issuedOnYesteredayMaybe.setDate((/* @__PURE__ */ new Date()).getDate() - 1);
|
|
55
|
+
issuedOnYesteredayMaybe.setHours(parseInt(issuedOnH));
|
|
56
|
+
issuedOnYesteredayMaybe.setMinutes(parseInt(issuedOnM));
|
|
57
|
+
issuedOnYesteredayMaybe.setSeconds(0);
|
|
58
|
+
issuedOnYesteredayMaybe.setMilliseconds(0);
|
|
59
|
+
const issuedOnYesteredayMaybeNowDiff = Math.abs((/* @__PURE__ */ new Date()).getTime() - issuedOnYesteredayMaybe.getTime());
|
|
60
|
+
const issuedOnTodayMaybe = /* @__PURE__ */ new Date();
|
|
61
|
+
issuedOnTodayMaybe.setHours(parseInt(issuedOnH));
|
|
62
|
+
issuedOnTodayMaybe.setMinutes(parseInt(issuedOnM));
|
|
63
|
+
issuedOnTodayMaybe.setSeconds(0);
|
|
64
|
+
issuedOnTodayMaybe.setMilliseconds(0);
|
|
65
|
+
const issuedOnTodayMaybeNowDiff = Math.abs((/* @__PURE__ */ new Date()).getTime() - issuedOnTodayMaybe.getTime());
|
|
66
|
+
const ISSUED_ON = issuedOnYesteredayMaybeNowDiff > issuedOnTodayMaybeNowDiff ? issuedOnTodayMaybe : issuedOnYesteredayMaybe;
|
|
67
|
+
function parseLocation(input) {
|
|
68
|
+
const regex = /Latitude\s+([NS])\s+(\d+)°(\d+)'\s+\/\s+Longitude\s+([EW])\s+(\d+)°(\d+)'\s+\/\s+Altitude\s*:\s*(\d+)\s*m/;
|
|
69
|
+
const match = input.match(regex);
|
|
70
|
+
if (!match) throw new Error("Format de coordonn\xE9es invalide");
|
|
71
|
+
const [, latDir, latDeg, latMin, lonDir, lonDeg, lonMin, altStr] = match;
|
|
72
|
+
let lat = Number(latDeg) + Number(latMin) / 60;
|
|
73
|
+
let lon = Number(lonDeg) + Number(lonMin) / 60;
|
|
74
|
+
if (latDir === "S") lat = -1 * lat;
|
|
75
|
+
if (lonDir === "W") lon = -1 * lon;
|
|
76
|
+
const alt = Number(altStr);
|
|
77
|
+
return {
|
|
78
|
+
latitudeDeg: lat,
|
|
79
|
+
longitudeDeg: lon,
|
|
80
|
+
elevationM: alt
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
let LOCATION;
|
|
84
|
+
try {
|
|
85
|
+
LOCATION = parseLocation(locationNode?.textContent ?? "");
|
|
86
|
+
} catch (err) {
|
|
87
|
+
throw "no-geolocation-found";
|
|
88
|
+
}
|
|
89
|
+
let currentDateLabel = null;
|
|
90
|
+
const currentHour = /* @__PURE__ */ new Date();
|
|
91
|
+
currentHour.setMinutes(0);
|
|
92
|
+
currentHour.setSeconds(0);
|
|
93
|
+
currentHour.setMilliseconds(0);
|
|
94
|
+
let RETURNED = [];
|
|
95
|
+
for (const row of forecastTableRows) {
|
|
96
|
+
const cells = Array.from(row.querySelectorAll("td"));
|
|
97
|
+
if (cells.length < 10 || cells.length > 11) continue;
|
|
98
|
+
let dateCell = void 0;
|
|
99
|
+
let otherCells = [];
|
|
100
|
+
if (cells.length === 11) {
|
|
101
|
+
dateCell = cells[0];
|
|
102
|
+
otherCells.push(...cells.slice(1));
|
|
103
|
+
} else {
|
|
104
|
+
otherCells.push(...cells);
|
|
105
|
+
}
|
|
106
|
+
if (dateCell !== void 0) {
|
|
107
|
+
currentDateLabel = dateCell.textContent;
|
|
108
|
+
}
|
|
109
|
+
const [
|
|
110
|
+
hourCell,
|
|
111
|
+
tempCell,
|
|
112
|
+
_emptyCell1,
|
|
113
|
+
windDirCell,
|
|
114
|
+
windSpeedCell,
|
|
115
|
+
gustSpeedCell,
|
|
116
|
+
rainAmountCell,
|
|
117
|
+
hygrometryCell,
|
|
118
|
+
airPressureCell,
|
|
119
|
+
weatherCell
|
|
120
|
+
] = otherCells;
|
|
121
|
+
const hour = hourCell?.textContent ?? null;
|
|
122
|
+
const temp = tempCell?.textContent ?? null;
|
|
123
|
+
const windDir = windDirCell?.querySelector("img")?.getAttribute("title") ?? null;
|
|
124
|
+
const [windDirName = null, windDirAngle = null] = windDir?.split(" : ") ?? [];
|
|
125
|
+
const windSpeed = windSpeedCell?.textContent ?? null;
|
|
126
|
+
const gustSpeed = gustSpeedCell?.textContent ?? null;
|
|
127
|
+
const rainAmount = rainAmountCell?.textContent === "--" ? null : rainAmountCell?.textContent ?? null;
|
|
128
|
+
const hygrometry = hygrometryCell?.textContent ?? null;
|
|
129
|
+
const airPressure = airPressureCell?.textContent ?? null;
|
|
130
|
+
const weather = weatherCell?.querySelector("img")?.getAttribute("title") ?? null;
|
|
131
|
+
const rawDay = currentDateLabel;
|
|
132
|
+
const foundDate = rawDay?.slice(3) ?? "0";
|
|
133
|
+
const pMonthFullDate = new Date(currentHour);
|
|
134
|
+
pMonthFullDate.setMonth(currentHour.getMonth() - 1);
|
|
135
|
+
pMonthFullDate.setDate(parseInt(foundDate));
|
|
136
|
+
pMonthFullDate.setHours(parseInt(hour ?? "00"));
|
|
137
|
+
const curMonthFullDate = new Date(currentHour);
|
|
138
|
+
curMonthFullDate.setDate(parseInt(foundDate));
|
|
139
|
+
curMonthFullDate.setHours(parseInt(hour ?? "00"));
|
|
140
|
+
const nMonthFullDate = new Date(currentHour);
|
|
141
|
+
nMonthFullDate.setMonth(currentHour.getMonth() + 1);
|
|
142
|
+
nMonthFullDate.setDate(parseInt(foundDate));
|
|
143
|
+
nMonthFullDate.setHours(parseInt(hour ?? "00"));
|
|
144
|
+
const eligibleDateTimesDiff = [
|
|
145
|
+
Math.abs(currentHour.getTime() - pMonthFullDate.getTime()),
|
|
146
|
+
Math.abs(currentHour.getTime() - curMonthFullDate.getTime()),
|
|
147
|
+
Math.abs(currentHour.getTime() - nMonthFullDate.getTime())
|
|
148
|
+
];
|
|
149
|
+
const minDateDiff = Math.min(...eligibleDateTimesDiff);
|
|
150
|
+
const indexOfMinDateDiff = eligibleDateTimesDiff.indexOf(minDateDiff);
|
|
151
|
+
const date = indexOfMinDateDiff === 0 ? pMonthFullDate : indexOfMinDateDiff === 1 ? curMonthFullDate : indexOfMinDateDiff === 2 ? nMonthFullDate : currentHour;
|
|
152
|
+
const validityTo = new Date(date);
|
|
153
|
+
validityTo.setHours(date.getHours() + 3);
|
|
154
|
+
const VALIDITY = {
|
|
155
|
+
from: date,
|
|
156
|
+
to: validityTo
|
|
157
|
+
};
|
|
158
|
+
const TEMPERATURE = { tempC: parseFloat(temp?.replace(" \xB0C", "") ?? "") };
|
|
159
|
+
const HUMIDITY = { relativePercent: parseFloat(hygrometry?.replace(" %", "") ?? "") };
|
|
160
|
+
const WIND = {
|
|
161
|
+
speedMs: parseFloat(windSpeed ?? ""),
|
|
162
|
+
directionDeg: parseFloat(windDirAngle?.replace(" \xB0", "") ?? ""),
|
|
163
|
+
gustMs: parseFloat(gustSpeed ?? "")
|
|
164
|
+
};
|
|
165
|
+
const PRESSURE = { stationLevelHpa: parseFloat(airPressure?.replace(" hPa", "") ?? "") };
|
|
166
|
+
const SOURCE = {
|
|
167
|
+
provider: "M\xE9t\xE9ociel",
|
|
168
|
+
type: "model",
|
|
169
|
+
model: "GFS"
|
|
170
|
+
};
|
|
171
|
+
RETURNED.push({
|
|
172
|
+
issuedOn: ISSUED_ON,
|
|
173
|
+
location: LOCATION,
|
|
174
|
+
validity: VALIDITY,
|
|
175
|
+
temperature: TEMPERATURE,
|
|
176
|
+
humidity: HUMIDITY,
|
|
177
|
+
wind: WIND,
|
|
178
|
+
pressure: PRESSURE,
|
|
179
|
+
precipitationMmh: parseFloat(rainAmount ?? ""),
|
|
180
|
+
metadata: {
|
|
181
|
+
source: SOURCE,
|
|
182
|
+
description: weather ?? "unknown",
|
|
183
|
+
collectedOn: COLLECTED_ON
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
return RETURNED;
|
|
188
|
+
}
|
|
189
|
+
export {
|
|
190
|
+
fetchPage,
|
|
191
|
+
getCityAndModeUrl,
|
|
192
|
+
scrap,
|
|
193
|
+
scrapSimpleGfsPrevisions
|
|
194
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type ForecastMode = 'simple-gfs';
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core weather data types for archiving and exchanging meteorological information.
|
|
3
|
+
*
|
|
4
|
+
* The structures are inspired by WMO (World Meteorological Organization) guidance
|
|
5
|
+
* and CF Conventions. Units are encoded explicitly in property names to avoid
|
|
6
|
+
* ambiguity and to make transformations between systems easier and safer.
|
|
7
|
+
*/
|
|
8
|
+
/** Geographic location following the WGS84 reference system. */
|
|
9
|
+
export type GeoLocation = {
|
|
10
|
+
/** Latitude in decimal degrees (-90 to +90). */ latitudeDeg: number;
|
|
11
|
+
/** Longitude in decimal degrees (-180 to +180). */ longitudeDeg: number;
|
|
12
|
+
/** Elevation in meters above mean sea level. */ elevationM?: number;
|
|
13
|
+
/** Human-readable place name (city, station, etc.). */ name?: string;
|
|
14
|
+
/** Country code in ISO 3166-1 alpha-2 format (e.g. "FR", "US"). */ countryCode31661a2?: string;
|
|
15
|
+
/** Administrative region, state, or province name. */ region?: string;
|
|
16
|
+
};
|
|
17
|
+
/** Source of observed meteorological data. */
|
|
18
|
+
export type ObservationDataSource = {
|
|
19
|
+
/** Data provider name */ provider: string;
|
|
20
|
+
/** High-level type of source */ type: 'satellite' | 'radar' | 'station' | 'derived' | 'manual' | 'other';
|
|
21
|
+
/** Identifier of the observing station or sensor when applicable. */ stationId?: string;
|
|
22
|
+
/** URL pointing to the data source or documentation. */ url?: string;
|
|
23
|
+
/** Data license or usage terms (short free-text). */ license?: string;
|
|
24
|
+
};
|
|
25
|
+
/** Source of forecast data. Describes both deterministic and ensemble-based forecast systems. */
|
|
26
|
+
export type ForecastDataSource = {
|
|
27
|
+
/** Provider or operator name */ provider: string;
|
|
28
|
+
/** Type of forecast product. */ type: 'model' | 'ensemble' | 'derived' | 'other';
|
|
29
|
+
/** Model name or product identifier (e.g. "GFS", "AROME"). */ model?: string;
|
|
30
|
+
/** Model version, configuration, or cycle identifier. */ modelVersion?: string;
|
|
31
|
+
/** Model initialization date */ runTime?: Date;
|
|
32
|
+
/** Ensemble member identifier for probabilistic forecasts. */ ensembleMember?: string;
|
|
33
|
+
/** URL pointing to model documentation or the data endpoint. */ url?: string;
|
|
34
|
+
/** Data license or usage terms for the forecast. */ license?: string;
|
|
35
|
+
};
|
|
36
|
+
/** Temperature-related fields. */
|
|
37
|
+
export type Temperature = {
|
|
38
|
+
/** Air temperature in degrees Celsius. */ tempC: number;
|
|
39
|
+
/** Feels-like temperature in °C (wind chill or heat index depending on conditions). */ feelsLikeC?: number;
|
|
40
|
+
/** Dew point temperature in °C. */ dewPointC?: number;
|
|
41
|
+
/** Minimum temperature over the period in °C. */ minC?: number;
|
|
42
|
+
/** Maximum temperature over the period in °C. */ maxC?: number;
|
|
43
|
+
};
|
|
44
|
+
/** Humidity parameters. */
|
|
45
|
+
export type Humidity = {
|
|
46
|
+
/** Relative humidity in percent (0–100). */ relativePercent: number;
|
|
47
|
+
/** Absolute humidity in grams per cubic meter. */ absoluteGm3?: number;
|
|
48
|
+
};
|
|
49
|
+
/** Wind according to common WMO conventions. */
|
|
50
|
+
export type Wind = {
|
|
51
|
+
/** Mean wind speed in meters per second. */ speedMs: number;
|
|
52
|
+
/** Wind direction in degrees (0–360, where 0/360 = North, 90 = East). */ directionDeg: number;
|
|
53
|
+
/** Maximum gust speed in meters per second over the period. */ gustMs?: number;
|
|
54
|
+
};
|
|
55
|
+
/** Canonical list of WMO cloud genera. */
|
|
56
|
+
export type CloudType = 'cirrus' | 'cirrocumulus' | 'cirrostratus' | 'altocumulus' | 'altostratus' | 'nimbostratus' | 'stratocumulus' | 'stratus' | 'cumulus' | 'cumulonimbus';
|
|
57
|
+
/** Cloud cover information following common WMO recommendations. */
|
|
58
|
+
export type CloudCover = {
|
|
59
|
+
/** Total cloud cover in percent (0–100). */ totalPercent: number;
|
|
60
|
+
/** Low-level cloud fraction in percent (0–100). */ lowPercent?: number;
|
|
61
|
+
/** Mid-level cloud fraction in percent (0–100). */ middlePercent?: number;
|
|
62
|
+
/** High-level cloud fraction in percent (0–100). */ highPercent?: number;
|
|
63
|
+
/** Cloud base height in meters above ground level. */ cloudBaseHeightM?: number;
|
|
64
|
+
/** Dominant cloud genus when it can be identified. */ dominantGenre?: CloudType;
|
|
65
|
+
};
|
|
66
|
+
/** Canonical list of atmospheric phenomena based on WMO classification. */
|
|
67
|
+
export type AtmosphericPhenomenon = 'rain' | 'drizzle' | 'freezing-rain' | 'snow' | 'snow-grains' | 'ice-pellets' | 'hail' | 'fog' | 'mist' | 'haze' | 'smoke' | 'dust' | 'sand' | 'volcanic-ash' | 'thunderstorm' | 'squall' | 'funnel-cloud';
|
|
68
|
+
/** Single atmospheric phenomenon, observed or forecast. */
|
|
69
|
+
export type WeatherPhenomenon = {
|
|
70
|
+
/** Phenomenon type according to the WMO-inspired list. */ phenomenon: AtmosphericPhenomenon;
|
|
71
|
+
/** Qualitative intensity: "+" for heavy, "-" for light, or null for normal. */ intensity: '+' | '-' | null;
|
|
72
|
+
/** Optional free-text remark describing special characteristics. */ remark?: string;
|
|
73
|
+
};
|
|
74
|
+
/** Atmospheric pressure. */
|
|
75
|
+
export type Pressure = {
|
|
76
|
+
/** Sea-level pressure in hectopascals (millibars). */ seaLevelHpa?: number;
|
|
77
|
+
/** Station-level pressure in hectopascals at the observing elevation. */ stationLevelHpa: number;
|
|
78
|
+
};
|
|
79
|
+
/** Visibility metrics. */
|
|
80
|
+
export type Visibility = {
|
|
81
|
+
/** Horizontal visibility in meters. */ horizontalM?: number;
|
|
82
|
+
/** Vertical visibility in meters (e.g. ceiling height). */ verticalM?: number;
|
|
83
|
+
};
|
|
84
|
+
/** Solar radiation and UV information. */
|
|
85
|
+
export type Solar = {
|
|
86
|
+
/** Global solar radiation in watts per square meter. */ radiationWm2?: number;
|
|
87
|
+
/** Dimensionless UV index, typically between 0 and ~12. */ uvIndex?: number;
|
|
88
|
+
};
|
|
89
|
+
/** Air quality indicators. */
|
|
90
|
+
export type AirQuality = {
|
|
91
|
+
/** Fine particulate matter PM2.5 in micrograms per cubic meter. */ pm25Ugm3?: number;
|
|
92
|
+
/** Particulate matter PM10 in micrograms per cubic meter. */ pm10Ugm3?: number;
|
|
93
|
+
/** Ozone (O₃) concentration in micrograms per cubic meter. */ o3Ugm3?: number;
|
|
94
|
+
/** Nitrogen dioxide (NO₂) concentration in micrograms per cubic meter. */ no2Ugm3?: number;
|
|
95
|
+
/** Sulfur dioxide (SO₂) concentration in micrograms per cubic meter. */ so2Ugm3?: number;
|
|
96
|
+
/** Carbon monoxide (CO) concentration in micrograms per cubic meter. */ coUgm3?: number;
|
|
97
|
+
};
|
|
98
|
+
/** Quality metadata attached to an observation or measurement. */
|
|
99
|
+
export type ObservationQualityMetadata = {
|
|
100
|
+
/** Status of the data, typically resulting from QC checks or post-processing. */ status?: 'verified' | 'unverified' | 'estimated' | 'interpolated' | 'suspect';
|
|
101
|
+
/** Free-text notes or comments about the data quality. */ notes?: string;
|
|
102
|
+
};
|
|
103
|
+
/** Validity period of a forecast or alert. */
|
|
104
|
+
export type ValidityPeriod = {
|
|
105
|
+
/** Start of validity */ from: Date;
|
|
106
|
+
/** End of validity */ to: Date;
|
|
107
|
+
};
|
|
108
|
+
/** Weather alert or warning issued by an authority or provider. */
|
|
109
|
+
export type WeatherAlert = {
|
|
110
|
+
/** Alert type or category (e.g. "wind", "flood", "heat"). */ type: string;
|
|
111
|
+
/** Severity level as a normalized qualitative scale. */ severity: 'minor' | 'moderate' | 'severe' | 'extreme';
|
|
112
|
+
/** Human-readable description of the alert and its expected impacts. */ description?: string;
|
|
113
|
+
/** Validity period of a forecast or alert. */ validity?: ValidityPeriod;
|
|
114
|
+
};
|
|
115
|
+
/** Probabilistic forecast data for a single parameter. */
|
|
116
|
+
export type ProbabilisticData = {
|
|
117
|
+
/** Confidence level for the forecast (0–100%). */ confidencePercent?: number;
|
|
118
|
+
/** Temperature 10th percentile in °C (optimistic or cooler scenario). */ tempP10C?: number;
|
|
119
|
+
/** Temperature 90th percentile in °C (pessimistic or warmer scenario). */ tempP90C?: number;
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Core weather data structure containing essential meteorological measurements.
|
|
123
|
+
*
|
|
124
|
+
* This type represents the fundamental weather parameters that can be used
|
|
125
|
+
* for both observations and forecasts. It follows WMO conventions and uses
|
|
126
|
+
* explicit unit encoding in property names to ensure clarity and type safety.
|
|
127
|
+
*
|
|
128
|
+
* All core measurements are required, while some extended parameters
|
|
129
|
+
* (visibility, solar, air quality, alerts) are optional.
|
|
130
|
+
*
|
|
131
|
+
* @see WeatherObservation - For observation-specific data with location and time
|
|
132
|
+
* @see WeatherForecast - For forecast-specific data with validity periods
|
|
133
|
+
*/
|
|
134
|
+
export type WeatherDataCore = {
|
|
135
|
+
/** Temperature-related measurements at the observation time. */ temperature: Temperature;
|
|
136
|
+
/** Humidity-related measurements at the observation time. */ humidity: Humidity;
|
|
137
|
+
/** Wind conditions at the observation time. */ wind: Wind;
|
|
138
|
+
/** Instantaneous precipitation rates at the observation time. */ precipitationMmh: number;
|
|
139
|
+
/** Atmospheric pressure at or reduced to station/sea level. */ pressure: Pressure;
|
|
140
|
+
/** List of significant weather phenomena observed. */ phenomena?: WeatherPhenomenon[];
|
|
141
|
+
/** Cloud cover information at the observation time. */ clouds?: CloudCover;
|
|
142
|
+
/** Visibility metrics at the observation time. */ visibility?: Visibility;
|
|
143
|
+
/** Solar radiation and UV information at the observation time. */ solar?: Solar;
|
|
144
|
+
/** Air quality indicators at or near the observation site. */ airQuality?: AirQuality;
|
|
145
|
+
/** Weather alerts that are active for this forecast period and location. */ alerts?: WeatherAlert[];
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Weather observation.
|
|
149
|
+
*
|
|
150
|
+
* Represents a real-world measurement or observation at a specific point in time,
|
|
151
|
+
* typically coming from an instrumented station or human report.
|
|
152
|
+
*/
|
|
153
|
+
export type WeatherObservation = WeatherDataCore & {
|
|
154
|
+
/** Exact time when the observation was made. */ observedOn: Date;
|
|
155
|
+
/** Geographic location of the observation. */ location: GeoLocation;
|
|
156
|
+
/** Observation metadata */ metadata?: {
|
|
157
|
+
/** Observation source metadata (station, provider, license...). */ source?: ObservationDataSource;
|
|
158
|
+
/** Time when the data was collected or ingested by the system. */ collectedOn?: Date;
|
|
159
|
+
/** Human-readable description or summary of the observation. */ description?: string;
|
|
160
|
+
/** Raw source data as received from the provider. */ rawData?: Record<string, any> | string;
|
|
161
|
+
/** Quality control and processing metadata for the observation. */ observationQuality?: ObservationQualityMetadata;
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Weather forecast.
|
|
166
|
+
*
|
|
167
|
+
* Represents a forecast for a given future period, possibly including a mix
|
|
168
|
+
* of deterministic and probabilistic information as provided by a model.
|
|
169
|
+
*/
|
|
170
|
+
export type WeatherForecast = WeatherDataCore & {
|
|
171
|
+
/** Time when the forecast was issued. */ issuedOn: Date;
|
|
172
|
+
/** Period of time this forecast is valid for. */ validity: ValidityPeriod;
|
|
173
|
+
/** Geographic location to which the forecast applies. */ location: GeoLocation;
|
|
174
|
+
/** Observation metadata */ metadata?: {
|
|
175
|
+
/** Forecast model and source metadata. */ source?: ForecastDataSource;
|
|
176
|
+
/** Time when the data was collected or ingested by the system. */ collectedOn?: Date;
|
|
177
|
+
/** Human-readable description or summary of the observation. */ description?: string;
|
|
178
|
+
/** Raw source data as received from the provider. */ rawData?: Record<string, any> | string;
|
|
179
|
+
/** Associated probabilistic forecast information, if available. */ probabilistic?: ProbabilisticData;
|
|
180
|
+
};
|
|
181
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
File without changes
|
package/node/index.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { MxfbCliConfig };
|
|
1
|
+
export {};
|
package/node/index.js
ADDED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,41 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mxfb/tools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"author": "Maxime Fabas",
|
|
6
6
|
"license": "ISC",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/
|
|
9
|
+
"url": "https://github.com/maximefabas/mf-tools"
|
|
10
10
|
},
|
|
11
11
|
"type": "module",
|
|
12
12
|
"main": "index.js",
|
|
13
13
|
"module": "index.js",
|
|
14
|
-
"
|
|
15
|
-
"@
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@aws-sdk/client-s3": "^3.907.0",
|
|
16
|
+
"@aws-sdk/lib-storage": "^3.907.0",
|
|
17
|
+
"@design-edito/tools": "^0.2.41",
|
|
18
|
+
"@google-cloud/storage": "^7.17.2",
|
|
19
|
+
"@inquirer/prompts": "^8.2.0",
|
|
20
|
+
"ansi-regex": "^6.2.2",
|
|
21
|
+
"archiver": "^7.0.1",
|
|
22
|
+
"basic-ftp": "^5.0.5",
|
|
23
|
+
"chalk": "^5.6.2",
|
|
24
|
+
"express": "^5.1.0",
|
|
25
|
+
"multer": "^2.0.0",
|
|
26
|
+
"quantize": "^1.0.2",
|
|
27
|
+
"react": "^19.2.0",
|
|
28
|
+
"react-dom": "^19.2.0",
|
|
29
|
+
"sharp": "^0.34.4",
|
|
30
|
+
"ssh2": "^1.17.0",
|
|
31
|
+
"ssh2-sftp-client": "^12.0.0",
|
|
32
|
+
"xss": "^1.0.15",
|
|
33
|
+
"zod": "^4.1.12"
|
|
23
34
|
},
|
|
24
35
|
"devDependencies": {
|
|
25
|
-
"@
|
|
36
|
+
"@preact/preset-vite": "^2.10.2",
|
|
37
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
38
|
+
"@testing-library/preact": "^3.2.4",
|
|
39
|
+
"@testing-library/react": "^16.3.1",
|
|
40
|
+
"@types/archiver": "^6.0.3",
|
|
41
|
+
"@types/express": "^5.0.2",
|
|
42
|
+
"@types/jsdom": "^27.0.0",
|
|
43
|
+
"@types/multer": "^2.0.0",
|
|
44
|
+
"@types/node": "^24.7.1",
|
|
26
45
|
"@types/prompts": "^2.4.9",
|
|
27
|
-
"@types/
|
|
28
|
-
"
|
|
46
|
+
"@types/quantize": "^1.0.2",
|
|
47
|
+
"@types/react": "^19.2.2",
|
|
48
|
+
"@types/react-dom": "^19.2.1",
|
|
49
|
+
"@types/semver": "^7.7.1",
|
|
50
|
+
"@types/sharp": "^0.31.1",
|
|
51
|
+
"@types/ssh2-sftp-client": "^9.0.4",
|
|
52
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
29
53
|
"chokidar-cli": "^3.0.0",
|
|
30
|
-
"concurrently": "^9.1
|
|
31
|
-
"esbuild": "^0.25.
|
|
54
|
+
"concurrently": "^9.2.1",
|
|
55
|
+
"esbuild": "^0.25.10",
|
|
56
|
+
"jsdom": "^27.4.0",
|
|
32
57
|
"prompts": "^2.4.2",
|
|
33
|
-
"puppeteer": "^24.
|
|
34
|
-
"
|
|
35
|
-
"react-dom": "^19.1.0",
|
|
36
|
-
"semver": "^7.7.2",
|
|
37
|
-
"sharp": "^0.34.2",
|
|
58
|
+
"puppeteer": "^24.23.1",
|
|
59
|
+
"semver": "^7.7.3",
|
|
38
60
|
"simple-git": "^3.27.0",
|
|
39
|
-
"typescript": "^5.
|
|
61
|
+
"typescript": "^5.9.3",
|
|
62
|
+
"vitest": "^4.0.17"
|
|
40
63
|
}
|
|
41
64
|
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
// src/agnostic/_WIP_tennis/index.ts
|
|
2
|
-
var Tennis;
|
|
3
|
-
((Tennis2) => {
|
|
4
|
-
function getGameScore(game) {
|
|
5
|
-
const score = [0, 0];
|
|
6
|
-
game.forEach((point) => {
|
|
7
|
-
if (score.includes(5)) return;
|
|
8
|
-
const winnerPos = point ? 0 : 1;
|
|
9
|
-
const loserPos = point ? 1 : 0;
|
|
10
|
-
const winnerCurrScore = score[winnerPos];
|
|
11
|
-
const loserCurrScore = score[loserPos];
|
|
12
|
-
if (loserCurrScore === 5 || winnerCurrScore === 5) return;
|
|
13
|
-
if (loserCurrScore === 4 && winnerCurrScore === 3) {
|
|
14
|
-
score[loserPos] = 3;
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
if (loserCurrScore === 3 && winnerCurrScore === 4) {
|
|
18
|
-
score[winnerPos] = 5;
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
if (winnerCurrScore === 3) {
|
|
22
|
-
score[winnerPos] = 5;
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
score[winnerPos]++;
|
|
26
|
-
return;
|
|
27
|
-
});
|
|
28
|
-
return score;
|
|
29
|
-
}
|
|
30
|
-
function getGameWinner(gameScore) {
|
|
31
|
-
const [scoreA, scoreB] = gameScore;
|
|
32
|
-
if (scoreA === 5) return true;
|
|
33
|
-
if (scoreB === 5) return false;
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
function getSetScore(set) {
|
|
37
|
-
const score = [0, 0];
|
|
38
|
-
let foundUnfinishedGame = false;
|
|
39
|
-
set.forEach((game) => {
|
|
40
|
-
if (foundUnfinishedGame) return;
|
|
41
|
-
if (score.includes(7)) return;
|
|
42
|
-
const [scoreA, scoreB] = score;
|
|
43
|
-
if (scoreA >= 6 && scoreB < 5) return;
|
|
44
|
-
if (scoreB <= 6 && scoreA < 5) return;
|
|
45
|
-
const gameScore = getGameScore(game);
|
|
46
|
-
const gameWinner = getGameWinner(gameScore);
|
|
47
|
-
if (gameWinner === true) {
|
|
48
|
-
score[0]++;
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
if (gameWinner === false) {
|
|
52
|
-
score[1]++;
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
foundUnfinishedGame = true;
|
|
56
|
-
});
|
|
57
|
-
return score;
|
|
58
|
-
}
|
|
59
|
-
})(Tennis || (Tennis = {}));
|
|
60
|
-
export {
|
|
61
|
-
Tennis
|
|
62
|
-
};
|
package/agnostic/index.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
export declare namespace Elo {
|
|
2
|
-
const K_FACTOR = 32;
|
|
3
|
-
/**
|
|
4
|
-
* Calcule la probabilité de gain de A en fonction des scores Elo de A et B.
|
|
5
|
-
* @param eloA Le score Elo du joueur A.
|
|
6
|
-
* @param eloB Le score Elo du joueur B.
|
|
7
|
-
* @returns La probabilité que le joueur A gagne contre le joueur B.
|
|
8
|
-
*/
|
|
9
|
-
function getWinProbability(eloA: number, eloB: number): number;
|
|
10
|
-
/**
|
|
11
|
-
* Calcule les nouveaux classements Elo pour les scénarios de victoire de A, de B et de match nul.
|
|
12
|
-
* @param eloA Le score Elo actuel du joueur A.
|
|
13
|
-
* @param eloB Le score Elo actuel du joueur B.
|
|
14
|
-
* @param kFactor Facteur K pour les calculs Elo (facultatif, valeur par défaut = 32).
|
|
15
|
-
* @returns Un objet contenant les nouveaux classements Elo pour chaque scénario de résultat.
|
|
16
|
-
*/
|
|
17
|
-
function getOutcome(eloA: number, eloB: number, kFactor?: number): {
|
|
18
|
-
aWins: {
|
|
19
|
-
eloA: number;
|
|
20
|
-
eloB: number;
|
|
21
|
-
};
|
|
22
|
-
bWins: {
|
|
23
|
-
eloA: number;
|
|
24
|
-
eloB: number;
|
|
25
|
-
};
|
|
26
|
-
null: {
|
|
27
|
-
eloA: number;
|
|
28
|
-
eloB: number;
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
}
|
package/agnostic/misc/index.d.ts
DELETED
package/agnostic/misc/index.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Elo
|
|
3
|
-
} from "../../chunks/chunk-ZC5RZBNY.js";
|
|
4
|
-
import {
|
|
5
|
-
Tennis
|
|
6
|
-
} from "../../chunks/chunk-TTZHPIH7.js";
|
|
7
|
-
|
|
8
|
-
// src/agnostic/misc/index.ts
|
|
9
|
-
var Misc;
|
|
10
|
-
((Misc2) => {
|
|
11
|
-
Misc2.Elo = Elo;
|
|
12
|
-
Misc2.Tennis = Tennis;
|
|
13
|
-
})(Misc || (Misc = {}));
|
|
14
|
-
export {
|
|
15
|
-
Misc
|
|
16
|
-
};
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
export declare namespace Tennis {
|
|
2
|
-
namespace Score {
|
|
3
|
-
type PointDescriptor = boolean;
|
|
4
|
-
type GameFormat = {
|
|
5
|
-
upTo: number;
|
|
6
|
-
diffToWin: number;
|
|
7
|
-
};
|
|
8
|
-
class Game {
|
|
9
|
-
private format;
|
|
10
|
-
private pointsSequence;
|
|
11
|
-
constructor(format: GameFormat);
|
|
12
|
-
add(point: PointDescriptor): this;
|
|
13
|
-
getNumericScore(): [number, number];
|
|
14
|
-
static numericToStringScoreValue(numScore: [number, number]): string;
|
|
15
|
-
getScore(): string;
|
|
16
|
-
isFinished(): boolean;
|
|
17
|
-
getWinner(): boolean | null;
|
|
18
|
-
}
|
|
19
|
-
type SetFormat = {
|
|
20
|
-
upTo: number;
|
|
21
|
-
diffToWin: number;
|
|
22
|
-
gamesFormat: GameFormat;
|
|
23
|
-
tieBreakAt: number | null;
|
|
24
|
-
tieBreakFormat: GameFormat;
|
|
25
|
-
};
|
|
26
|
-
class Set {
|
|
27
|
-
private format;
|
|
28
|
-
private pointsSequence;
|
|
29
|
-
constructor(format: SetFormat);
|
|
30
|
-
add(point: PointDescriptor): void;
|
|
31
|
-
getGames(): Game[];
|
|
32
|
-
getNumericScore(): {
|
|
33
|
-
games: [number, number];
|
|
34
|
-
currentGame?: [number, number];
|
|
35
|
-
};
|
|
36
|
-
getScore(): string;
|
|
37
|
-
isFinished(): boolean;
|
|
38
|
-
getWinner(): boolean | null;
|
|
39
|
-
}
|
|
40
|
-
type MatchFormat = {
|
|
41
|
-
upTo: number;
|
|
42
|
-
setsFormat: SetFormat;
|
|
43
|
-
lastSetFormat: SetFormat;
|
|
44
|
-
};
|
|
45
|
-
class Match {
|
|
46
|
-
private format;
|
|
47
|
-
private pointsSequence;
|
|
48
|
-
constructor(format: MatchFormat);
|
|
49
|
-
add(point: PointDescriptor): void;
|
|
50
|
-
getSets(): Set[];
|
|
51
|
-
getNumericScore(): {
|
|
52
|
-
sets: [number, number][];
|
|
53
|
-
currentGame?: [number, number];
|
|
54
|
-
};
|
|
55
|
-
getScore(): string;
|
|
56
|
-
isFinished(): boolean;
|
|
57
|
-
getWinner(): boolean | null;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
package/chunks/chunk-TTZHPIH7.js
DELETED
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
// src/agnostic/misc/tennis/index.ts
|
|
2
|
-
var Tennis;
|
|
3
|
-
((Tennis2) => {
|
|
4
|
-
let Score;
|
|
5
|
-
((Score2) => {
|
|
6
|
-
class Game {
|
|
7
|
-
format;
|
|
8
|
-
pointsSequence = [];
|
|
9
|
-
constructor(format) {
|
|
10
|
-
this.format = format;
|
|
11
|
-
}
|
|
12
|
-
add(point) {
|
|
13
|
-
if (!this.isFinished()) this.pointsSequence.push(point);
|
|
14
|
-
return this;
|
|
15
|
-
}
|
|
16
|
-
getNumericScore() {
|
|
17
|
-
const { pointsSequence, format } = this;
|
|
18
|
-
const { upTo, diffToWin } = format;
|
|
19
|
-
return pointsSequence.reduce((reduced, point) => {
|
|
20
|
-
const [aScore, bScore] = reduced;
|
|
21
|
-
const aWon = aScore >= upTo && aScore - bScore >= diffToWin;
|
|
22
|
-
const bWon = bScore >= upTo && bScore - aScore >= diffToWin;
|
|
23
|
-
if (aWon || bWon) return reduced;
|
|
24
|
-
return point ? [aScore + 1, bScore] : [aScore, bScore + 1];
|
|
25
|
-
}, [0, 0]);
|
|
26
|
-
}
|
|
27
|
-
static numericToStringScoreValue(numScore) {
|
|
28
|
-
const [aScore, bScore] = numScore;
|
|
29
|
-
if (aScore >= 3 && bScore >= 3) {
|
|
30
|
-
if (aScore === bScore) return "deuce";
|
|
31
|
-
if (aScore < bScore) return "Ad/40";
|
|
32
|
-
return "40/Ad";
|
|
33
|
-
}
|
|
34
|
-
const strScoresList = ["0", "15", "30", "40"];
|
|
35
|
-
return `${strScoresList.at(aScore) ?? "?"}/${strScoresList.at(bScore) ?? "?"}`;
|
|
36
|
-
}
|
|
37
|
-
getScore() {
|
|
38
|
-
return Game.numericToStringScoreValue(this.getNumericScore());
|
|
39
|
-
}
|
|
40
|
-
isFinished() {
|
|
41
|
-
const { format } = this;
|
|
42
|
-
const { upTo, diffToWin } = format;
|
|
43
|
-
const [aScore, bScore] = this.getNumericScore();
|
|
44
|
-
const maxScoreReached = Math.max(aScore, bScore) >= upTo;
|
|
45
|
-
const diffReached = Math.abs(aScore - bScore) >= diffToWin;
|
|
46
|
-
if (maxScoreReached && diffReached) return true;
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
getWinner() {
|
|
50
|
-
if (!this.isFinished()) return null;
|
|
51
|
-
const [aScore, bScore] = this.getNumericScore();
|
|
52
|
-
return aScore > bScore;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
Score2.Game = Game;
|
|
56
|
-
class Set {
|
|
57
|
-
format;
|
|
58
|
-
pointsSequence = [];
|
|
59
|
-
constructor(format) {
|
|
60
|
-
this.format = format;
|
|
61
|
-
}
|
|
62
|
-
add(point) {
|
|
63
|
-
this.pointsSequence.push(point);
|
|
64
|
-
}
|
|
65
|
-
getGames() {
|
|
66
|
-
const { format } = this;
|
|
67
|
-
const { upTo, diffToWin, gamesFormat, tieBreakFormat, tieBreakAt } = format;
|
|
68
|
-
let aGames = 0;
|
|
69
|
-
let bGames = 0;
|
|
70
|
-
const games = [];
|
|
71
|
-
const { pointsSequence } = this;
|
|
72
|
-
pointsSequence.forEach((point) => {
|
|
73
|
-
const maxGamesReached = Math.max(aGames, bGames) >= upTo;
|
|
74
|
-
const diffReached = Math.abs(aGames - bGames) >= diffToWin;
|
|
75
|
-
if (maxGamesReached && diffReached) return;
|
|
76
|
-
const lastGameInList = games.at(-1);
|
|
77
|
-
const lastGameIsFinished = lastGameInList?.isFinished();
|
|
78
|
-
if (lastGameInList === void 0 || lastGameIsFinished === true) {
|
|
79
|
-
const isTiebreak = aGames === bGames && aGames === tieBreakAt;
|
|
80
|
-
if (isTiebreak) games.push(new Game(tieBreakFormat));
|
|
81
|
-
else games.push(new Game(gamesFormat));
|
|
82
|
-
}
|
|
83
|
-
const currentGame = games.at(-1);
|
|
84
|
-
if (currentGame === void 0) throw "Something went wrong, this should't be undefined";
|
|
85
|
-
currentGame.add(point);
|
|
86
|
-
const currentGameWinner = currentGame.getWinner();
|
|
87
|
-
if (currentGameWinner === true) {
|
|
88
|
-
aGames += 1;
|
|
89
|
-
}
|
|
90
|
-
if (currentGameWinner === false) {
|
|
91
|
-
bGames += 1;
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
return games;
|
|
95
|
-
}
|
|
96
|
-
getNumericScore() {
|
|
97
|
-
const games = this.getGames();
|
|
98
|
-
let aScore = 0;
|
|
99
|
-
let bScore = 0;
|
|
100
|
-
games.forEach((game) => {
|
|
101
|
-
const winner = game.getWinner();
|
|
102
|
-
if (winner === true) {
|
|
103
|
-
aScore++;
|
|
104
|
-
}
|
|
105
|
-
if (winner === false) {
|
|
106
|
-
bScore++;
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
const lastGame = games.at(-1);
|
|
110
|
-
if (lastGame === void 0) return { games: [aScore, bScore] };
|
|
111
|
-
if (lastGame.isFinished() === false) return {
|
|
112
|
-
games: [aScore, bScore],
|
|
113
|
-
currentGame: lastGame.getNumericScore()
|
|
114
|
-
};
|
|
115
|
-
return { games: [aScore, bScore] };
|
|
116
|
-
}
|
|
117
|
-
getScore() {
|
|
118
|
-
const numericScore = this.getNumericScore();
|
|
119
|
-
const { games, currentGame } = numericScore;
|
|
120
|
-
if (currentGame === void 0) return `${games.join("/")}`;
|
|
121
|
-
return `${games.join("/")} ${Game.numericToStringScoreValue(currentGame)}`;
|
|
122
|
-
}
|
|
123
|
-
isFinished() {
|
|
124
|
-
const { format } = this;
|
|
125
|
-
const { upTo, diffToWin, tieBreakAt } = format;
|
|
126
|
-
const [aScore, bScore] = this.getNumericScore().games;
|
|
127
|
-
const maxGamesReached = Math.max(aScore, bScore) >= upTo;
|
|
128
|
-
const diffReached = Math.abs(aScore - bScore) >= diffToWin;
|
|
129
|
-
const tieBreakBeenPlayed = tieBreakAt !== null && Math.max(aScore, bScore) > tieBreakAt;
|
|
130
|
-
return tieBreakBeenPlayed || maxGamesReached && diffReached;
|
|
131
|
-
}
|
|
132
|
-
getWinner() {
|
|
133
|
-
if (!this.isFinished()) return null;
|
|
134
|
-
const [aScore, bScore] = this.getNumericScore().games;
|
|
135
|
-
return aScore > bScore;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
Score2.Set = Set;
|
|
139
|
-
class Match {
|
|
140
|
-
format;
|
|
141
|
-
pointsSequence = [];
|
|
142
|
-
constructor(format) {
|
|
143
|
-
this.format = format;
|
|
144
|
-
}
|
|
145
|
-
add(point) {
|
|
146
|
-
this.pointsSequence.push(point);
|
|
147
|
-
}
|
|
148
|
-
getSets() {
|
|
149
|
-
const { format } = this;
|
|
150
|
-
const { upTo, setsFormat, lastSetFormat } = format;
|
|
151
|
-
let aSets = 0;
|
|
152
|
-
let bSets = 0;
|
|
153
|
-
const sets = [];
|
|
154
|
-
const { pointsSequence } = this;
|
|
155
|
-
pointsSequence.forEach((point) => {
|
|
156
|
-
const maxSetsReached = Math.max(aSets, bSets) >= upTo;
|
|
157
|
-
if (maxSetsReached) return;
|
|
158
|
-
const lastSetInList = sets.at(-1);
|
|
159
|
-
const lastSetIsFinished = lastSetInList?.isFinished();
|
|
160
|
-
if (lastSetInList === void 0 || lastSetIsFinished === true) {
|
|
161
|
-
const isLastSet = aSets === bSets && aSets === upTo - 1;
|
|
162
|
-
if (isLastSet) sets.push(new Set(lastSetFormat));
|
|
163
|
-
else sets.push(new Set(setsFormat));
|
|
164
|
-
}
|
|
165
|
-
const currentSet = sets.at(-1);
|
|
166
|
-
if (currentSet === void 0) throw "Something went wrong, this should't be undefined";
|
|
167
|
-
currentSet.add(point);
|
|
168
|
-
const currentSetWinner = currentSet.getWinner();
|
|
169
|
-
if (currentSetWinner === true) {
|
|
170
|
-
aSets += 1;
|
|
171
|
-
}
|
|
172
|
-
if (currentSetWinner === false) {
|
|
173
|
-
bSets += 1;
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
return sets;
|
|
177
|
-
}
|
|
178
|
-
getNumericScore() {
|
|
179
|
-
const sets = this.getSets();
|
|
180
|
-
const numericScore = {
|
|
181
|
-
sets: [],
|
|
182
|
-
currentGame: void 0
|
|
183
|
-
};
|
|
184
|
-
sets.forEach((set) => {
|
|
185
|
-
const setNumericScore = set.getNumericScore();
|
|
186
|
-
const { games, currentGame } = setNumericScore;
|
|
187
|
-
numericScore.sets.push(games);
|
|
188
|
-
if (currentGame !== void 0) {
|
|
189
|
-
numericScore.currentGame = currentGame;
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
return numericScore;
|
|
193
|
-
}
|
|
194
|
-
getScore() {
|
|
195
|
-
return this.getSets().map((set) => set.getScore()).join(" ");
|
|
196
|
-
}
|
|
197
|
-
isFinished() {
|
|
198
|
-
const { format } = this;
|
|
199
|
-
const { upTo } = format;
|
|
200
|
-
const sets = this.getSets();
|
|
201
|
-
let aSets = 0;
|
|
202
|
-
let bSets = 0;
|
|
203
|
-
sets.forEach((set) => {
|
|
204
|
-
const winner = set.getWinner();
|
|
205
|
-
if (winner === true) {
|
|
206
|
-
aSets++;
|
|
207
|
-
}
|
|
208
|
-
if (winner === false) {
|
|
209
|
-
bSets++;
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
return Math.max(aSets, bSets) >= upTo;
|
|
213
|
-
}
|
|
214
|
-
getWinner() {
|
|
215
|
-
if (!this.isFinished()) return null;
|
|
216
|
-
const sets = this.getSets();
|
|
217
|
-
let aSets = 0;
|
|
218
|
-
let bSets = 0;
|
|
219
|
-
sets.forEach((set) => {
|
|
220
|
-
const winner = set.getWinner();
|
|
221
|
-
if (winner === true) {
|
|
222
|
-
aSets++;
|
|
223
|
-
}
|
|
224
|
-
if (winner === false) {
|
|
225
|
-
bSets++;
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
return aSets > bSets;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
Score2.Match = Match;
|
|
232
|
-
})(Score = Tennis2.Score || (Tennis2.Score = {}));
|
|
233
|
-
})(Tennis || (Tennis = {}));
|
|
234
|
-
|
|
235
|
-
export {
|
|
236
|
-
Tennis
|
|
237
|
-
};
|
package/chunks/chunk-ZC5RZBNY.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
// src/agnostic/misc/elo/index.ts
|
|
2
|
-
var Elo;
|
|
3
|
-
((Elo2) => {
|
|
4
|
-
Elo2.K_FACTOR = 32;
|
|
5
|
-
function getWinProbability(eloA, eloB) {
|
|
6
|
-
const exponent = (eloB - eloA) / 400;
|
|
7
|
-
return 1 / (1 + Math.pow(10, exponent));
|
|
8
|
-
}
|
|
9
|
-
Elo2.getWinProbability = getWinProbability;
|
|
10
|
-
function getOutcome(eloA, eloB, kFactor = Elo2.K_FACTOR) {
|
|
11
|
-
const probA = getWinProbability(eloA, eloB);
|
|
12
|
-
const probB = 1 - probA;
|
|
13
|
-
return {
|
|
14
|
-
aWins: {
|
|
15
|
-
eloA: Math.round(eloA + kFactor * (1 - probA)),
|
|
16
|
-
eloB: Math.round(eloB + kFactor * (0 - probB))
|
|
17
|
-
},
|
|
18
|
-
bWins: {
|
|
19
|
-
eloA: Math.round(eloA + kFactor * (0 - probA)),
|
|
20
|
-
eloB: Math.round(eloB + kFactor * (1 - probB))
|
|
21
|
-
},
|
|
22
|
-
null: {
|
|
23
|
-
eloA: Math.round(eloA + kFactor * (0.5 - probA)),
|
|
24
|
-
eloB: Math.round(eloB + kFactor * (0.5 - probB))
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
Elo2.getOutcome = getOutcome;
|
|
29
|
-
})(Elo || (Elo = {}));
|
|
30
|
-
|
|
31
|
-
export {
|
|
32
|
-
Elo
|
|
33
|
-
};
|
package/components/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare namespace Components { }
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export type Config = {
|
|
2
|
-
mxfb_projects_sync?: {
|
|
3
|
-
lm_path?: string;
|
|
4
|
-
mxfb_path?: string;
|
|
5
|
-
};
|
|
6
|
-
};
|
|
7
|
-
export declare const emptyConfigObj: Config;
|
|
8
|
-
export declare function createConfigFile(force?: boolean): Promise<true | Error>;
|
|
9
|
-
declare const deletionSecurityPhrase = "THIS IS A DESTRUCTIVE AND DEFINITIVE ACTION";
|
|
10
|
-
export declare function deleteConfigDir(security: typeof deletionSecurityPhrase): Promise<true | Error>;
|
|
11
|
-
export declare function deleteConfigFile(security: typeof deletionSecurityPhrase): Promise<true | Error>;
|
|
12
|
-
export declare function ensureConfigFile(): Promise<true | Error>;
|
|
13
|
-
export declare function getConfigFile(): Promise<string | Error>;
|
|
14
|
-
export declare function validateConfigObj(configObj: any): configObj is Config;
|
|
15
|
-
export declare function parseConfigFile(fileContents: string): Config | Error;
|
|
16
|
-
export declare function getConfig(create?: boolean): Promise<Config | Error>;
|
|
17
|
-
export type ConfigUpdater = Partial<Config> | ((curr: Config) => Config);
|
|
18
|
-
export declare function updateConfig(updater: ConfigUpdater, create?: boolean): Promise<true | Error>;
|
|
19
|
-
export {};
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
// src/node/mxfb-cli-config/index.ts
|
|
2
|
-
import os from "node:os";
|
|
3
|
-
import { promises as fs } from "node:fs";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
var homeDirPath = os.homedir();
|
|
6
|
-
var mxfbConfigDirPath = path.join(homeDirPath, ".config/mxfb-cli");
|
|
7
|
-
var mxfbConfigFilePath = path.join(mxfbConfigDirPath, "config.json");
|
|
8
|
-
var emptyConfigObj = {};
|
|
9
|
-
async function createConfigFile(force = false) {
|
|
10
|
-
await fs.mkdir(mxfbConfigDirPath, { recursive: true });
|
|
11
|
-
try {
|
|
12
|
-
await fs.stat(mxfbConfigFilePath);
|
|
13
|
-
if (force === true) throw "create anyway";
|
|
14
|
-
return true;
|
|
15
|
-
} catch (err) {
|
|
16
|
-
try {
|
|
17
|
-
await fs.writeFile(
|
|
18
|
-
mxfbConfigFilePath,
|
|
19
|
-
`${JSON.stringify(emptyConfigObj, null, 2)}
|
|
20
|
-
`,
|
|
21
|
-
{ encoding: "utf-8" }
|
|
22
|
-
);
|
|
23
|
-
return true;
|
|
24
|
-
} catch (err2) {
|
|
25
|
-
if (err2 instanceof Error) return err2;
|
|
26
|
-
if (typeof err2 === "string") return new Error(err2);
|
|
27
|
-
return new Error(`${err2}`);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
var deletionSecurityPhrase = "THIS IS A DESTRUCTIVE AND DEFINITIVE ACTION";
|
|
32
|
-
async function deleteConfigDir(security) {
|
|
33
|
-
if (security !== deletionSecurityPhrase) return new Error("You must provide the security phrase");
|
|
34
|
-
await fs.rm(mxfbConfigDirPath, { recursive: true, force: true });
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
async function deleteConfigFile(security) {
|
|
38
|
-
if (security !== deletionSecurityPhrase) return new Error("You must provide the security phrase");
|
|
39
|
-
await fs.rm(mxfbConfigDirPath);
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
async function ensureConfigFile() {
|
|
43
|
-
try {
|
|
44
|
-
await fs.stat(mxfbConfigFilePath);
|
|
45
|
-
return true;
|
|
46
|
-
} catch (err) {
|
|
47
|
-
try {
|
|
48
|
-
const created = await createConfigFile();
|
|
49
|
-
if (created instanceof Error) throw created;
|
|
50
|
-
return true;
|
|
51
|
-
} catch (err2) {
|
|
52
|
-
if (err2 instanceof Error) return err2;
|
|
53
|
-
if (typeof err2 === "string") return new Error(err2);
|
|
54
|
-
return new Error(`${err2}`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
async function getConfigFile() {
|
|
59
|
-
try {
|
|
60
|
-
const fileContents = await fs.readFile(mxfbConfigFilePath, { encoding: "utf-8" });
|
|
61
|
-
return fileContents;
|
|
62
|
-
} catch (err) {
|
|
63
|
-
if (err instanceof Error) return err;
|
|
64
|
-
if (typeof err === "string") return new Error(err);
|
|
65
|
-
return new Error(`${err}`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
function validateConfigObj(configObj) {
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
function parseConfigFile(fileContents) {
|
|
72
|
-
try {
|
|
73
|
-
const parsed = JSON.parse(fileContents);
|
|
74
|
-
const validated = validateConfigObj(parsed);
|
|
75
|
-
if (!validated) throw new Error(`Invalid config file shape: ${fileContents}`);
|
|
76
|
-
return parsed;
|
|
77
|
-
} catch (err) {
|
|
78
|
-
if (err instanceof Error) return err;
|
|
79
|
-
if (typeof err === "string") return new Error(err);
|
|
80
|
-
return new Error(`${err}`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
async function getConfig(create = false) {
|
|
84
|
-
if (create) {
|
|
85
|
-
const created = await ensureConfigFile();
|
|
86
|
-
if (created !== true) return created;
|
|
87
|
-
}
|
|
88
|
-
const configStr = await getConfigFile();
|
|
89
|
-
if (configStr instanceof Error) return configStr;
|
|
90
|
-
const parsed = parseConfigFile(configStr);
|
|
91
|
-
if (parsed instanceof Error) return parsed;
|
|
92
|
-
return parsed;
|
|
93
|
-
}
|
|
94
|
-
async function updateConfig(updater, create = false) {
|
|
95
|
-
const currentConfig = await getConfig(create);
|
|
96
|
-
if (currentConfig instanceof Error) return currentConfig;
|
|
97
|
-
const updatedConfig = typeof updater === "function" ? updater(currentConfig) : { ...currentConfig, ...updater };
|
|
98
|
-
const stringified = JSON.stringify(updatedConfig, null, 2);
|
|
99
|
-
await fs.writeFile(mxfbConfigFilePath, stringified, { encoding: "utf-8" });
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
export {
|
|
103
|
-
createConfigFile,
|
|
104
|
-
deleteConfigDir,
|
|
105
|
-
deleteConfigFile,
|
|
106
|
-
emptyConfigObj,
|
|
107
|
-
ensureConfigFile,
|
|
108
|
-
getConfig,
|
|
109
|
-
getConfigFile,
|
|
110
|
-
parseConfigFile,
|
|
111
|
-
updateConfig,
|
|
112
|
-
validateConfigObj
|
|
113
|
-
};
|
package/shared/errors/index.d.ts
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { Errors } from '@design-edito/tools/agnostic/errors';
|
|
2
|
-
export declare enum Codes {
|
|
3
|
-
NO_DOCUMENT = "no-window-document-on-runtime",
|
|
4
|
-
NO_DOCUMENT_PLEASE_PROVIDE = "no-window-document-on-runtime-please-provide",
|
|
5
|
-
IMPOSSIBLE_TO_PICK_IN_ARRAY = "impossible-to-pick-in-array"
|
|
6
|
-
}
|
|
7
|
-
export declare const register: {
|
|
8
|
-
getMessage: <Code extends Codes>(code: Code) => Errors.Register.Message<{
|
|
9
|
-
"no-window-document-on-runtime": {
|
|
10
|
-
message: string;
|
|
11
|
-
detailsMaker: () => undefined;
|
|
12
|
-
};
|
|
13
|
-
"no-window-document-on-runtime-please-provide": {
|
|
14
|
-
message: string;
|
|
15
|
-
detailsMaker: (tips?: string) => string | undefined;
|
|
16
|
-
};
|
|
17
|
-
"impossible-to-pick-in-array": {
|
|
18
|
-
message: string;
|
|
19
|
-
detailsMaker: (array: any[]) => "Array cannot be empty" | "Too many exclusions";
|
|
20
|
-
};
|
|
21
|
-
}, Code>;
|
|
22
|
-
getDetailsMaker: <Code extends Codes>(code: Code) => Errors.Register.DetailsMaker<{
|
|
23
|
-
"no-window-document-on-runtime": {
|
|
24
|
-
message: string;
|
|
25
|
-
detailsMaker: () => undefined;
|
|
26
|
-
};
|
|
27
|
-
"no-window-document-on-runtime-please-provide": {
|
|
28
|
-
message: string;
|
|
29
|
-
detailsMaker: (tips?: string) => string | undefined;
|
|
30
|
-
};
|
|
31
|
-
"impossible-to-pick-in-array": {
|
|
32
|
-
message: string;
|
|
33
|
-
detailsMaker: (array: any[]) => "Array cannot be empty" | "Too many exclusions";
|
|
34
|
-
};
|
|
35
|
-
}, Code>;
|
|
36
|
-
getDetails: <Code extends Codes>(code: Code, ...params: Parameters<Errors.Register.DetailsMaker<{
|
|
37
|
-
"no-window-document-on-runtime": {
|
|
38
|
-
message: string;
|
|
39
|
-
detailsMaker: () => undefined;
|
|
40
|
-
};
|
|
41
|
-
"no-window-document-on-runtime-please-provide": {
|
|
42
|
-
message: string;
|
|
43
|
-
detailsMaker: (tips?: string) => string | undefined;
|
|
44
|
-
};
|
|
45
|
-
"impossible-to-pick-in-array": {
|
|
46
|
-
message: string;
|
|
47
|
-
detailsMaker: (array: any[]) => "Array cannot be empty" | "Too many exclusions";
|
|
48
|
-
};
|
|
49
|
-
}, Code>>) => ReturnType<Errors.Register.DetailsMaker<{
|
|
50
|
-
"no-window-document-on-runtime": {
|
|
51
|
-
message: string;
|
|
52
|
-
detailsMaker: () => undefined;
|
|
53
|
-
};
|
|
54
|
-
"no-window-document-on-runtime-please-provide": {
|
|
55
|
-
message: string;
|
|
56
|
-
detailsMaker: (tips?: string) => string | undefined;
|
|
57
|
-
};
|
|
58
|
-
"impossible-to-pick-in-array": {
|
|
59
|
-
message: string;
|
|
60
|
-
detailsMaker: (array: any[]) => "Array cannot be empty" | "Too many exclusions";
|
|
61
|
-
};
|
|
62
|
-
}, Code>>;
|
|
63
|
-
getErrorData: <Code extends Codes>(code: Code, ...params: Parameters<Errors.Register.DetailsMaker<{
|
|
64
|
-
"no-window-document-on-runtime": {
|
|
65
|
-
message: string;
|
|
66
|
-
detailsMaker: () => undefined;
|
|
67
|
-
};
|
|
68
|
-
"no-window-document-on-runtime-please-provide": {
|
|
69
|
-
message: string;
|
|
70
|
-
detailsMaker: (tips?: string) => string | undefined;
|
|
71
|
-
};
|
|
72
|
-
"impossible-to-pick-in-array": {
|
|
73
|
-
message: string;
|
|
74
|
-
detailsMaker: (array: any[]) => "Array cannot be empty" | "Too many exclusions";
|
|
75
|
-
};
|
|
76
|
-
}, Code>>) => Errors.Register.ErrorData<{
|
|
77
|
-
"no-window-document-on-runtime": {
|
|
78
|
-
message: string;
|
|
79
|
-
detailsMaker: () => undefined;
|
|
80
|
-
};
|
|
81
|
-
"no-window-document-on-runtime-please-provide": {
|
|
82
|
-
message: string;
|
|
83
|
-
detailsMaker: (tips?: string) => string | undefined;
|
|
84
|
-
};
|
|
85
|
-
"impossible-to-pick-in-array": {
|
|
86
|
-
message: string;
|
|
87
|
-
detailsMaker: (array: any[]) => "Array cannot be empty" | "Too many exclusions";
|
|
88
|
-
};
|
|
89
|
-
}, Code>;
|
|
90
|
-
getError: <Code extends Codes>(code: Code, ...params: Parameters<Errors.Register.DetailsMaker<{
|
|
91
|
-
"no-window-document-on-runtime": {
|
|
92
|
-
message: string;
|
|
93
|
-
detailsMaker: () => undefined;
|
|
94
|
-
};
|
|
95
|
-
"no-window-document-on-runtime-please-provide": {
|
|
96
|
-
message: string;
|
|
97
|
-
detailsMaker: (tips?: string) => string | undefined;
|
|
98
|
-
};
|
|
99
|
-
"impossible-to-pick-in-array": {
|
|
100
|
-
message: string;
|
|
101
|
-
detailsMaker: (array: any[]) => "Array cannot be empty" | "Too many exclusions";
|
|
102
|
-
};
|
|
103
|
-
}, Code>>) => {
|
|
104
|
-
code: Code;
|
|
105
|
-
details: ReturnType<Errors.Register.DetailsMaker<{
|
|
106
|
-
"no-window-document-on-runtime": {
|
|
107
|
-
message: string;
|
|
108
|
-
detailsMaker: () => undefined;
|
|
109
|
-
};
|
|
110
|
-
"no-window-document-on-runtime-please-provide": {
|
|
111
|
-
message: string;
|
|
112
|
-
detailsMaker: (tips?: string) => string | undefined;
|
|
113
|
-
};
|
|
114
|
-
"impossible-to-pick-in-array": {
|
|
115
|
-
message: string;
|
|
116
|
-
detailsMaker: (array: any[]) => "Array cannot be empty" | "Too many exclusions";
|
|
117
|
-
};
|
|
118
|
-
}, Code>>;
|
|
119
|
-
name: string;
|
|
120
|
-
message: string;
|
|
121
|
-
stack?: string;
|
|
122
|
-
cause?: unknown;
|
|
123
|
-
};
|
|
124
|
-
source: {
|
|
125
|
-
"no-window-document-on-runtime": {
|
|
126
|
-
message: string;
|
|
127
|
-
detailsMaker: () => undefined;
|
|
128
|
-
};
|
|
129
|
-
"no-window-document-on-runtime-please-provide": {
|
|
130
|
-
message: string;
|
|
131
|
-
detailsMaker: (tips?: string) => string | undefined;
|
|
132
|
-
};
|
|
133
|
-
"impossible-to-pick-in-array": {
|
|
134
|
-
message: string;
|
|
135
|
-
detailsMaker: (array: any[]) => "Array cannot be empty" | "Too many exclusions";
|
|
136
|
-
};
|
|
137
|
-
};
|
|
138
|
-
};
|
|
File without changes
|