motely-wasm 19.1.1 → 19.4.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.
Files changed (36) hide show
  1. package/README.md +134 -199
  2. package/dist/bcl/cancellation.d.mts +4 -7
  3. package/dist/bcl/cancellation.mjs +5 -10
  4. package/dist/bcl/collection.d.mts +1 -4
  5. package/dist/bcl/collection.mjs +1 -4
  6. package/dist/bcl/dictionary.d.mts +25 -0
  7. package/dist/bcl/dictionary.mjs +47 -0
  8. package/dist/bcl/event.d.mts +1 -1
  9. package/dist/bcl/event.mjs +1 -1
  10. package/dist/bcl/index.d.mts +1 -0
  11. package/dist/bcl/index.mjs +1 -0
  12. package/dist/bcl/list.d.mts +5 -8
  13. package/dist/bcl/list.mjs +9 -12
  14. package/dist/bin/motely-wasm.wasm +0 -0
  15. package/dist/dotnet/dotnet.native.js +1 -1
  16. package/dist/generated/imports.g.mjs +12 -2
  17. package/dist/generated/instances.g.mjs +111 -847
  18. package/dist/generated/modules/index.g.d.mts +1 -1222
  19. package/dist/generated/modules/index.g.mjs +1 -1975
  20. package/dist/generated/modules/motely/analysis.g.d.mts +49 -0
  21. package/dist/generated/modules/motely/analysis.g.mjs +6 -0
  22. package/dist/generated/modules/motely/enums.g.d.mts +136 -0
  23. package/dist/generated/modules/motely/enums.g.mjs +262 -0
  24. package/dist/generated/modules/motely/filters/jaml.g.d.mts +30 -0
  25. package/dist/generated/modules/motely/filters/jaml.g.mjs +48 -0
  26. package/dist/generated/modules/motely/filters.g.d.mts +8 -0
  27. package/dist/generated/modules/motely/filters.g.mjs +6 -0
  28. package/dist/generated/modules/motely/wasm.g.d.mts +36 -0
  29. package/dist/generated/modules/motely/wasm.g.mjs +42 -0
  30. package/dist/generated/modules/motely.g.d.mts +141 -0
  31. package/dist/generated/modules/motely.g.mjs +139 -0
  32. package/dist/generated/resources.g.mjs +1 -15
  33. package/dist/generated/serializer.g.mjs +0 -270
  34. package/package.json +10 -13
  35. package/motely-item-formats.d.ts +0 -535
  36. package/motely-item-formats.mjs +0 -525
package/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # motely-wasm
2
2
 
3
- WebAssembly package for [Motely](https://github.com/OptimusPi/MotelyJAML) the Balatro seed search engine, with filters written in JAML.
4
-
5
- The package ships `jaml.schema.json` (JSON Schema for the JAML filter format) at the package root, so editors can wire it up for autocomplete / validation without an extra fetch.
3
+ Balatro seed finder and per-seed analyzer, compiled to WebAssembly via [Bootsharp](https://bootsharp.com). Runs in browsers and Node/Deno/Bun.
6
4
 
7
5
  ## Install
8
6
 
@@ -10,255 +8,192 @@ The package ships `jaml.schema.json` (JSON Schema for the JAML filter format) at
10
8
  npm install motely-wasm
11
9
  ```
12
10
 
13
- ## Quick start
11
+ ## Module layout
14
12
 
15
- ```js
16
- import bootsharp, { Motely } from "motely-wasm";
13
+ The root barrel (`motely-wasm`) re-exports only the Bootsharp runtime (`boot`, `getStatus`, `manifest`, …). The Motely API and enums live in generated submodules and **must be imported directly** via the package's subpath exports:
17
14
 
18
- // Boot the .NET WASM runtime — no arguments needed.
19
- // The WASM binary is embedded in the package as base64 (single self-contained ESM).
20
- await bootsharp.boot();
15
+ ```js
16
+ import bootsharp from "motely-wasm";
17
+ import { Program as Motely } from "motely-wasm/motely/wasm";
18
+ import * as enums from "motely-wasm/motely/enums";
19
+ ```
21
20
 
22
- // A JAML filter — see https://github.com/OptimusPi/MotelyJAML for the language.
23
- const jaml = `
24
- name: WeeMonday
25
- deck: Erratic
26
- stake: Black
27
- must:
28
- - joker: WeeJoker
29
- antes: [1]
30
- `;
21
+ Additional submodules: `motely-wasm/motely/analysis`, `motely-wasm/motely/filters/jaml`, `motely-wasm/motely/filters`.
31
22
 
32
- // Validate before searching — returns "valid" or an error message.
33
- const status = Motely.validateJaml(jaml);
34
- if (status !== "valid") throw new Error(status);
23
+ ## Boot
35
24
 
36
- // One callback set per WASM load subscribe after boot, before .start().
37
- Motely.onScoredResult.subscribe(r => console.log("match:", r.seed, r.score));
38
- Motely.onProgress.subscribe(p => console.log(`${p.percentComplete.toFixed(1)}%`));
25
+ Binaries are sideloaded (published to `dist/bin/` as separate files). Pass the wasm bytes directly — `boot()` accepts either raw bytes or a URL root.
39
26
 
40
- const search = Motely.fromJaml(jaml).withSequentialSearch().start();
27
+ ### Node
41
28
 
42
- await search.waitForCompletionAsync();
43
- console.log("done:", search.totalSeedsSearched, "searched,", search.matchingSeeds, "matched");
44
- ```
29
+ ```js
30
+ import { readFile } from "node:fs/promises";
31
+ import { resolve, dirname } from "node:path";
32
+ import { fileURLToPath } from "node:url";
33
+ import bootsharp from "motely-wasm";
34
+ import { Program as Motely } from "motely-wasm/motely/wasm";
35
+ import * as enums from "motely-wasm/motely/enums";
45
36
 
46
- ## Booting
37
+ // [Import] callbacks must be assigned BEFORE boot.
38
+ Motely.reportWasmError = (message) => console.error("[WASM ERROR]", message);
39
+ Motely.jimmolateProbe = () => false; // required even when not using Jimmolate
47
40
 
48
- `bootsharp.boot()` initializes the .NET WASM runtime. Call it once before any `Motely.*` API.
41
+ // import.meta.resolve("motely-wasm") <pkg>/dist/index.mjs; dirname is already <pkg>/dist.
42
+ const distDir = dirname(fileURLToPath(import.meta.resolve("motely-wasm")));
43
+ const wasm = await readFile(resolve(distDir, "bin", bootsharp.manifest.wasm));
44
+ await bootsharp.boot({ wasm });
45
+ ```
49
46
 
50
- The WASM binary is **embedded** in the package as base64 — no separate files to serve, no CDN config, no `bin/` directory. One import, one `boot()`, done. Works in browser, Node, Web Workers, v0 previews, MCP apps, GitHub Pages — anywhere.
47
+ ### Browser
51
48
 
52
49
  ```js
53
50
  import bootsharp from "motely-wasm";
51
+ import { Program as Motely } from "motely-wasm/motely/wasm";
54
52
 
55
- if (bootsharp.getStatus() === bootsharp.BootStatus.Standby) {
56
- await bootsharp.boot();
57
- }
53
+ Motely.reportWasmError = (message) => console.error("[WASM ERROR]", message);
54
+ Motely.jimmolateProbe = () => false;
58
55
 
59
- console.log(bootsharp.getStatus()); // BootStatus.Booted
56
+ // Pass the URL root where dist/bin/ is served.
57
+ await bootsharp.boot("/assets/motely-wasm/dist/bin");
60
58
  ```
61
59
 
62
- **Publish gate (repo):** after `dotnet publish Motely.Wasm -c Release`, run
63
- `node Motely.Wasm/motely.test.mjs` and `node Motely.Wasm/pack-consumer-smoke.mjs`.
60
+ ## Core API
64
61
 
65
- ## JAML API
62
+ All methods are on the `Motely` (i.e. `Program`) namespace.
66
63
 
67
- ```js
68
- import { Motely } from "motely-wasm";
64
+ ### Parse a JAML filter
69
65
 
70
- // Validate a JAML filter string — returns "valid" on success, an error message on failure.
71
- const status = Motely.validateJaml(jaml);
66
+ ```js
67
+ const cfg = Motely.parseJaml(`
68
+ name: My Filter
69
+ deck: Red
70
+ stake: White
71
+ must:
72
+ - joker: Triboulet
73
+ antes: [1, 2, 3]
74
+ `);
75
+ ```
72
76
 
73
- // Human-readable explanation of what a JAML filter does.
74
- const explanation = Motely.explainJaml(jaml);
77
+ `parseJaml` throws on invalid YAML. Use `jamlToJson` / `jsonToJaml` to convert between representations. Use `explainJaml` to get a human-readable summary of a parsed config.
75
78
 
76
- // Inspect the search plan (tally column count, CSV header, labels).
77
- const plan = Motely.createPlan(jaml);
79
+ ### Search
78
80
 
79
- // Analyze specific seeds against a JAML filter.
80
- const result = Motely.analyzeJamlSeeds(jaml, ["ABCD1234", "XYZ99"]);
81
+ All `run*Search` methods return an `IMotelySearch` call `.start()` then listen to events, or call `.runSearchUntilCompletion()` / `await .runSearchAsync()` for synchronous / async use.
81
82
 
82
- // Engine version string.
83
- console.log(Motely.version());
83
+ ```js
84
+ // Subscribe to results before starting.
85
+ Motely.onSeedMatch.subscribe((seed) => console.log("match:", seed));
86
+ Motely.onProgress.subscribe((p) => console.log(`${p.percentComplete.toFixed(1)}%`));
87
+
88
+ // Sequential: scan all seeds in batch range [0, 1).
89
+ const search = Motely.runSequentialSearch(cfg, 0n, 1n);
90
+ await search.runSearchAsync();
91
+
92
+ // List: check specific seeds.
93
+ cfg.seeds = ["PIFREAK1", "DEADBEEF"];
94
+ const listSearch = Motely.runSeedListSearch(cfg);
95
+ listSearch.runSearchUntilCompletion();
96
+ console.log(listSearch.matchingSeeds); // bigint
97
+
98
+ // Scored: emits onScoredResult instead of onSeedMatch.
99
+ Motely.onScoredResult.subscribe((r) => console.log(r.seed, r.score));
100
+ const scored = Motely.runSequentialSearch(cfg, 0n, 1n);
101
+ await scored.runSearchAsync();
84
102
  ```
85
103
 
86
- ## JAML schema
104
+ ### Analyze a seed
87
105
 
88
- `motely-wasm` ships `jaml.schema.json` at the package root point your editor at it for
89
- autocomplete and inline validation while writing filters:
106
+ `jamlyzer` runs the JAMLyzer on all seeds in `cfg.seeds` and returns per-ante detail: boss blind, voucher, tags, shop queue, booster packs.
90
107
 
91
- ```json
92
- {
93
- "$schema": "node_modules/motely-wasm/jaml.schema.json"
108
+ ```js
109
+ cfg.seeds = ["PIFREAK1"];
110
+ const result = Motely.jamlyzer(cfg);
111
+ for (const { seed, analysis } of result.seeds) {
112
+ for (const ante of analysis.antes) {
113
+ console.log(`ante ${ante.ante} boss: ${enums.MotelyBossBlind[ante.boss]}`);
114
+ for (const { item, matched } of ante.shopQueue) {
115
+ console.log(" shop item", item.value, matched ? "(matched)" : "");
116
+ }
117
+ }
94
118
  }
95
119
  ```
96
120
 
97
- Or with a `# yaml-language-server` comment at the top of any `.jaml` / `.yml` filter file:
98
-
99
- ```yaml
100
- # yaml-language-server: $schema=node_modules/motely-wasm/jaml.schema.json
101
- name: WeeMonday
102
- deck: Erratic
103
- stake: Black
104
- must:
105
- - joker: WeeJoker
106
- antes: [1]
107
- ```
108
-
109
- JAML is a YAML dialect (JSON-compatible). A filter is a flat document:
110
-
111
- | Key | Purpose | Example |
112
- |---|---|---|
113
- | `name` | Human label | `WeeMonday` |
114
- | `deck` | Starting deck | `Erratic`, `Red`, `Ghost`, … |
115
- | `stake` | Difficulty floor | `White`, `Black`, `Gold`, … |
116
- | `must` | All clauses must match | list of clause objects |
117
- | `mustNot` | All clauses must NOT match | list of clause objects |
118
- | `should` | Scored clauses (use with `score:`) | list of clause objects |
121
+ ### Seed context (low-level)
119
122
 
120
- Each clause object names a target type (`joker`, `tarot`, `planet`, `voucher`, `tag`, `boss`) plus
121
- optional filters (`antes`, `sources`, `min`, `score`):
123
+ For custom logic, get a `MotelySingleSearchContext` and query PRNG streams directly:
122
124
 
123
- ```yaml
124
- must:
125
- - joker: WeeJoker # specific item name, or "Any"
126
- antes: [1, 2] # which antes to check (omit = all)
127
- sources:
128
- shopItems: [0, 1] # shop slot indices
129
- boosterPacks: [0] # pack slot indices
130
- - tag: NegativeTag
131
- antes: [1]
132
- - voucher: Telescope
133
- antes: [1, 2]
134
- min: 2 # appear at least N times across all listed antes
125
+ ```js
126
+ const ctx = Motely.seedContext("PIFREAK1", enums.MotelyDeck.Red, enums.MotelyStake.White);
127
+ const voucher = ctx.getAnteFirstVoucher(1);
128
+ console.log("ante 1 voucher:", enums.MotelyVoucher[voucher]);
135
129
  ```
136
130
 
137
- Use `Motely.validateJaml(jaml)` to check a filter string at runtime; `Motely.explainJaml(jaml)`
138
- returns a human-readable plan; `Motely.createPlan(jaml)` returns the scoring structure.
139
-
140
- ## Running a search
131
+ ## Jimmolate
141
132
 
142
- Use `Motely.createSearchSettings()`, `Motely.createNativeSearchSettings(name)`, or
143
- `Motely.fromJaml(jaml)` — then chain modes. Callbacks are registered once per WASM load on `Motely`.
133
+ Jimmolate is a custom JS scalar predicate that runs on every seed that passes the base JAML filter. Wire it up and call `enableJimmolate()` after boot:
144
134
 
145
135
  ```js
146
- import { Motely } from "motely-wasm";
147
-
148
- Motely.onSeedMatch.subscribe(seed => { /* */ });
149
- Motely.onScoredResult.subscribe(r => { /* … */ });
150
- Motely.onProgress.subscribe(p => { /* … */ });
151
-
152
- const search = Motely.fromJaml(jaml).withSequentialSearch().start();
153
-
154
- // Async (yields between batches — good on the main thread or in a Worker)
155
- await search.waitForCompletionAsync();
136
+ // Must be set BEFORE boot.
137
+ Motely.jimmolateProbe = (ctx) => {
138
+ // ctx is MotelySingleSearchContext same API as seedContext().
139
+ return ctx.getSeed().startsWith("PI");
140
+ };
156
141
 
157
- // or synchronous (blocks until done — only inside a Worker)
158
- // search.runSearchUntilCompletion();
142
+ await bootsharp.boot({ wasm });
143
+ Motely.enableJimmolate();
159
144
 
160
- console.log(search.isCompleted, search.totalSeedsSearched, search.matchingSeeds);
161
- search.cancel(); // stop early
145
+ cfg.seeds = ["PIFREAK1", "XYZABCDE"];
146
+ Motely.onSeedMatch.subscribe((s) => console.log("jimmolate match:", s));
147
+ Motely.runPassthroughListSearch(cfg.seeds).runSearchUntilCompletion();
162
148
  ```
163
149
 
164
- ## Events
165
-
166
- Callbacks are registered on `Motely` once per `bootsharp.boot()` — not on each settings
167
- chain. Every search started from that WASM load shares the same handlers; run one search at
168
- a time or use separate worker boots if you need isolated callbacks.
150
+ Use `runPassthroughListSearch` to skip the JAML filter entirely and let Jimmolate do all the culling.
169
151
 
170
- | Event | Payload |
171
- |---|---|
172
- | `Motely.onSeedMatch` | `string` — matching seed |
173
- | `Motely.onScoredResult` | `{ seed, score, tallies }` |
174
- | `Motely.onProgress` | `MotelyProgress` — `percentComplete`, `seedsSearched`, `matchingSeeds`, `seedsPerMillisecond`, `elapsedMilliseconds` |
175
- | `Motely.onFileChanges` | `Change[]` — fires when files change under a directory mounted via `Motely.mountRoot` (browser File System Access API, requires `Bootsharp.FileSystem`). Ignore if your app doesn't mount local directories. |
152
+ ## JAML quick reference
176
153
 
177
- Subscribe and unsubscribe:
154
+ ```yaml
155
+ name: string # optional display name
156
+ deck: Red # MotelyDeck value (case-insensitive)
157
+ stake: White # MotelyStake value
158
+ seeds: [] # pre-populate for list searches
159
+
160
+ must: # all clauses must match
161
+ - joker: Triboulet
162
+ antes: [1, 2] # ante numbers (1–8)
163
+ min: 1 # default 1
164
+ max: 2 # optional upper bound
165
+
166
+ should: # scoring; doesn't filter, raises score
167
+ - voucher: Telescope
168
+ antes: [1, 2]
169
+ score: 10
178
170
 
179
- ```js
180
- const handler = r => console.log(r.seed);
181
- Motely.onScoredResult.subscribe(handler);
182
- Motely.onScoredResult.unsubscribe(handler);
171
+ mustNot: # any match rejects the seed
172
+ - boss: TheHook
173
+ antes: [1]
183
174
  ```
184
175
 
185
- ## Submodule exports
186
-
187
- | Import path | Contents |
188
- |---|---|
189
- | `motely-wasm` | Default export: `boot`, `getStatus`, `BootStatus`. Named export: `Motely` (main API) |
190
- | `motely-wasm/motely` | `IMotelySearch`, `SearchSettings`, `MotelyProgress`, `MotelyScoredSeedResult`, `MotelyStreamKind` |
191
- | `motely-wasm/motely/enums` | All Balatro enums — `MotelyItemType`, `MotelyItemTypeCategory`, `MotelyJokerRarity`, `MotelyItemEdition`, `MotelyItemSeal`, `MotelyItemEnhancement`, `MotelyTag`, `MotelyVoucher`, `MotelyBoosterPack`, `MotelyDeck`, `MotelyStake`, `MotelyBossBlind`, etc. |
192
- | `motely-wasm/motely/filters` | `JamlAesthetic`, `JamlSearchPlan` |
193
- | `motely-wasm/motely/analysis` | `MotelyJamlyzerResult`, `MotelySeedAnalysis` |
194
- | `motely-wasm/bootsharp/file-system` | File-system interop (browser OPFS) — `PermissionMode`, `IFileMounter` |
195
-
196
- ## Using in a Web Worker
176
+ Clause types: `joker`, `voucher`, `boss`, `tag`, `spectral`, `tarot`, `planet`, `pack`.
177
+ Sources can be narrowed via `sources: { shopItems: [0, 1], boosterPacks: [0] }`.
197
178
 
198
- The WASM runtime is single-threaded. For a non-blocking UI, boot a runtime inside a
199
- Worker and drive it with messages. This mirrors the proven setup in the `jaml-ui`
200
- package's `searchWorker.ts`.
179
+ ## Enums
201
180
 
202
- ```js
203
- // search-worker.js
204
- import bootsharp, { Motely } from "motely-wasm";
205
-
206
- let currentSearch = null;
207
-
208
- self.onmessage = async ({ data }) => {
209
- if (data.type === "stop") {
210
- currentSearch?.cancel();
211
- self.postMessage({ type: "cancelled" });
212
- return;
213
- }
214
- if (data.type !== "start") return;
215
-
216
- try {
217
- if (bootsharp.getStatus() === bootsharp.BootStatus.Standby) {
218
- await bootsharp.boot();
219
- }
181
+ All enum values are available in the `enums` module:
220
182
 
221
- const onResult = r =>
222
- self.postMessage({ type: "result", seed: r.seed, score: r.score });
223
- const onProgress = p =>
224
- self.postMessage({ type: "progress", percent: p.percentComplete });
225
- Motely.onScoredResult.subscribe(onResult);
226
- Motely.onProgress.subscribe(onProgress);
227
-
228
- try {
229
- currentSearch = Motely.createSearch(data.jaml)
230
- .withThreadCount(1)
231
- .withSequentialSearch()
232
- .start();
233
- await currentSearch.waitForCompletionAsync();
234
- self.postMessage({
235
- type: "complete",
236
- total: Number(currentSearch.totalSeedsSearched),
237
- matched: Number(currentSearch.matchingSeeds),
238
- });
239
- } finally {
240
- Motely.onScoredResult.unsubscribe(onResult);
241
- Motely.onProgress.unsubscribe(onProgress);
242
- currentSearch = null;
243
- }
244
- } catch (error) {
245
- self.postMessage({ type: "error", message: String(error?.message ?? error) });
246
- }
247
- };
183
+ - `MotelyDeck` Red, Blue, Yellow, Green, Black, Magic, Nebula, Ghost, Abandoned, Checkered, Zodiac, Painted, Anaglyph, Plasma, Erratic
184
+ - `MotelyStake` — White, Red, Green, Black, Blue, Purple, Orange, Gold
185
+ - `MotelyBossBlind`, `MotelyVoucher`, `MotelyTag`, `MotelyBoosterPack`
248
186
 
249
- self.postMessage({ type: "ready" });
250
- ```
187
+ ## Utility
251
188
 
252
189
  ```js
253
- // main thread
254
- const worker = new Worker(new URL("./search-worker.js", import.meta.url), { type: "module" });
190
+ // Convert between JAML text and JSON.
191
+ const json = Motely.jamlToJson(jamlText);
192
+ const jaml = Motely.jsonToJaml(json);
255
193
 
256
- worker.onmessage = ({ data }) => {
257
- if (data.type === "result") console.log("match:", data.seed, data.score);
258
- if (data.type === "progress") console.log(`${data.percent.toFixed(1)}%`);
259
- if (data.type === "complete") console.log("done:", data.total, data.matched);
260
- };
194
+ // Human-readable description of a parsed config.
195
+ const explanation = Motely.explainJaml(cfg);
261
196
 
262
- worker.postMessage({ type: "start", jaml });
263
- // worker.postMessage({ type: "stop" }); // cancel early
197
+ // List available native (non-JAML) filter names.
198
+ const names = Motely.nativeFilterNames();
264
199
  ```
@@ -1,14 +1,11 @@
1
1
  import { Event } from "./event.mjs";
2
- /** JavaScript counterpart of the C# <c>System.Threading.CancellationToken</c> specialization.
3
- * Hand-rolled instances are constructed in user code and passed across the interop boundary;
4
- * instances received from C# are wrapped into generated proxies that expose the same shape. */
2
+ /** A cancellation token compatible with C# `CancellationToken`. */
5
3
  export declare class CancellationToken {
6
- private cancelled;
7
- /** Fires when the token is cancelled. */
4
+ /** Occurs when the token is cancelled. */
8
5
  readonly onCancellationRequested: Event<[]>;
9
6
  /** Whether cancellation has been requested. */
10
7
  get isCancellationRequested(): boolean;
11
- /** Signal cancellation: flips <see cref="isCancellationRequested"/> and broadcasts
12
- * <see cref="onCancellationRequested"/>. Idempotent. */
8
+ private cancelled;
9
+ /** Signal cancellation. */
13
10
  cancel(): void;
14
11
  }
@@ -1,17 +1,12 @@
1
1
  import { Event } from "./event.mjs";
2
- /** JavaScript counterpart of the C# <c>System.Threading.CancellationToken</c> specialization.
3
- * Hand-rolled instances are constructed in user code and passed across the interop boundary;
4
- * instances received from C# are wrapped into generated proxies that expose the same shape. */
2
+ /** A cancellation token compatible with C# `CancellationToken`. */
5
3
  export class CancellationToken {
6
- cancelled = false;
7
- /** Fires when the token is cancelled. */
4
+ /** Occurs when the token is cancelled. */
8
5
  onCancellationRequested = new Event();
9
6
  /** Whether cancellation has been requested. */
10
- get isCancellationRequested() {
11
- return this.cancelled;
12
- }
13
- /** Signal cancellation: flips <see cref="isCancellationRequested"/> and broadcasts
14
- * <see cref="onCancellationRequested"/>. Idempotent. */
7
+ get isCancellationRequested() { return this.cancelled; }
8
+ cancelled = false;
9
+ /** Signal cancellation. */
15
10
  cancel() {
16
11
  if (this.cancelled)
17
12
  return;
@@ -1,7 +1,4 @@
1
- /** JavaScript counterpart of the C# <c>Bootsharp.Collection&lt;T&gt;</c> specialization of
2
- * <c>System.Collections.Generic.ICollection&lt;T&gt;</c>. Hand-rolled instances are constructed in
3
- * user code and passed across the interop boundary; instances received from C# are wrapped into
4
- * generated proxies that expose the same shape. */
1
+ /** A collection of items compatible with C# `ICollection<T>`. */
5
2
  export declare class Collection<T> {
6
3
  protected readonly items: T[];
7
4
  constructor(items?: Iterable<T>);
@@ -1,7 +1,4 @@
1
- /** JavaScript counterpart of the C# <c>Bootsharp.Collection&lt;T&gt;</c> specialization of
2
- * <c>System.Collections.Generic.ICollection&lt;T&gt;</c>. Hand-rolled instances are constructed in
3
- * user code and passed across the interop boundary; instances received from C# are wrapped into
4
- * generated proxies that expose the same shape. */
1
+ /** A collection of items compatible with C# `ICollection<T>`. */
5
2
  export class Collection {
6
3
  items;
7
4
  constructor(items) {
@@ -0,0 +1,25 @@
1
+ /** A dictionary of key-value pairs compatible with C# `IDictionary<TKey, TValue>`. */
2
+ export declare class Dictionary<TKey, TValue> {
3
+ protected readonly map: Map<TKey, TValue>;
4
+ constructor(entries?: Iterable<[TKey, TValue]>);
5
+ /** Number of key-value pairs in the dictionary. */
6
+ get count(): number;
7
+ /** Associates the specified value with the specified key. */
8
+ add(key: TKey, value: TValue): void;
9
+ /** Whether the dictionary contains the specified key. */
10
+ containsKey(key: TKey): boolean;
11
+ /** Removes the value with the specified key from the dictionary.
12
+ * @returns true when the key was removed; false when it wasn't found. */
13
+ remove(key: TKey): boolean;
14
+ /** Removes all key-value pairs from the dictionary. */
15
+ clear(): void;
16
+ /** Returns the value associated with the specified key. */
17
+ getAt(key: TKey): TValue;
18
+ /** Associates the specified value with the specified key. */
19
+ setAt(key: TKey, value: TValue): void;
20
+ /** Returns a fresh array with a snapshot of the current keys. */
21
+ getKeys(): TKey[];
22
+ /** Returns a fresh array with a snapshot of the current values. */
23
+ getValues(): TValue[];
24
+ [Symbol.iterator](): IterableIterator<[TKey, TValue]>;
25
+ }
@@ -0,0 +1,47 @@
1
+ /** A dictionary of key-value pairs compatible with C# `IDictionary<TKey, TValue>`. */
2
+ export class Dictionary {
3
+ map;
4
+ constructor(entries) {
5
+ this.map = new Map(entries);
6
+ }
7
+ /** Number of key-value pairs in the dictionary. */
8
+ get count() {
9
+ return this.map.size;
10
+ }
11
+ /** Associates the specified value with the specified key. */
12
+ add(key, value) {
13
+ this.map.set(key, value);
14
+ }
15
+ /** Whether the dictionary contains the specified key. */
16
+ containsKey(key) {
17
+ return this.map.has(key);
18
+ }
19
+ /** Removes the value with the specified key from the dictionary.
20
+ * @returns true when the key was removed; false when it wasn't found. */
21
+ remove(key) {
22
+ return this.map.delete(key);
23
+ }
24
+ /** Removes all key-value pairs from the dictionary. */
25
+ clear() {
26
+ this.map.clear();
27
+ }
28
+ /** Returns the value associated with the specified key. */
29
+ getAt(key) {
30
+ return this.map.get(key);
31
+ }
32
+ /** Associates the specified value with the specified key. */
33
+ setAt(key, value) {
34
+ this.map.set(key, value);
35
+ }
36
+ /** Returns a fresh array with a snapshot of the current keys. */
37
+ getKeys() {
38
+ return Array.from(this.map.keys());
39
+ }
40
+ /** Returns a fresh array with a snapshot of the current values. */
41
+ getValues() {
42
+ return Array.from(this.map.values());
43
+ }
44
+ [Symbol.iterator]() {
45
+ return this.map[Symbol.iterator]();
46
+ }
47
+ }
@@ -20,7 +20,7 @@ export type EventOptions = {
20
20
  /** Custom warnings handler; by default <code>console.warn</code> is used. */
21
21
  warn?: (message: string) => void;
22
22
  };
23
- /** Allows attaching handlers and broadcasting events. */
23
+ /** Allows attaching handlers and broadcasting events; compatible with C# events. */
24
24
  export declare class Event<T extends unknown[]> implements EventBroadcaster<T>, EventSubscriber<T> {
25
25
  private readonly handlers;
26
26
  private readonly warn;
@@ -1,4 +1,4 @@
1
- /** Allows attaching handlers and broadcasting events. */
1
+ /** Allows attaching handlers and broadcasting events; compatible with C# events. */
2
2
  export class Event {
3
3
  handlers = new Map();
4
4
  warn;
@@ -1,4 +1,5 @@
1
1
  export * from "./event.mjs";
2
2
  export * from "./collection.mjs";
3
3
  export * from "./list.mjs";
4
+ export * from "./dictionary.mjs";
4
5
  export * from "./cancellation.mjs";
@@ -1,4 +1,5 @@
1
1
  export * from "./event.mjs";
2
2
  export * from "./collection.mjs";
3
3
  export * from "./list.mjs";
4
+ export * from "./dictionary.mjs";
4
5
  export * from "./cancellation.mjs";
@@ -1,17 +1,14 @@
1
1
  import { Collection } from "./collection.mjs";
2
- /** JavaScript counterpart of the C# <c>System.Collections.Generic.IList&lt;T&gt;</c> specialization.
3
- * Extends <see cref="Collection"/> with index-based access; like the collection, hand-rolled
4
- * instances are passed across the interop boundary, while instances received from C# are wrapped
5
- * into generated proxies that expose the same shape. */
2
+ /** A list of items compatible with C# `IList<T>`. */
6
3
  export declare class List<T> extends Collection<T> {
4
+ /** Returns the item at the specified index. */
5
+ getAt(index: number): T;
6
+ /** Assigns the specified item at the specified index. */
7
+ setAt(index: number, item: T): void;
7
8
  /** Returns the index of the first occurrence of the specified item, or -1 when not found. */
8
9
  indexOf(item: T): number;
9
10
  /** Inserts the specified item at the specified index. */
10
11
  insert(index: number, item: T): void;
11
12
  /** Removes the item at the specified index. */
12
13
  removeAt(index: number): void;
13
- /** Returns the item at the specified index. */
14
- getAt(index: number): T;
15
- /** Assigns the specified item at the specified index. */
16
- setAt(index: number, item: T): void;
17
14
  }
package/dist/bcl/list.mjs CHANGED
@@ -1,9 +1,14 @@
1
1
  import { Collection } from "./collection.mjs";
2
- /** JavaScript counterpart of the C# <c>System.Collections.Generic.IList&lt;T&gt;</c> specialization.
3
- * Extends <see cref="Collection"/> with index-based access; like the collection, hand-rolled
4
- * instances are passed across the interop boundary, while instances received from C# are wrapped
5
- * into generated proxies that expose the same shape. */
2
+ /** A list of items compatible with C# `IList<T>`. */
6
3
  export class List extends Collection {
4
+ /** Returns the item at the specified index. */
5
+ getAt(index) {
6
+ return this.items[index];
7
+ }
8
+ /** Assigns the specified item at the specified index. */
9
+ setAt(index, item) {
10
+ this.items[index] = item;
11
+ }
7
12
  /** Returns the index of the first occurrence of the specified item, or -1 when not found. */
8
13
  indexOf(item) {
9
14
  return this.items.indexOf(item);
@@ -16,12 +21,4 @@ export class List extends Collection {
16
21
  removeAt(index) {
17
22
  this.items.splice(index, 1);
18
23
  }
19
- /** Returns the item at the specified index. */
20
- getAt(index) {
21
- return this.items[index];
22
- }
23
- /** Assigns the specified item at the specified index. */
24
- setAt(index, item) {
25
- this.items[index] = item;
26
- }
27
24
  }
Binary file