@spotlab/range 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/README.md +58 -2
- package/dist/errors.d.ts +43 -0
- package/dist/generated/constants.d.ts +6 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +661 -0
- package/dist/init.d.ts +14 -0
- package/dist/types.d.ts +17 -0
- package/package.json +57 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 James DiGioia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,59 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @spotlab/range
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A poker range-string parser and serializer, compiled from Rust
|
|
4
|
+
([`spotlab-range`](../../crates/spotlab-range)) to wasm. No React, no DOM —
|
|
5
|
+
suitable for a Node script, a server, a CLI, or a browser/Electron renderer.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { initRange, parseRange, formatRange } from "@spotlab/range";
|
|
9
|
+
|
|
10
|
+
await initRange(); // once, at startup; idempotent, returns the same promise
|
|
11
|
+
const hands = parseRange("AKs:0.5,QQ"); // synchronous from here on
|
|
12
|
+
formatRange(hands); // "AKs:0.5,QQ"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Why an explicit `initRange()`
|
|
16
|
+
|
|
17
|
+
Instantiating a wasm module is asynchronous, and top-level `await` is not
|
|
18
|
+
available in every consumer's bundle output — a CommonJS build has no way to
|
|
19
|
+
express it. Call `initRange()` once at startup and await it; every other export
|
|
20
|
+
is synchronous afterward, and throws `RangeNotInitializedError` if called
|
|
21
|
+
before it resolves. `initRange()` itself is idempotent on success and
|
|
22
|
+
retryable after a failure — a rejected call does not poison later calls.
|
|
23
|
+
|
|
24
|
+
## No bundler step for consumers
|
|
25
|
+
|
|
26
|
+
The `.wasm` binary is base64-inlined into the published JavaScript at build
|
|
27
|
+
time (`scripts/inline-wasm.mjs`), so this package ships as a single
|
|
28
|
+
self-contained ESM file with no binary asset, no `.wasm` import, and no
|
|
29
|
+
loader configuration for the consumer — even though the package's own build
|
|
30
|
+
bundles with Vite. The trade is roughly 33% extra payload size for
|
|
31
|
+
eliminating bundler/asset-loading configuration across every consuming
|
|
32
|
+
environment. The build asserts a size budget on the shipped bundle
|
|
33
|
+
(`scripts/check-size.mjs`); see the package's `build:size` script.
|
|
34
|
+
|
|
35
|
+
Concretely: the release `.wasm` is 132 KiB and the inlined module 176 KiB,
|
|
36
|
+
and the overhead survives compression largely intact — roughly 28% gzipped.
|
|
37
|
+
That is a deliberate trade for consumers loading from disk, where the
|
|
38
|
+
absolute difference is immaterial. If you are serving this over a network
|
|
39
|
+
and the overhead is material for you, please open an issue — shipping the
|
|
40
|
+
raw `.wasm` alongside the inlined build, as a separate entry point, is
|
|
41
|
+
straightforward and worth doing for a real use case.
|
|
42
|
+
|
|
43
|
+
## Errors
|
|
44
|
+
|
|
45
|
+
- `SpotlabInputError` — the input you gave was rejected: a range string
|
|
46
|
+
failed the grammar, a hand token didn't resolve to a combo, or a frequency
|
|
47
|
+
fell outside `[0, 1]`. `.kind` is `"invalid"` or `"frequencyOutOfRange"`.
|
|
48
|
+
Shared across `@spotlab` packages — a sibling package should reuse this
|
|
49
|
+
class rather than minting its own.
|
|
50
|
+
- `RangeInitError` — `initRange()` could not instantiate the wasm module.
|
|
51
|
+
Carries the underlying failure as `.cause`.
|
|
52
|
+
- `RangeNotInitializedError` — a function export other than `initRange` was
|
|
53
|
+
called before it resolved. `RANKS`, `SUITS`, and `COMBOS` are static data
|
|
54
|
+
generated from the Rust crate's own constants and are readable immediately,
|
|
55
|
+
with no `initRange()` call required.
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
MIT — see `LICENSE`.
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The input you gave an `@spotlab/range` export was rejected: a range string
|
|
3
|
+
* failed the grammar, a hand token didn't resolve to a combo, or a frequency
|
|
4
|
+
* fell outside `[0, 1]`. Mirrors the Rust crate's `RangeParseError`,
|
|
5
|
+
* deserialized from the `#[serde(tag = "type", content = "data")]` shape the
|
|
6
|
+
* wasm shim throws across the boundary.
|
|
7
|
+
*
|
|
8
|
+
* This is the shared input-rejection error for `@spotlab` packages — a
|
|
9
|
+
* sibling package (e.g. `@spotlab/hand-matrix`) should reuse this class
|
|
10
|
+
* rather than minting its own.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately not named `RangeError` — that is a JavaScript built-in.
|
|
13
|
+
*/
|
|
14
|
+
export declare class SpotlabInputError extends Error {
|
|
15
|
+
readonly name = "SpotlabInputError";
|
|
16
|
+
readonly kind: "invalid" | "frequencyOutOfRange";
|
|
17
|
+
readonly combo?: string;
|
|
18
|
+
readonly frequency?: number;
|
|
19
|
+
private constructor();
|
|
20
|
+
/**
|
|
21
|
+
* Build a `SpotlabInputError` from whatever the wasm boundary threw. A
|
|
22
|
+
* well-formed `RawSpotlabInputError` (itself the Rust crate's
|
|
23
|
+
* `RangeParseError`, serialized across the boundary) round-trips as-is;
|
|
24
|
+
* anything else (e.g. a raw deserialization failure at the `format_range` /
|
|
25
|
+
* `to_combo_weights` JS-value boundary, which cannot carry the structured
|
|
26
|
+
* Rust error) is wrapped as `kind: "invalid"` with the failure's own
|
|
27
|
+
* message.
|
|
28
|
+
*/
|
|
29
|
+
static fromWasm(raw: unknown): SpotlabInputError;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* `initRange()` could not instantiate the wasm module — corrupt bytes, no
|
|
33
|
+
* `WebAssembly` global, out of memory. Carries the underlying failure as
|
|
34
|
+
* `cause`. `initRange()` is retryable after this: see its own doc comment.
|
|
35
|
+
*/
|
|
36
|
+
export declare class RangeInitError extends Error {
|
|
37
|
+
readonly name = "RangeInitError";
|
|
38
|
+
}
|
|
39
|
+
/** Thrown when any export other than `initRange` is called before it has resolved. */
|
|
40
|
+
export declare class RangeNotInitializedError extends Error {
|
|
41
|
+
readonly name = "RangeNotInitializedError";
|
|
42
|
+
constructor(fnName: string);
|
|
43
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** The 13 rank characters, ascending (`'2'..'A'`). */
|
|
2
|
+
export declare const RANKS: readonly ["2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A"];
|
|
3
|
+
/** The 4 suit characters (`c`, `d`, `h`, `s`). */
|
|
4
|
+
export declare const SUITS: readonly ["c", "d", "h", "s"];
|
|
5
|
+
/** The 169 canonical combos in the hand matrix's row-major order. */
|
|
6
|
+
export declare const COMBOS: readonly ["AA", "AKs", "AQs", "AJs", "ATs", "A9s", "A8s", "A7s", "A6s", "A5s", "A4s", "A3s", "A2s", "AKo", "KK", "KQs", "KJs", "KTs", "K9s", "K8s", "K7s", "K6s", "K5s", "K4s", "K3s", "K2s", "AQo", "KQo", "QQ", "QJs", "QTs", "Q9s", "Q8s", "Q7s", "Q6s", "Q5s", "Q4s", "Q3s", "Q2s", "AJo", "KJo", "QJo", "JJ", "JTs", "J9s", "J8s", "J7s", "J6s", "J5s", "J4s", "J3s", "J2s", "ATo", "KTo", "QTo", "JTo", "TT", "T9s", "T8s", "T7s", "T6s", "T5s", "T4s", "T3s", "T2s", "A9o", "K9o", "Q9o", "J9o", "T9o", "99", "98s", "97s", "96s", "95s", "94s", "93s", "92s", "A8o", "K8o", "Q8o", "J8o", "T8o", "98o", "88", "87s", "86s", "85s", "84s", "83s", "82s", "A7o", "K7o", "Q7o", "J7o", "T7o", "97o", "87o", "77", "76s", "75s", "74s", "73s", "72s", "A6o", "K6o", "Q6o", "J6o", "T6o", "96o", "86o", "76o", "66", "65s", "64s", "63s", "62s", "A5o", "K5o", "Q5o", "J5o", "T5o", "95o", "85o", "75o", "65o", "55", "54s", "53s", "52s", "A4o", "K4o", "Q4o", "J4o", "T4o", "94o", "84o", "74o", "64o", "54o", "44", "43s", "42s", "A3o", "K3o", "Q3o", "J3o", "T3o", "93o", "83o", "73o", "63o", "53o", "43o", "33", "32s", "A2o", "K2o", "Q2o", "J2o", "T2o", "92o", "82o", "72o", "62o", "52o", "42o", "32o", "22"];
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Hand, HandFrequency } from './types.js';
|
|
2
|
+
export { initRange } from './init.js';
|
|
3
|
+
export declare const RANKS: readonly string[];
|
|
4
|
+
export declare const SUITS: readonly string[];
|
|
5
|
+
export declare const COMBOS: readonly string[];
|
|
6
|
+
export { SpotlabInputError, RangeInitError, RangeNotInitializedError, } from './errors.js';
|
|
7
|
+
export { Hand } from './types.js';
|
|
8
|
+
export type { HandFrequency } from './types.js';
|
|
9
|
+
/** Parse a range string into its expanded hand-keyed form. */
|
|
10
|
+
export declare function parseRange(input: string): HandFrequency[];
|
|
11
|
+
/** Render a hand-keyed range back to its compacted string form. */
|
|
12
|
+
export declare function formatRange(hands: HandFrequency[]): string;
|
|
13
|
+
/** The canonical combo a hand token belongs to (e.g. `AsKd` -> `AKo`). */
|
|
14
|
+
export declare function comboOf(hand: string): string;
|
|
15
|
+
/** Every concrete hand of a canonical combo, already canonical. */
|
|
16
|
+
export declare function handsOfCombo(combo: string): Hand[];
|
|
17
|
+
/**
|
|
18
|
+
* Collapse a hand-keyed range to the mean weight per combo. `serde-wasm-bindgen`
|
|
19
|
+
* serializes the underlying Rust map as a JS `Map`, not a plain object.
|
|
20
|
+
*/
|
|
21
|
+
export declare function toComboWeights(hands: HandFrequency[]): Map<string, number>;
|
|
22
|
+
/** Every hand in the deck at weight 1 — the unconstrained range. */
|
|
23
|
+
export declare function fullRange(): HandFrequency[];
|