browser-chess-engine 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
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,24 @@
1
- # Temporary Holding Version
1
+ # browser-chess-engine
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A classical chess engine for the browser. Search runs in a Web Worker, so the page stays responsive. Strength is a number from 800 to 2200. Depth, time, node limit, and skill can override that curve.
4
+
5
+ The 2200 setting is the top of the curve, not a certified rating. It is depth 10, 1764 ms, and skill 13.
6
+
7
+ ```ts
8
+ import { ChessEngine, STRENGTH_PRESETS } from "browser-chess-engine";
9
+
10
+ const engine = await ChessEngine.create({ rating: STRENGTH_PRESETS.master });
11
+ engine.onInfo((info) => console.log(info.depth, info.scoreCp, info.pv.join(" ")));
12
+
13
+ await engine.play("e2e4");
14
+ const result = await engine.go();
15
+ await engine.play(result.bestMove);
16
+ ```
17
+
18
+ `setStrength(1800)` selects depth, time, and skill from the curve and clears overrides. `setLimits({ depth: 12, movetimeMs: 800 })` replaces individual fields. `analyze()` returns the best move at the current depth and time and does not use the opening book. `go()` may pick a close alternative when skill is below 20.
19
+
20
+ Presets: beginner 800, casual 1100, intermediate 1400, club 1700, expert 1900, candidate 2100, master 2200.
21
+
22
+ Immediate `stop()` needs a cross-origin isolated page (`Cross-Origin-Opener-Policy: same-origin` and `Cross-Origin-Embedder-Policy: require-corp`). Without those headers, a search still stops when its time or depth budget is spent. `engine.canStopImmediately` reports which case the page is in.
23
+
24
+ The package is ESM. A bundler that understands `new URL("./engine.worker.js", import.meta.url)` will copy the worker and the wasm file. The worker and `chess_engine_wasm_bg.wasm` ship in the package next to the entry.
@@ -0,0 +1,82 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ export class WasmEngine {
5
+ free(): void;
6
+ [Symbol.dispose](): void;
7
+ fen(): string;
8
+ in_check(): boolean;
9
+ legal_moves(): string;
10
+ constructor(hash_mb: number);
11
+ new_game(): void;
12
+ /**
13
+ * `perft` node count. Values above 2^53 are not expected for interactive depths.
14
+ */
15
+ perft(depth: number): number;
16
+ play(uci: string): void;
17
+ /**
18
+ * Sentinel values: -1 leaves the rating curve in charge.
19
+ * `movetime_ms` of 0, when not -1, means no time limit.
20
+ */
21
+ search(rating: number, depth: number, movetime_ms: number, nodes: number, skill: number, seed: number, book: number, contempt: number, on_info?: Function | null, poll?: Function | null): string;
22
+ set_fen(fen: string): void;
23
+ side(): string;
24
+ status(): string;
25
+ stop(): void;
26
+ }
27
+
28
+ export function preview_limits(rating: number, depth: number, movetime_ms: number, nodes: number, skill: number, book: number, contempt: number): string;
29
+
30
+ export function rating_curve(rating: number): string;
31
+
32
+ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
33
+
34
+ export interface InitOutput {
35
+ readonly memory: WebAssembly.Memory;
36
+ readonly __wbg_wasmengine_free: (a: number, b: number) => void;
37
+ readonly preview_limits: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void;
38
+ readonly rating_curve: (a: number, b: number) => void;
39
+ readonly wasmengine_fen: (a: number, b: number) => void;
40
+ readonly wasmengine_in_check: (a: number) => number;
41
+ readonly wasmengine_legal_moves: (a: number, b: number) => void;
42
+ readonly wasmengine_new: (a: number) => number;
43
+ readonly wasmengine_new_game: (a: number) => void;
44
+ readonly wasmengine_perft: (a: number, b: number) => number;
45
+ readonly wasmengine_play: (a: number, b: number, c: number, d: number) => void;
46
+ readonly wasmengine_search: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number) => void;
47
+ readonly wasmengine_set_fen: (a: number, b: number, c: number, d: number) => void;
48
+ readonly wasmengine_side: (a: number, b: number) => void;
49
+ readonly wasmengine_status: (a: number, b: number) => void;
50
+ readonly wasmengine_stop: (a: number) => void;
51
+ readonly __wbindgen_export: (a: number) => void;
52
+ readonly __wbindgen_export2: () => number;
53
+ readonly __wbindgen_externrefs: WebAssembly.Table;
54
+ readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
55
+ readonly __wbindgen_export3: (a: number, b: number, c: number) => void;
56
+ readonly __wbindgen_export4: (a: number, b: number) => number;
57
+ readonly __wbindgen_export5: (a: number, b: number, c: number, d: number) => number;
58
+ readonly __wbindgen_export6: (a: number) => void;
59
+ readonly __wbindgen_start: () => void;
60
+ }
61
+
62
+ export type SyncInitInput = BufferSource | WebAssembly.Module;
63
+
64
+ /**
65
+ * Instantiates the given `module`, which can either be bytes or
66
+ * a precompiled `WebAssembly.Module`.
67
+ *
68
+ * @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
69
+ *
70
+ * @returns {InitOutput}
71
+ */
72
+ export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
73
+
74
+ /**
75
+ * If `module_or_path` is {RequestInfo} or {URL}, makes a request and
76
+ * for everything else, calls `WebAssembly.instantiate` directly.
77
+ *
78
+ * @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
79
+ *
80
+ * @returns {Promise<InitOutput>}
81
+ */
82
+ export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;
@@ -0,0 +1,543 @@
1
+ /* @ts-self-types="./chess_engine_wasm.d.ts" */
2
+
3
+ export class WasmEngine {
4
+ __destroy_into_raw() {
5
+ const ptr = this.__wbg_ptr;
6
+ this.__wbg_ptr = 0;
7
+ WasmEngineFinalization.unregister(this);
8
+ return ptr;
9
+ }
10
+ free() {
11
+ const ptr = this.__destroy_into_raw();
12
+ wasm.__wbg_wasmengine_free(ptr, 0);
13
+ }
14
+ /**
15
+ * @returns {string}
16
+ */
17
+ fen() {
18
+ let deferred1_0;
19
+ let deferred1_1;
20
+ try {
21
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16) >>> 0;
22
+ wasm.wasmengine_fen(retptr, this.__wbg_ptr);
23
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
24
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
25
+ deferred1_0 = r0;
26
+ deferred1_1 = r1;
27
+ return getStringFromWasm0(r0, r1);
28
+ } finally {
29
+ wasm.__wbindgen_add_to_stack_pointer(16);
30
+ wasm.__wbindgen_export3(deferred1_0, deferred1_1, 1);
31
+ }
32
+ }
33
+ /**
34
+ * @returns {boolean}
35
+ */
36
+ in_check() {
37
+ const ret = wasm.wasmengine_in_check(this.__wbg_ptr);
38
+ return ret !== 0;
39
+ }
40
+ /**
41
+ * @returns {string}
42
+ */
43
+ legal_moves() {
44
+ let deferred1_0;
45
+ let deferred1_1;
46
+ try {
47
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16) >>> 0;
48
+ wasm.wasmengine_legal_moves(retptr, this.__wbg_ptr);
49
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
50
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
51
+ deferred1_0 = r0;
52
+ deferred1_1 = r1;
53
+ return getStringFromWasm0(r0, r1);
54
+ } finally {
55
+ wasm.__wbindgen_add_to_stack_pointer(16);
56
+ wasm.__wbindgen_export3(deferred1_0, deferred1_1, 1);
57
+ }
58
+ }
59
+ /**
60
+ * @param {number} hash_mb
61
+ */
62
+ constructor(hash_mb) {
63
+ const ret = wasm.wasmengine_new(hash_mb);
64
+ this.__wbg_ptr = ret;
65
+ WasmEngineFinalization.register(this, this.__wbg_ptr, this);
66
+ return this;
67
+ }
68
+ new_game() {
69
+ wasm.wasmengine_new_game(this.__wbg_ptr);
70
+ }
71
+ /**
72
+ * `perft` node count. Values above 2^53 are not expected for interactive depths.
73
+ * @param {number} depth
74
+ * @returns {number}
75
+ */
76
+ perft(depth) {
77
+ const ret = wasm.wasmengine_perft(this.__wbg_ptr, depth);
78
+ return ret;
79
+ }
80
+ /**
81
+ * @param {string} uci
82
+ */
83
+ play(uci) {
84
+ try {
85
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16) >>> 0;
86
+ const ptr0 = passStringToWasm0(uci, wasm.__wbindgen_export4, wasm.__wbindgen_export5);
87
+ const len0 = WASM_VECTOR_LEN;
88
+ wasm.wasmengine_play(retptr, this.__wbg_ptr, ptr0, len0);
89
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
90
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
91
+ if (r1) {
92
+ throw takeFromExternrefTable0(r0);
93
+ }
94
+ } finally {
95
+ wasm.__wbindgen_add_to_stack_pointer(16);
96
+ }
97
+ }
98
+ /**
99
+ * Sentinel values: -1 leaves the rating curve in charge.
100
+ * `movetime_ms` of 0, when not -1, means no time limit.
101
+ * @param {number} rating
102
+ * @param {number} depth
103
+ * @param {number} movetime_ms
104
+ * @param {number} nodes
105
+ * @param {number} skill
106
+ * @param {number} seed
107
+ * @param {number} book
108
+ * @param {number} contempt
109
+ * @param {Function | null} [on_info]
110
+ * @param {Function | null} [poll]
111
+ * @returns {string}
112
+ */
113
+ search(rating, depth, movetime_ms, nodes, skill, seed, book, contempt, on_info, poll) {
114
+ let deferred9_0;
115
+ let deferred9_1;
116
+ try {
117
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16) >>> 0;
118
+ const val1 = +rating;
119
+ const val2 = +depth;
120
+ const val3 = +movetime_ms;
121
+ const val4 = +nodes;
122
+ const val5 = +skill;
123
+ const val6 = +seed;
124
+ const val7 = +book;
125
+ const val8 = +contempt;
126
+ wasm.wasmengine_search(retptr, this.__wbg_ptr, val1, val2, val3, val4, val5, val6, val7, val8, isLikeNone(on_info) ? 0 : addToExternrefTable0(on_info), isLikeNone(poll) ? 0 : addToExternrefTable0(poll));
127
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
128
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
129
+ deferred9_0 = r0;
130
+ deferred9_1 = r1;
131
+ return getStringFromWasm0(r0, r1);
132
+ } finally {
133
+ wasm.__wbindgen_add_to_stack_pointer(16);
134
+ wasm.__wbindgen_export3(deferred9_0, deferred9_1, 1);
135
+ }
136
+ }
137
+ /**
138
+ * @param {string} fen
139
+ */
140
+ set_fen(fen) {
141
+ try {
142
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16) >>> 0;
143
+ const ptr0 = passStringToWasm0(fen, wasm.__wbindgen_export4, wasm.__wbindgen_export5);
144
+ const len0 = WASM_VECTOR_LEN;
145
+ wasm.wasmengine_set_fen(retptr, this.__wbg_ptr, ptr0, len0);
146
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
147
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
148
+ if (r1) {
149
+ throw takeFromExternrefTable0(r0);
150
+ }
151
+ } finally {
152
+ wasm.__wbindgen_add_to_stack_pointer(16);
153
+ }
154
+ }
155
+ /**
156
+ * @returns {string}
157
+ */
158
+ side() {
159
+ let deferred1_0;
160
+ let deferred1_1;
161
+ try {
162
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16) >>> 0;
163
+ wasm.wasmengine_side(retptr, this.__wbg_ptr);
164
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
165
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
166
+ deferred1_0 = r0;
167
+ deferred1_1 = r1;
168
+ return getStringFromWasm0(r0, r1);
169
+ } finally {
170
+ wasm.__wbindgen_add_to_stack_pointer(16);
171
+ wasm.__wbindgen_export3(deferred1_0, deferred1_1, 1);
172
+ }
173
+ }
174
+ /**
175
+ * @returns {string}
176
+ */
177
+ status() {
178
+ let deferred1_0;
179
+ let deferred1_1;
180
+ try {
181
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16) >>> 0;
182
+ wasm.wasmengine_status(retptr, this.__wbg_ptr);
183
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
184
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
185
+ deferred1_0 = r0;
186
+ deferred1_1 = r1;
187
+ return getStringFromWasm0(r0, r1);
188
+ } finally {
189
+ wasm.__wbindgen_add_to_stack_pointer(16);
190
+ wasm.__wbindgen_export3(deferred1_0, deferred1_1, 1);
191
+ }
192
+ }
193
+ stop() {
194
+ wasm.wasmengine_stop(this.__wbg_ptr);
195
+ }
196
+ }
197
+ if (Symbol.dispose) WasmEngine.prototype[Symbol.dispose] = WasmEngine.prototype.free;
198
+
199
+ /**
200
+ * @param {number} rating
201
+ * @param {number} depth
202
+ * @param {number} movetime_ms
203
+ * @param {number} nodes
204
+ * @param {number} skill
205
+ * @param {number} book
206
+ * @param {number} contempt
207
+ * @returns {string}
208
+ */
209
+ export function preview_limits(rating, depth, movetime_ms, nodes, skill, book, contempt) {
210
+ let deferred1_0;
211
+ let deferred1_1;
212
+ try {
213
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16) >>> 0;
214
+ wasm.preview_limits(retptr, rating, depth, movetime_ms, nodes, skill, book, contempt);
215
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
216
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
217
+ deferred1_0 = r0;
218
+ deferred1_1 = r1;
219
+ return getStringFromWasm0(r0, r1);
220
+ } finally {
221
+ wasm.__wbindgen_add_to_stack_pointer(16);
222
+ wasm.__wbindgen_export3(deferred1_0, deferred1_1, 1);
223
+ }
224
+ }
225
+
226
+ /**
227
+ * @param {number} rating
228
+ * @returns {string}
229
+ */
230
+ export function rating_curve(rating) {
231
+ let deferred1_0;
232
+ let deferred1_1;
233
+ try {
234
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16) >>> 0;
235
+ wasm.rating_curve(retptr, rating);
236
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
237
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
238
+ deferred1_0 = r0;
239
+ deferred1_1 = r1;
240
+ return getStringFromWasm0(r0, r1);
241
+ } finally {
242
+ wasm.__wbindgen_add_to_stack_pointer(16);
243
+ wasm.__wbindgen_export3(deferred1_0, deferred1_1, 1);
244
+ }
245
+ }
246
+ function __wbg_get_imports() {
247
+ const import0 = {
248
+ __proto__: null,
249
+ __wbg___wbindgen_boolean_get_4f1baf9a00be3c74: function(arg0) {
250
+ const v = arg0;
251
+ const ret = typeof(v) === 'boolean' ? v : undefined;
252
+ return isLikeNone(ret) ? 0xFFFFFF : ret ? 1 : 0;
253
+ },
254
+ __wbg___wbindgen_is_function_741ff2bb612836f5: function(arg0) {
255
+ const ret = typeof(arg0) === 'function';
256
+ return ret;
257
+ },
258
+ __wbg___wbindgen_is_undefined_be5b34249ff1fc15: function(arg0) {
259
+ const ret = arg0 === undefined;
260
+ return ret;
261
+ },
262
+ __wbg___wbindgen_number_get_934b640d0889af78: function(arg0, arg1) {
263
+ const obj = arg1;
264
+ const ret = typeof(obj) === 'number' ? obj : undefined;
265
+ getDataViewMemory0().setFloat64((arg0 >>> 0) + 8 * 1, isLikeNone(ret) ? 0 : ret, true);
266
+ getDataViewMemory0().setInt32((arg0 >>> 0) + 4 * 0, !isLikeNone(ret), true);
267
+ },
268
+ __wbg___wbindgen_throw_4c88d8bf264fd984: function(arg0, arg1) {
269
+ throw new Error(getStringFromWasm0(arg0, arg1));
270
+ },
271
+ __wbg_call_40b008ba32f73991: function() { return handleError(function (arg0, arg1) {
272
+ const ret = arg0.call(arg1);
273
+ return ret;
274
+ }, arguments); },
275
+ __wbg_call_a410eb4b59ed5965: function() { return handleError(function (arg0, arg1, arg2) {
276
+ const ret = arg0.call(arg1, arg2);
277
+ return ret;
278
+ }, arguments); },
279
+ __wbg_get_2080c49015e35254: function() { return handleError(function (arg0, arg1) {
280
+ const ret = Reflect.get(arg0, arg1);
281
+ return ret;
282
+ }, arguments); },
283
+ __wbg_now_463ac581d2fc7ea3: function() {
284
+ const ret = Date.now();
285
+ return ret;
286
+ },
287
+ __wbg_static_accessor_GLOBAL_THIS_003ee833af9699fc: function() {
288
+ const ret = typeof globalThis === 'undefined' ? undefined : globalThis;
289
+ return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
290
+ },
291
+ __wbg_static_accessor_GLOBAL_e4293fb22cbe77da: function() {
292
+ const ret = typeof global === 'undefined' ? undefined : global;
293
+ return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
294
+ },
295
+ __wbg_static_accessor_SELF_53bb20457401453d: function() {
296
+ const ret = typeof self === 'undefined' ? undefined : self;
297
+ return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
298
+ },
299
+ __wbg_static_accessor_WINDOW_b5e0810bfecb7feb: function() {
300
+ const ret = typeof window === 'undefined' ? undefined : window;
301
+ return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
302
+ },
303
+ __wbindgen_generic_0000000000000001: function(arg0, arg1) {
304
+ // Cast intrinsic for `Ref(String) -> Externref`.
305
+ const ret = getStringFromWasm0(arg0, arg1);
306
+ return ret;
307
+ },
308
+ __wbindgen_init_externref_table: function() {
309
+ const table = wasm.__wbindgen_externrefs;
310
+ table.set(0, undefined);
311
+ table.set(1024, undefined);
312
+ table.set(1025, null);
313
+ table.set(1026, true);
314
+ table.set(1027, false);
315
+ },
316
+ };
317
+ return {
318
+ __proto__: null,
319
+ "./chess_engine_wasm_bg.js": import0,
320
+ };
321
+ }
322
+
323
+ const WasmEngineFinalization = (typeof FinalizationRegistry === 'undefined')
324
+ ? { register: () => {}, unregister: () => {} }
325
+ : new FinalizationRegistry(ptr => wasm.__wbg_wasmengine_free(ptr, 1));
326
+
327
+ function addToExternrefTable0(obj) {
328
+ const idx = wasm.__wbindgen_export2();
329
+ wasm.__wbindgen_externrefs.set(idx, obj);
330
+ return idx;
331
+ }
332
+
333
+ let cachedDataViewMemory0 = null;
334
+ function getDataViewMemory0() {
335
+ if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
336
+ cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
337
+ }
338
+ return cachedDataViewMemory0;
339
+ }
340
+
341
+ function getStringFromWasm0(ptr, len) {
342
+ ptr = ptr >>> 0;
343
+ return decodeText(getUint8ArrayMemory0().subarray(ptr, ptr + len));
344
+ }
345
+
346
+ let cachedUint8ArrayMemory0 = null;
347
+ function getUint8ArrayMemory0() {
348
+ if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
349
+ cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
350
+ }
351
+ return cachedUint8ArrayMemory0;
352
+ }
353
+
354
+ function handleError(f, args) {
355
+ try {
356
+ return f.apply(this, args);
357
+ } catch (e) {
358
+ const idx = addToExternrefTable0(e);
359
+ wasm.__wbindgen_export(idx);
360
+ }
361
+ }
362
+
363
+ function isLikeNone(x) {
364
+ return x === undefined || x === null;
365
+ }
366
+
367
+ function passStringToWasm0(arg, malloc, realloc) {
368
+ if (realloc === undefined) {
369
+ const buf = cachedTextEncoder.encode(arg);
370
+ const ptr = malloc(buf.length, 1) >>> 0;
371
+ getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
372
+ WASM_VECTOR_LEN = buf.length;
373
+ return ptr;
374
+ }
375
+
376
+ let len = arg.length;
377
+ let ptr = malloc(len, 1) >>> 0;
378
+
379
+ const mem = getUint8ArrayMemory0();
380
+
381
+ let offset = 0;
382
+
383
+ for (; offset < len; offset++) {
384
+ const code = arg.charCodeAt(offset);
385
+ if (code > 0x7F) break;
386
+ mem[ptr + offset] = code;
387
+ }
388
+ if (offset !== len) {
389
+ if (offset !== 0) {
390
+ arg = arg.slice(offset);
391
+ }
392
+ ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
393
+ const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
394
+ const ret = cachedTextEncoder.encodeInto(arg, view);
395
+
396
+ offset += ret.written;
397
+ ptr = realloc(ptr, len, offset, 1) >>> 0;
398
+ }
399
+
400
+ WASM_VECTOR_LEN = offset;
401
+ return ptr;
402
+ }
403
+
404
+ function takeFromExternrefTable0(idx) {
405
+ const value = wasm.__wbindgen_externrefs.get(idx);
406
+ wasm.__wbindgen_export6(idx);
407
+ return value;
408
+ }
409
+
410
+ let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true });
411
+ cachedTextDecoder.decode();
412
+ const MAX_SAFARI_DECODE_BYTES = 2146435072;
413
+ let numBytesDecoded = 0;
414
+ function decodeText(bytes) {
415
+ numBytesDecoded += bytes.length;
416
+ if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
417
+ cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true });
418
+ cachedTextDecoder.decode();
419
+ numBytesDecoded = bytes.length;
420
+ }
421
+ return cachedTextDecoder.decode(bytes);
422
+ }
423
+
424
+ const cachedTextEncoder = new TextEncoder();
425
+
426
+ if (!('encodeInto' in cachedTextEncoder)) {
427
+ cachedTextEncoder.encodeInto = function (arg, view) {
428
+ const buf = cachedTextEncoder.encode(arg);
429
+ view.set(buf);
430
+ return {
431
+ read: arg.length,
432
+ written: buf.length
433
+ };
434
+ };
435
+ }
436
+
437
+ let WASM_VECTOR_LEN = 0;
438
+
439
+ let wasmModule, wasmInstance, wasm;
440
+ function __wbg_finalize_init(instance, module) {
441
+ wasmInstance = instance;
442
+ wasm = instance.exports;
443
+ wasmModule = module;
444
+ cachedDataViewMemory0 = null;
445
+ cachedUint8ArrayMemory0 = null;
446
+ wasm.__wbindgen_start();
447
+ return wasm;
448
+ }
449
+
450
+ async function __wbg_load(module, imports) {
451
+ if (typeof Response === 'function' && module instanceof Response) {
452
+ if (!module.ok) {
453
+ throw new Error(`failed to fetch Wasm: ${module.status} ${module.statusText} fetching '${module.url}'`);
454
+ }
455
+
456
+ if (typeof WebAssembly.instantiateStreaming === 'function') {
457
+ try {
458
+ return await WebAssembly.instantiateStreaming(module, imports);
459
+ } catch (e) {
460
+ const validResponse = expectedResponseType(module.type);
461
+
462
+ if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') {
463
+ console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
464
+
465
+ } else { throw e; }
466
+ }
467
+ }
468
+
469
+ const bytes = await module.arrayBuffer();
470
+ return await WebAssembly.instantiate(bytes, imports);
471
+ } else {
472
+ const instance = await WebAssembly.instantiate(module, imports);
473
+
474
+ if (instance instanceof WebAssembly.Instance) {
475
+ return { instance, module };
476
+ } else {
477
+ return instance;
478
+ }
479
+ }
480
+
481
+ function expectedResponseType(type) {
482
+ switch (type) {
483
+ case 'basic': case 'cors': case 'default': return true;
484
+ }
485
+ return false;
486
+ }
487
+ }
488
+
489
+ function initSync(module) {
490
+ if (wasm !== undefined) return wasm;
491
+
492
+
493
+ if (module !== undefined) {
494
+ if (Object.getPrototypeOf(module) === Object.prototype) {
495
+ ({module} = module)
496
+ } else {
497
+ console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
498
+ }
499
+ }
500
+
501
+ const imports = __wbg_get_imports();
502
+ if (!(module instanceof WebAssembly.Module)) {
503
+ module = new WebAssembly.Module(module);
504
+ }
505
+ const instance = new WebAssembly.Instance(module, imports);
506
+ return __wbg_finalize_init(instance, module);
507
+ }
508
+
509
+ let __wbg_init_promise;
510
+ async function __wbg_init(module_or_path) {
511
+ if (wasm !== undefined) return wasm;
512
+ if (__wbg_init_promise === undefined) {
513
+ __wbg_init_promise = __wbg_init_once(module_or_path);
514
+ __wbg_init_promise.catch(() => { __wbg_init_promise = undefined; });
515
+ }
516
+ return __wbg_init_promise;
517
+ }
518
+
519
+ async function __wbg_init_once(module_or_path) {
520
+
521
+ if (module_or_path !== undefined) {
522
+ if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
523
+ ({module_or_path} = module_or_path)
524
+ } else {
525
+ console.warn('using deprecated parameters for the initialization function; pass a single object instead')
526
+ }
527
+ }
528
+
529
+ if (module_or_path === undefined) {
530
+ module_or_path = new URL('chess_engine_wasm_bg.wasm', import.meta.url);
531
+ }
532
+ const imports = __wbg_get_imports();
533
+
534
+ if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
535
+ module_or_path = fetch(module_or_path);
536
+ }
537
+
538
+ const { instance, module } = await __wbg_load(await module_or_path, imports);
539
+
540
+ return __wbg_finalize_init(instance, module);
541
+ }
542
+
543
+ export { initSync, __wbg_init as default };
Binary file
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,87 @@
1
+ /// <reference lib="webworker" />
2
+ import init, { preview_limits, WasmEngine } from "./chess_engine_wasm.js";
3
+ const wasmUrl = new URL("./chess_engine_wasm_bg.wasm", import.meta.url);
4
+ let engine = null;
5
+ let stopFlag = null;
6
+ function isTrap(error) {
7
+ return error instanceof WebAssembly.RuntimeError
8
+ || (error instanceof Error && /unreachable|RuntimeError/.test(error.message));
9
+ }
10
+ function shouldStop() {
11
+ return stopFlag !== null && Atomics.load(stopFlag, 0) !== 0;
12
+ }
13
+ function snapshot() {
14
+ if (!engine)
15
+ throw new Error("engine is not ready");
16
+ return {
17
+ fen: engine.fen(),
18
+ side: engine.side(),
19
+ inCheck: engine.in_check(),
20
+ status: engine.status(),
21
+ legal: JSON.parse(engine.legal_moves()),
22
+ };
23
+ }
24
+ function limitsOf(req) {
25
+ return JSON.parse(preview_limits(req.rating, req.depth, req.movetimeMs, req.nodes, req.skill, req.book, req.contempt));
26
+ }
27
+ self.onmessage = async (event) => {
28
+ const msg = event.data;
29
+ try {
30
+ if (msg.type === "init") {
31
+ await init(wasmUrl);
32
+ engine = new WasmEngine(msg.hashMb ?? 16);
33
+ if (msg.sab)
34
+ stopFlag = new Int32Array(msg.sab);
35
+ self.postMessage({ type: "ready" });
36
+ return;
37
+ }
38
+ if (!engine)
39
+ throw new Error("engine is not ready");
40
+ if (msg.type === "configure") {
41
+ self.postMessage({ type: "configured", id: msg.id, limits: limitsOf(msg) });
42
+ return;
43
+ }
44
+ if (msg.type === "setFen") {
45
+ engine.set_fen(msg.fen);
46
+ self.postMessage({ type: "position", id: msg.id, position: snapshot() });
47
+ return;
48
+ }
49
+ if (msg.type === "play") {
50
+ engine.play(msg.uci);
51
+ self.postMessage({ type: "position", id: msg.id, position: snapshot() });
52
+ return;
53
+ }
54
+ if (msg.type === "newGame") {
55
+ engine.new_game();
56
+ self.postMessage({ type: "position", id: msg.id, position: snapshot() });
57
+ return;
58
+ }
59
+ if (msg.type === "position") {
60
+ self.postMessage({ type: "position", id: msg.id, position: snapshot() });
61
+ return;
62
+ }
63
+ if (msg.type === "go") {
64
+ if (stopFlag)
65
+ Atomics.store(stopFlag, 0, 0);
66
+ const req = msg;
67
+ const json = engine.search(req.rating, req.depth, req.movetimeMs, req.nodes, req.skill, req.seed, req.book, req.contempt, (infoJson) => {
68
+ const info = JSON.parse(infoJson);
69
+ self.postMessage({ type: "info", info });
70
+ }, shouldStop);
71
+ self.postMessage({ type: "result", id: msg.id, result: JSON.parse(json) });
72
+ }
73
+ }
74
+ catch (error) {
75
+ if (isTrap(error)) {
76
+ setTimeout(() => {
77
+ throw error;
78
+ });
79
+ return;
80
+ }
81
+ self.postMessage({
82
+ type: "error",
83
+ id: msg.id,
84
+ error: error instanceof Error ? error.message : String(error),
85
+ });
86
+ }
87
+ };
@@ -0,0 +1,61 @@
1
+ import { type PositionView, type ResolvedLimits, type SearchInfo, type SearchLimits, type SearchResult } from "./types.js";
2
+ export type { Difficulty, GameStatus, PositionView, ResolvedLimits, SearchInfo, SearchLimits, SearchResult, Side } from "./types.js";
3
+ export { STRENGTH_PRESETS } from "./types.js";
4
+ /**
5
+ * Browser chess engine. Search runs in a Web Worker so the page stays responsive.
6
+ * Strength 800–2200 selects depth, time, and how often a near-best move is chosen.
7
+ * Pass {@link ChessEngine.setLimits} to override any of those directly.
8
+ */
9
+ export declare class ChessEngine {
10
+ private worker;
11
+ private pending;
12
+ private nextId;
13
+ private chain;
14
+ private infoHandler;
15
+ private ratingValue;
16
+ private overrides;
17
+ private resolvedLimits;
18
+ private flag;
19
+ private sab;
20
+ private hashMb;
21
+ private lastFen;
22
+ private readyPromise;
23
+ private started;
24
+ private recovering;
25
+ private closed;
26
+ private constructor();
27
+ private openWorker;
28
+ private waitUntilReady;
29
+ private postInit;
30
+ private onWorkerError;
31
+ private recover;
32
+ private restore;
33
+ static create(options?: {
34
+ hashMb?: number;
35
+ rating?: number;
36
+ }): Promise<ChessEngine>;
37
+ get rating(): number;
38
+ get limits(): ResolvedLimits | null;
39
+ /** True when a search can be interrupted immediately from the UI thread. */
40
+ get canStopImmediately(): boolean;
41
+ onInfo(handler: ((info: SearchInfo) => void) | null): void;
42
+ setStrength(rating: number): Promise<ResolvedLimits>;
43
+ setLimits(limits: SearchLimits): Promise<ResolvedLimits>;
44
+ clearLimits(): Promise<ResolvedLimits>;
45
+ setFen(fen: string): Promise<PositionView>;
46
+ play(uci: string): Promise<PositionView>;
47
+ newGame(): Promise<PositionView>;
48
+ position(): Promise<PositionView>;
49
+ /** Play a move using the current strength. Lower skills sometimes choose a close alternative. */
50
+ go(): Promise<SearchResult>;
51
+ /** Always return the best move at the current depth and time. Ignores the opening book. */
52
+ analyze(): Promise<SearchResult>;
53
+ stop(): void;
54
+ destroy(): void;
55
+ private refreshLimits;
56
+ private search;
57
+ private enqueue;
58
+ private ask;
59
+ private onMessage;
60
+ private settle;
61
+ }
package/dist/index.js ADDED
@@ -0,0 +1,222 @@
1
+ import { requestFrom } from "./types.js";
2
+ export { STRENGTH_PRESETS } from "./types.js";
3
+ /**
4
+ * Browser chess engine. Search runs in a Web Worker so the page stays responsive.
5
+ * Strength 800–2200 selects depth, time, and how often a near-best move is chosen.
6
+ * Pass {@link ChessEngine.setLimits} to override any of those directly.
7
+ */
8
+ export class ChessEngine {
9
+ worker;
10
+ pending = new Map();
11
+ nextId = 1;
12
+ chain = Promise.resolve();
13
+ infoHandler = null;
14
+ ratingValue = 1500;
15
+ overrides = {};
16
+ resolvedLimits = null;
17
+ flag = null;
18
+ sab = null;
19
+ hashMb;
20
+ lastFen = null;
21
+ readyPromise;
22
+ started = false;
23
+ recovering = false;
24
+ closed = false;
25
+ constructor(hashMb) {
26
+ this.hashMb = hashMb;
27
+ if (typeof crossOriginIsolated === "boolean" && crossOriginIsolated) {
28
+ this.sab = new SharedArrayBuffer(4);
29
+ this.flag = new Int32Array(this.sab);
30
+ }
31
+ this.worker = this.openWorker();
32
+ this.readyPromise = this.waitUntilReady();
33
+ this.postInit();
34
+ }
35
+ openWorker() {
36
+ const worker = new Worker(new URL("./engine.worker.js", import.meta.url), { type: "module" });
37
+ worker.onmessage = (event) => this.onMessage(event);
38
+ worker.onerror = (event) => this.onWorkerError(event);
39
+ return worker;
40
+ }
41
+ waitUntilReady() {
42
+ return new Promise((resolve, reject) => {
43
+ this.pending.set(0, {
44
+ resolve: () => resolve(),
45
+ reject,
46
+ });
47
+ });
48
+ }
49
+ postInit() {
50
+ this.worker.postMessage({ type: "init", id: 0, hashMb: this.hashMb, sab: this.sab });
51
+ }
52
+ onWorkerError(event) {
53
+ event.preventDefault();
54
+ if (this.closed)
55
+ return;
56
+ if (!this.started || this.recovering) {
57
+ const pending = this.pending.get(0);
58
+ if (pending) {
59
+ this.pending.delete(0);
60
+ pending.reject(new Error(event.message || "engine failed to start"));
61
+ }
62
+ return;
63
+ }
64
+ this.recover();
65
+ }
66
+ recover() {
67
+ this.recovering = true;
68
+ const reason = new Error("engine restarted after a wasm trap");
69
+ for (const pending of this.pending.values())
70
+ pending.reject(reason);
71
+ this.pending.clear();
72
+ this.worker.onerror = null;
73
+ this.worker.terminate();
74
+ const job = this.restore().finally(() => {
75
+ this.recovering = false;
76
+ });
77
+ this.chain = job.then(() => undefined, () => undefined);
78
+ }
79
+ async restore() {
80
+ this.worker = this.openWorker();
81
+ const ready = this.waitUntilReady();
82
+ this.postInit();
83
+ await ready;
84
+ if (this.lastFen) {
85
+ await this.ask("setFen", { fen: this.lastFen }, "position");
86
+ }
87
+ this.resolvedLimits = await this.ask("configure", requestFrom(this.ratingValue, this.overrides, -1), "limits");
88
+ }
89
+ static async create(options) {
90
+ const engine = new ChessEngine(options?.hashMb ?? 16);
91
+ await engine.readyPromise;
92
+ await engine.setStrength(options?.rating ?? 1500);
93
+ return engine;
94
+ }
95
+ get rating() {
96
+ return this.ratingValue;
97
+ }
98
+ get limits() {
99
+ return this.resolvedLimits;
100
+ }
101
+ /** True when a search can be interrupted immediately from the UI thread. */
102
+ get canStopImmediately() {
103
+ return this.flag !== null;
104
+ }
105
+ onInfo(handler) {
106
+ this.infoHandler = handler;
107
+ }
108
+ async setStrength(rating) {
109
+ this.ratingValue = Math.max(800, Math.min(2200, Math.round(rating)));
110
+ this.overrides = {};
111
+ return this.refreshLimits();
112
+ }
113
+ async setLimits(limits) {
114
+ const next = { ...this.overrides };
115
+ for (const [key, value] of Object.entries(limits)) {
116
+ if (value === undefined)
117
+ delete next[key];
118
+ else
119
+ next[key] = value;
120
+ }
121
+ this.overrides = next;
122
+ return this.refreshLimits();
123
+ }
124
+ async clearLimits() {
125
+ this.overrides = {};
126
+ return this.refreshLimits();
127
+ }
128
+ setFen(fen) {
129
+ this.stop();
130
+ return this.enqueue(() => this.ask("setFen", { fen }, "position"));
131
+ }
132
+ play(uci) {
133
+ this.stop();
134
+ return this.enqueue(() => this.ask("play", { uci }, "position"));
135
+ }
136
+ newGame() {
137
+ this.stop();
138
+ return this.enqueue(() => this.ask("newGame", {}, "position"));
139
+ }
140
+ position() {
141
+ return this.enqueue(() => this.ask("position", {}, "position"));
142
+ }
143
+ /** Play a move using the current strength. Lower skills sometimes choose a close alternative. */
144
+ go() {
145
+ return this.enqueue(() => this.search(false));
146
+ }
147
+ /** Always return the best move at the current depth and time. Ignores the opening book. */
148
+ analyze() {
149
+ return this.enqueue(() => this.search(true));
150
+ }
151
+ stop() {
152
+ if (this.flag)
153
+ Atomics.store(this.flag, 0, 1);
154
+ this.worker.postMessage({ type: "stop" });
155
+ }
156
+ destroy() {
157
+ this.closed = true;
158
+ this.stop();
159
+ this.worker.onerror = null;
160
+ this.worker.terminate();
161
+ }
162
+ async refreshLimits() {
163
+ const limits = await this.enqueue(() => this.ask("configure", requestFrom(this.ratingValue, this.overrides, -1), "limits"));
164
+ this.resolvedLimits = limits;
165
+ return limits;
166
+ }
167
+ search(analyze) {
168
+ if (this.flag)
169
+ Atomics.store(this.flag, 0, 0);
170
+ const overrides = analyze ? { ...this.overrides, skill: 20, book: false } : this.overrides;
171
+ const seed = analyze ? -1 : Math.floor(Math.random() * 1_000_000_000);
172
+ return this.ask("go", { ...requestFrom(this.ratingValue, overrides, seed) }, "result");
173
+ }
174
+ enqueue(fn) {
175
+ const run = this.chain.then(fn, fn);
176
+ this.chain = run.then(() => undefined, () => undefined);
177
+ return run;
178
+ }
179
+ ask(type, body, field) {
180
+ const id = this.nextId++;
181
+ return new Promise((resolve, reject) => {
182
+ this.pending.set(id, { resolve: (value) => resolve(value), reject });
183
+ this.worker.postMessage({ type, id, ...body });
184
+ }).then((message) => {
185
+ const record = message;
186
+ if (field === "position") {
187
+ const view = record.position;
188
+ if (view?.fen)
189
+ this.lastFen = view.fen;
190
+ }
191
+ return record[field];
192
+ });
193
+ }
194
+ onMessage(event) {
195
+ const msg = event.data;
196
+ if (msg.type === "info" && msg.info) {
197
+ this.infoHandler?.(msg.info);
198
+ return;
199
+ }
200
+ if (msg.type === "ready") {
201
+ this.started = true;
202
+ this.settle(0, msg);
203
+ return;
204
+ }
205
+ if (msg.id === undefined)
206
+ return;
207
+ if (msg.type === "error") {
208
+ const pending = this.pending.get(msg.id);
209
+ this.pending.delete(msg.id);
210
+ pending?.reject(new Error(msg.error ?? "engine error"));
211
+ return;
212
+ }
213
+ this.settle(msg.id, msg);
214
+ }
215
+ settle(id, message) {
216
+ const pending = this.pending.get(id);
217
+ if (!pending)
218
+ return;
219
+ this.pending.delete(id);
220
+ pending.resolve(message);
221
+ }
222
+ }
@@ -0,0 +1,64 @@
1
+ export type GameStatus = "ongoing" | "checkmate" | "stalemate" | "fifty_move" | "threefold" | "insufficient_material";
2
+ export type Side = "w" | "b";
3
+ export interface PositionView {
4
+ fen: string;
5
+ side: Side;
6
+ inCheck: boolean;
7
+ status: GameStatus;
8
+ legal: string[];
9
+ }
10
+ export interface SearchInfo {
11
+ depth: number;
12
+ scoreCp: number;
13
+ mateIn: number | null;
14
+ nodes: number;
15
+ timeMs: number;
16
+ nps: number;
17
+ pv: string[];
18
+ }
19
+ export interface SearchResult extends SearchInfo {
20
+ bestMove: string;
21
+ ponder: string;
22
+ book: boolean;
23
+ }
24
+ export interface ResolvedLimits {
25
+ rating: number;
26
+ depth: number;
27
+ movetimeMs: number;
28
+ nodes: number;
29
+ skill: number;
30
+ book: boolean;
31
+ contempt: number;
32
+ }
33
+ /** Fields left undefined keep the strength curve. `movetimeMs: 0` is no time limit. */
34
+ export interface SearchLimits {
35
+ depth?: number;
36
+ movetimeMs?: number;
37
+ nodes?: number;
38
+ skill?: number;
39
+ book?: boolean;
40
+ contempt?: number;
41
+ }
42
+ export interface SearchRequest {
43
+ rating: number;
44
+ depth: number;
45
+ movetimeMs: number;
46
+ nodes: number;
47
+ skill: number;
48
+ seed: number;
49
+ book: number;
50
+ contempt: number;
51
+ }
52
+ export declare const UNSET = -1;
53
+ export declare const CONTEMPT_UNSET = -2147483648;
54
+ export declare function requestFrom(rating: number, limits: SearchLimits, seed: number): SearchRequest;
55
+ export declare const STRENGTH_PRESETS: {
56
+ readonly beginner: 800;
57
+ readonly casual: 1100;
58
+ readonly intermediate: 1400;
59
+ readonly club: 1700;
60
+ readonly expert: 1900;
61
+ readonly candidate: 2100;
62
+ readonly master: 2200;
63
+ };
64
+ export type Difficulty = keyof typeof STRENGTH_PRESETS;
package/dist/types.js ADDED
@@ -0,0 +1,23 @@
1
+ export const UNSET = -1;
2
+ export const CONTEMPT_UNSET = -2147483648;
3
+ export function requestFrom(rating, limits, seed) {
4
+ return {
5
+ rating,
6
+ depth: limits.depth ?? UNSET,
7
+ movetimeMs: limits.movetimeMs ?? UNSET,
8
+ nodes: limits.nodes ?? UNSET,
9
+ skill: limits.skill ?? UNSET,
10
+ seed,
11
+ book: limits.book === undefined ? UNSET : limits.book ? 1 : 0,
12
+ contempt: limits.contempt ?? CONTEMPT_UNSET,
13
+ };
14
+ }
15
+ export const STRENGTH_PRESETS = {
16
+ beginner: 800,
17
+ casual: 1100,
18
+ intermediate: 1400,
19
+ club: 1700,
20
+ expert: 1900,
21
+ candidate: 2100,
22
+ master: 2200,
23
+ };
package/package.json CHANGED
@@ -1,6 +1,22 @@
1
1
  {
2
2
  "name": "browser-chess-engine",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Browser bindings for the WebAssembly chess engine.",
6
+ "license": "MIT",
7
+ "files": ["dist"],
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "tsc -p tsconfig.build.json && node scripts/assemble-dist.mjs",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "devDependencies": {
20
+ "typescript": "^5.6.3"
21
+ }
22
+ }